Spring-Security-OAuth2服務(wù)器之搭建認(rèn)證授權(quán)服務(wù)器[一]

結(jié)構(gòu)基礎(chǔ)

基礎(chǔ)框架:Spring Boot + Spring-Security-OAuth2
存儲(chǔ)介質(zhì):Mysql + Redis
持久化方式:Spring-data-jpa
測(cè)試工具:Postman
大局觀:
1黎棠、OAuth2服務(wù)器分為兩部分組成:認(rèn)證授權(quán)服務(wù)器和資源服務(wù)器晋渺。聞名知意,不解釋脓斩。本文只講認(rèn)證授權(quán)服務(wù)器的搭建木西,資源服務(wù)器部分后續(xù)。
2随静、認(rèn)證授權(quán)服務(wù)器分為兩大步驟八千,一是認(rèn)證,二是授權(quán)燎猛。而認(rèn)證則主要由Spring-Security負(fù)責(zé)恋捆,而授權(quán)則有Oauth2負(fù)責(zé)。
3重绷、本項(xiàng)目有2個(gè)存儲(chǔ)介質(zhì)沸停,Mysql和Redis。Mysql的作用是用來(lái)存儲(chǔ)認(rèn)證數(shù)據(jù)昭卓,而Redis用作緩存和存儲(chǔ)授權(quán)信息及AccessToken的愤钾。其實(shí),Mysql同事可以用來(lái)存儲(chǔ)認(rèn)證數(shù)據(jù)和存儲(chǔ)授權(quán)信息以及AccessToken的候醒,而且Spring-Security-OAuth2也提供了存儲(chǔ)基礎(chǔ)能颁。那么問(wèn)題來(lái)了,為什么不用Mysql呢倒淫?考慮原因:AccessToken是有時(shí)效性的伙菊,也就是說(shuō),存儲(chǔ)一段時(shí)間后敌土,將會(huì)失效镜硕,也許是一天或者一個(gè)月。在單體應(yīng)用情況下纯赎,當(dāng)業(yè)務(wù)比較多谦疾、訪問(wèn)頻率大的時(shí)候,如果使用mysql犬金,那么有可能導(dǎo)致響應(yīng)速度降低念恍,基于性能的考慮,減小數(shù)據(jù)庫(kù)的壓力晚顷,所以將其改良為使用Redis存儲(chǔ)授權(quán)信息和AccessToken峰伙。而Redis性能十分優(yōu)越,同時(shí)還能作為緩存認(rèn)證信息使用该默,一舉兩得瞳氓,何樂(lè)而不為呢?

學(xué)習(xí)基礎(chǔ)

參考理解OAuth 2.0 - 阮一峰的網(wǎng)絡(luò)日志

認(rèn)證方式

Oauth2授權(quán)有多種方式栓袖,此處將使用grant_typeclient_secretpassword兩種方式匣摘。


1店诗、客戶端授權(quán)(Client Credentials Grant)

POST /oauth2-server/oauth/token?grant_type=client_credentials HTTP/1.1
Host: 127.0.0.1:8050
Authorization: Basic Y2xpZW50X2F1dGhfbW9kZToxMjM0NTY=

請(qǐng)求信息如上。注意事項(xiàng)如下:
1音榜、在mysql中建立基礎(chǔ)表:oauth_client_details庞瘸,查看建表以及初始化。其中client_id=client_auth_mode,client_secret的原始值為123456赠叼,數(shù)據(jù)庫(kù)中存儲(chǔ)的是加密后的值擦囊,加密方式為BCrypt。
2嘴办、請(qǐng)求頭:key=Authorization瞬场;value=Basic+空格+Base64(username:password)
3、Basic后面的信息由[username:password]內(nèi)的字符Base64加密而成
4、此中的username和password分別為oauth_client_details表中的client_id和client_secret,也就是客戶端模式下的標(biāo)識(shí)客戶端的憑證(用以區(qū)別是哪種受信任的客戶端),對(duì)應(yīng)OAuth2映射為ClientDetails對(duì)象。


2冶忱、密碼授權(quán)

POST /token HTTP/1.1
     Host: server.example.com
     Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
     Content-Type: application/x-www-form-urlencoded
 grant_type=password&username=johndoe&password=A3ddj3w

請(qǐng)求信息如上。注意事項(xiàng)如下:
1迟蜜、在mysql中建立基礎(chǔ)表:oauth_client_details和ux_member向图,查看建表以及初始化。其中oauth_client_details表中client_id=password_auth_mode,client_secret的原始值為123456桌吃,數(shù)據(jù)庫(kù)中存儲(chǔ)的是加密后的值朱沃,加密方式為BCrypt。ux_member表中茅诱,username=member_name,password=123456逗物,加密方式MD5。
1瑟俭、請(qǐng)求頭:key=Authorization翎卓;value=Basic+空格+Base64(username:password)
2、Basic后面的信息由[username:password]內(nèi)的字符Base64加密而成
3摆寄、此中的username和password依舊為oauth_client_details表中的client_id和client_secret失暴,也就是客戶端模式下的標(biāo)識(shí)客戶端的憑證(用以區(qū)別是哪種受信任的客戶端),對(duì)應(yīng)OAuth2映射為DetailDetails對(duì)象微饥。
4逗扒、由上至少可看出二者在傳參時(shí)的表面上的區(qū)別,只是密碼授權(quán)模式欠橘,多了2個(gè)參數(shù):username和password矩肩,以及grant_type的值不一樣。而里層的區(qū)別肃续,在于密碼模式下黍檩,Spring-Security-Oauth2中叉袍,有個(gè)叫做UserDetails的對(duì)象,而剛好ux_member表就是與之對(duì)應(yīng)刽酱。


大局觀已有喳逛,廢話少說(shuō),下面開始講述相關(guān)配置

存儲(chǔ)介質(zhì)

  • Mysql
    a肛跌、作用:存儲(chǔ)認(rèn)證管理信息和業(yè)務(wù)數(shù)據(jù)艺配。那么問(wèn)題來(lái)了,什么稱之為認(rèn)證信息呢衍慎?我的理解為能標(biāo)識(shí)用戶主體是誰(shuí)的唯一性的信息转唉,這里的主體可能為客戶端也可能為某個(gè)PC或者移動(dòng)端的某個(gè)人。
    b稳捆、設(shè)計(jì):在本項(xiàng)目中赠法,所謂的認(rèn)證信息有2個(gè),oauth_client_details與ux_member表乔夯。與之對(duì)應(yīng)的也就是ClientDetails和UserDetails對(duì)象砖织。這兩個(gè)都是待認(rèn)證的主體,也就是說(shuō)在客戶端模式下末荐,需要對(duì)ClientDetails對(duì)象進(jìn)行認(rèn)證侧纯;而在密碼模式下,則既需要對(duì)ClientDetails對(duì)象認(rèn)證甲脏,也需要對(duì)UserDetails對(duì)象認(rèn)證眶熬。
  • Redis
    a、存儲(chǔ)授權(quán)信息以及AccessToken
    b块请、緩存密碼模式下的認(rèn)證信息(UserDetails對(duì)象娜氏,以u(píng)sername為key)

配置信息


security:
    basic:
        enabled: false # 是否開啟基本的鑒權(quán),默認(rèn)為true墩新。 true:所有的接口默認(rèn)都需要被驗(yàn)證贸弥,將導(dǎo)致 攔截器[對(duì)于 excludePathPatterns()方法失效]
server:
  context-path: /oauth2-server
  port: 8050
---
spring:
  application:
      name: oauth2-server
  redis:
      database: 4
      host: 127.0.0.1
      password: root123456
      port: 6379
      pool:
          max-active: 8
          max-wait: 8
          min-idle: 0
          max-idle: 8

  datasource:
#    dataSourceClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/redis-oauth2?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    database: MYSQL
    openInView: true
    show_sql: true
    generate-ddl: true #(false)
    hibernate:
        ddl-auto: update #(none)

在resources文件夾下建立一個(gè)application.yml文件,然后把上述信息拷貝進(jìn)去海渊,即可绵疲。
因?yàn)楸卷?xiàng)目是基于Spring Boot的開發(fā),Spring Boot其中一個(gè)好處就是能夠根據(jù)你的配置信息自動(dòng)生成相關(guān)的Bean對(duì)象切省,如數(shù)據(jù)源DataSource最岗、緩存工廠類RedisConnectionFactory、緩存RedisCache等Bean對(duì)象朝捆。

驚不驚喜般渡,意不意外

數(shù)據(jù)存儲(chǔ)配置

@Configuration
public class DataStoreConfig {

    public static final String REDIS_CACHE_NAME="redis_cache_name";//不為null即可
    public static final String REDIS_PREFIX ="redis_cache_prefix";//不為null即可
    public static final Long EXPIRE =60*60L;//緩存有效時(shí)間

    /**
     * 配置用以存儲(chǔ)用戶認(rèn)證信息的緩存
     */
    @Bean
    RedisCache redisCache(RedisTemplate redisTemplate){
        RedisCache redisCache = new RedisCache(REDIS_CACHE_NAME,REDIS_PREFIX.getBytes(),redisTemplate,EXPIRE);
        return redisCache;
    }
    /**
     *
     * 創(chuàng)建UserDetails存儲(chǔ)服務(wù)的Bean:使用Redis作為緩存介質(zhì)
     * UserDetails user = this.userCache.getUserFromCache(username)
     */
    @Bean
    public UserCache userCache(RedisCache redisCache) throws Exception {
        UserCache userCache = new SpringCacheBasedUserCache(redisCache);
        return userCache;
    }

    /**
     * 配置AccessToken的存儲(chǔ)方式:此處使用Redis存儲(chǔ)
     * Token的可選存儲(chǔ)方式
     * 1、InMemoryTokenStore
     * 2、JdbcTokenStore
     * 3驯用、JwtTokenStore
     * 4脸秽、RedisTokenStore
     * 5、JwkTokenStore
     */
    @Bean
    public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory) {
        return new RedisTokenStore(redisConnectionFactory);
    }
}

Domain層簡(jiǎn)述

@Entity
@Table(name = "ux_member")
public class Member implements Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    private String password;

    public Member(Member member){
        super();
        this.username = member.getUsername();
        this.password = member.getPassword();
    }
    
    public Member() {

    }
//略過(guò)getter和setter
}

//默認(rèn)角色
public class Role implements GrantedAuthority {

    private static final long serialVersionUID = -2633659220734280260L;
    
    private Set<Role> roles = new HashSet<Role>();

    @Override
    public String getAuthority() {
        return "USER";
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

}

Dao層

@Component("memberRepository")
public interface MemberRepository extends JpaRepository<Member, Long> {
    Member findOneByUsername(String username);
}

Service層

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private static final Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);
    @Autowired
    private MemberRepository memberRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Member member = memberRepository.findOneByUsername(username);
        if (member == null) {
            log.error("用戶不存在");
            throw new UsernameNotFoundException(String.format("User %s does not exist!", username));
        }
        return new UserRepositoryUserDetails(member);
    }

    /**
     *  注意該類的層次結(jié)構(gòu)蝴乔,繼承了Member并實(shí)現(xiàn)了UserDetails接口记餐,繼承是為了使用Member的username和password信息
     */
    private final static class UserRepositoryUserDetails extends Member implements UserDetails {
        private static final long serialVersionUID = 1L;
        private UserRepositoryUserDetails(Member member) {
            super(member);
        }

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            Role role = new Role();
            return role.getRoles();
        }

        @Override
        public String getUsername() {
            return super.getUsername();
        }

        @Override
        public boolean isAccountNonExpired() {
            return true;
        }

        @Override
        public boolean isAccountNonLocked() {
            return true;
        }

        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }

        @Override
        public boolean isEnabled() {
            return true;
        }

    }
}

自定義認(rèn)證服務(wù)器類:用來(lái)對(duì)UserDetails信息進(jìn)行認(rèn)證,CustomUserDetailsService類實(shí)現(xiàn)了UserDetailsService接口薇正,而UserDetailsService則是用來(lái)對(duì)UserDetails進(jìn)行認(rèn)證檢查的片酝,該項(xiàng)目是基于SpringBoot的,所以挖腰,該Bean對(duì)象將會(huì)注入依賴該Bean的其他的Bean對(duì)象中雕沿,如DaoAuthenticationProvider、DefaultTokenServices等猴仑,并在相關(guān)的認(rèn)證流程中對(duì)UserDetails進(jìn)行檢查审轮。

認(rèn)證授權(quán)配置

1、Spring-Security-OAuth2對(duì)于認(rèn)證信息的存儲(chǔ)提供了如下方案:數(shù)據(jù)庫(kù)和內(nèi)存辽俗。而此處將使用Mysql存儲(chǔ)疾渣。
2、認(rèn)證管理信息的配置主要是針對(duì)ClientDetails和UserDetails對(duì)象的檢查崖飘,客戶端模式針對(duì)ClientDetails檢查榴捡,而密碼模式則先檢查ClientDetails后檢查UserDetails對(duì)象。
認(rèn)證授權(quán)配置如下

@Configuration
@EnableAuthorizationServer//開啟配置 OAuth 2.0 認(rèn)證授權(quán)服務(wù)
public class AuthAuthorizeConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    DataSource dataSource;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private CustomUserDetailsService userDetailsService;
    /**
     * 配置 oauth_client_details【client_id和client_secret等】信息的認(rèn)證【檢查ClientDetails的合法性】服務(wù)
     * 設(shè)置 認(rèn)證信息的來(lái)源:數(shù)據(jù)庫(kù) (可選項(xiàng):數(shù)據(jù)庫(kù)和內(nèi)存,使用內(nèi)存一般用來(lái)作測(cè)試)
     * 自動(dòng)注入:ClientDetailsService的實(shí)現(xiàn)類 JdbcClientDetailsService (檢查 ClientDetails 對(duì)象)
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource);
    }


    /**
     * 密碼模式下配置認(rèn)證管理器 AuthenticationManager,并且設(shè)置 AccessToken的存儲(chǔ)介質(zhì)tokenStore,如果不設(shè)置朱浴,則會(huì)默認(rèn)使用內(nèi)存當(dāng)做存儲(chǔ)介質(zhì)薄疚。
     * 而該AuthenticationManager將會(huì)注入 2個(gè)Bean對(duì)象用以檢查(認(rèn)證)
     * 1、ClientDetailsService的實(shí)現(xiàn)類 JdbcClientDetailsService (檢查 ClientDetails 對(duì)象)
     * 2赊琳、UserDetailsService的實(shí)現(xiàn)類 CustomUserDetailsService (檢查 UserDetails 對(duì)象)
     * 
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
            throws Exception {
        endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore).userDetailsService(userDetailsService);
    }

    /**
     *  配置:安全檢查流程
     *  默認(rèn)過(guò)濾器:BasicAuthenticationFilter
     *  1、oauth_client_details表中clientSecret字段加密【ClientDetails屬性secret】
     *  2砰碴、CheckEndpoint類的接口 oauth/check_token 無(wú)需經(jīng)過(guò)過(guò)濾器過(guò)濾躏筏,默認(rèn)值:denyAll()
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();//允許客戶表單認(rèn)證
        security.passwordEncoder(new BCryptPasswordEncoder());//設(shè)置oauth_client_details中的密碼編碼器
        security.checkTokenAccess("permitAll()");//對(duì)于CheckEndpoint控制器[框架自帶的校驗(yàn)]的/oauth/check端點(diǎn)允許所有客戶端發(fā)送器請(qǐng)求而不會(huì)被Spring-security攔截
    }
}

啟動(dòng)服務(wù)器

@SpringBootApplication
public class Oauth2ServerApplication {
   public static void main(String[] args) {
       SpringApplication.run(Oauth2ServerApplication.class, args);
   }
}

Postman測(cè)試

客戶端授權(quán)模式獲取AccessToken請(qǐng)求如下:


客戶端模式

請(qǐng)求的報(bào)文信息如下:

POST /oauth2-server/oauth/token?grant_type=client_credentials HTTP/1.1
Host: 127.0.0.1:8050
Authorization: Basic Y2xpZW50X2F1dGhfbW9kZToxMjM0NTY=
Cache-Control: no-cache
Postman-Token: e5d3ea12-af31-d344-8804-f92db46112a3
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

返回結(jié)果如:

{
    "access_token": "afef641c-62de-4f5d-a5b8-7864ac2b7127",
    "token_type": "bearer",
    "expires_in": 3463,
    "scope": "read write"
}

密碼授權(quán)模式獲取AccessToken請(qǐng)求如下:


密碼模式

請(qǐng)求的報(bào)文信息如下:

 POST /oauth2-server/oauth/token?username=member_name&password=e10adc3949ba59abbe56e057f20f883e&grant_type=password&client_id=password_auth_mode&client_secret=123456 HTTP/1.1
Host: 127.0.0.1:8050
Authorization: Basic cGFzc3dvcmRfYXV0aF9tb2RlOjEyMzQ1Ng==
Cache-Control: no-cache
Postman-Token: 0ccf7ea9-c2ac-10bc-a9da-3d15de82840b
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

返回結(jié)果如:

 {
    "access_token": "a83ba33f-9f1a-4f9a-ba65-99e7fc905ba2",
    "token_type": "bearer",
    "refresh_token": "89f724d6-8553-4838-b4ff-7f6c8fb4d88b",
    "expires_in": 3378,
    "scope": "read write"
}

結(jié)果對(duì)比

差異:客戶端授權(quán)返回結(jié)果比密碼模式返回結(jié)果少了一個(gè)refresh_token,因?yàn)榭蛻裟J讲恢С謗efresh_token認(rèn)證呈枉。
原因:client_credentials是受信任的認(rèn)證模式趁尼,也就意味著你對(duì)于此種信息都是信任的,即可以設(shè)置為永久性的AccessToken猖辫,而不需要刷新重新獲取AccessToken酥泞。

總結(jié)

對(duì)于Spring-Security-Oauth2的學(xué)習(xí)和研究,陸陸續(xù)續(xù)地持續(xù)了不少時(shí)間啃憎,零零散散地也做了不少的筆記芝囤,踩了不少的坑,不奇怪,Spring-Security-OAuth2都沒(méi)個(gè)官方文檔悯姊。寫文章的時(shí)候羡藐,也是一邊敲著代碼,一邊優(yōu)化著悯许,去除了不少無(wú)用的代碼仆嗦,也理清了頭緒。如有錯(cuò)誤先壕,還請(qǐng)大牛們指出瘩扼。

源代碼地址:oauth2-redis-mysql[提醒,直接導(dǎo)入我的項(xiàng)目前垃僚,需要啟動(dòng)redis服務(wù)集绰,并修改相關(guān)的redis配置和數(shù)據(jù)庫(kù)配置,如果未啟動(dòng)redis服務(wù)冈在,程序運(yùn)行成功倒慧,但是spring boot默認(rèn)將TokenStore設(shè)置為InMemoryStore,獲取AccessToken也將失敯纫谅!]

話外篇

oauth2-redis-mysql項(xiàng)目中的oauth2-server模塊項(xiàng)目?jī)H在OAuth2服務(wù)器中充當(dāng)認(rèn)證授權(quán)的角色,而一個(gè)完整的OAuth2服務(wù)溅固,則由資源服務(wù)器和認(rèn)證授權(quán)服務(wù)器組成付秕,這兩個(gè)可以合二為一,也可以分開侍郭。后續(xù)我將抽空询吴,編寫OAuth2資源服務(wù)器的搭建,在上述鏈接中已經(jīng)有個(gè)名為oauth2-client的模塊項(xiàng)目亮元,也就是OAuth2資源服務(wù)器猛计,具體使用,稍后再續(xù)爆捞。

Spring-Security-OAuth2服務(wù)器之搭建認(rèn)證授權(quán)服務(wù)器[一]

Spring-Security-OAuth2服務(wù)器搭建之AccessToken的檢測(cè)[二]

Spring-Security-OAuth2服務(wù)器搭建之資源服務(wù)器搭建[三]

Spring-Security-OAuth2資源服務(wù)器及SpringSecurity權(quán)限控制[四]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奉瘤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子煮甥,更是在濱河造成了極大的恐慌盗温,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件成肘,死亡現(xiàn)場(chǎng)離奇詭異卖局,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)双霍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門砚偶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)批销,“玉大人,你說(shuō)我怎么就攤上這事蟹演》缱辏” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵酒请,是天一觀的道長(zhǎng)骡技。 經(jīng)常有香客問(wèn)我,道長(zhǎng)羞反,這世上最難降的妖魔是什么布朦? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮昼窗,結(jié)果婚禮上是趴,老公的妹妹穿的比我還像新娘。我一直安慰自己澄惊,他們只是感情好唆途,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掸驱,像睡著了一般肛搬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上毕贼,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天温赔,我揣著相機(jī)與錄音,去河邊找鬼鬼癣。 笑死陶贼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的待秃。 我是一名探鬼主播拜秧,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼章郁!你這毒婦竟也來(lái)了腹纳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤驱犹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后足画,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雄驹,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年淹辞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了医舆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蔬将,靈堂內(nèi)的尸體忽然破棺而出爷速,到底是詐尸還是另有隱情,我是刑警寧澤霞怀,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布惫东,位于F島的核電站,受9級(jí)特大地震影響毙石,放射性物質(zhì)發(fā)生泄漏廉沮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一徐矩、第九天 我趴在偏房一處隱蔽的房頂上張望滞时。 院中可真熱鬧,春花似錦滤灯、人聲如沸坪稽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窒百。三九已至,卻和暖如春弟孟,著一層夾襖步出監(jiān)牢的瞬間贝咙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工拂募, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庭猩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓陈症,卻偏偏與公主長(zhǎng)得像蔼水,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子录肯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容