spring security(一)-基本認證

1.簡介

當前絕大多數(shù)網(wǎng)站都都存在著用戶認證用戶授權這最基本的功能稠通,關于這兩個功能概述如下:

  • 用戶認證:驗證某個用戶身份為系統(tǒng)中合法的身份藐俺,說白了就是驗證用戶有沒有權限來操作系統(tǒng)某些功能炕置。傳統(tǒng)做法通過==用戶名==和==密碼==來完成認證的功能
  • 用戶授權:校驗某個用戶是否有權限去執(zhí)行某個操作辩涝。在一個系統(tǒng)中吏够,不同的用戶擁有的權限是不同的推正。例如:后臺管理系統(tǒng)顿颅,不同的用戶登錄進去缸濒,看到的界面不同,這就是用戶授權

Spring Security 就是一個這樣的用戶認證與授權框架粱腻,其介紹如下:

image-20201221141447471

官網(wǎng)地址:https://spring.io/projects/spring-security

image-20201221141542308

官方文檔地址:https://docs.spring.io/spring-security/site/docs/5.4.2/reference/html5/

image-20201221141619207

2.對比

除了Spring Security可以進行授權認證以外庇配,Apach Shiro也可以進行授權認證,簡單對比如下:

  • 使用方面

    ShiroSpring更容易使用绍些,實現(xiàn)和最重要的理解,在SSM階段捞慌,授權認證一致都是Shiro的天下,雖然Spring Security已經(jīng)出現(xiàn)好久遇革,但是由于其配置復雜性卿闹,就讓很多人望而卻步了,同時對于一般的項目來說萝快,Shiro也完全能夠勝任锻霎。

但是SpringBoot以后,它的自動配置功能揪漩,簡化了Spring Security 配置步驟旋恼,只需要使用更少的配置來使用該框架

因此,具體是使用Shiro還是Security具體看整個項目的架構奄容,常見組合如下:

  • ssm + shiro

  • springboot / spring cloud + spring security

  • 其他方面

    Spring SecuritySpring 天然無縫結合冰更,同時還提供了 對 OAuth 與 OpenId的支持产徊,但是Shiro則需要手動實現(xiàn)

3.實現(xiàn)

在這里先通過實現(xiàn)一個最基本的HelloWorld,來了解Spring Security工作原理

  • 版本:

    Spring Boot : 2.3.7.RELEASE

  • 步驟

    創(chuàng)建SpringBoot項目蜀细,并且導入相關依賴舟铜,具體POM文件如下:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <!-- spring security 依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    

    創(chuàng)建Controller,具體如下:

    package com.briup.security.web;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/test")
    public class TestController {
    
        @GetMapping("/hello")
        public String add() {
            return "hello security";
        }
    
    }
    

    由于SpringBoot項目默認使用8080端口奠衔,在這里修改其默認端口谆刨,如下:

    server.port=8081
    

    啟動項目,并且訪問地址:http://127.0.0.1:8081/test/hello

    發(fā)現(xiàn)地址自動跳轉到了登錄界面归斤,進行授權認證

    image-20201221143409495

    只要導入Spring Security 依賴痊夭,那么默認就會有一個用戶名為user,其密碼為啟動時打印出的加密字符串,如下

    image-20201221143518978

    將用戶名脏里,密碼填充進去即可完成認證她我,如下

    image

    這樣就完成了一個基本Spring Security的簡單用戶認證,實際項目中迫横,用戶名與密碼肯定是要從數(shù)據(jù)庫中查詢出來番舆,這里只是做一個簡單的認證感受一下該框架的魅力

4.原理

思考:

==從上述的例子中,體驗了Spring Security認證流程员淫,那么它的原理到底是什么呢?==

Spring Security 是 基于 Servlet過濾器鏈進行安全認證的合蔽,如下:

image-20201221144756445

當客戶端發(fā)送請求,那么過濾器就會把該請求攔截下來介返,進行校驗拴事,校驗通過過濾器則放行,具體的過濾器如下:

image

從上圖中可以看出當用戶發(fā)送請求圣蝎,首先經(jīng)過了用戶名密碼校驗過濾器刃宵,我們來看一下該過濾器的源碼

UsernamePasswordAuthenticationFilter部分源碼如下:

image-20201221145210665

從源碼可以以下特點:

  • 先判斷認證請求是否是Post請求,如果不是則拋出異常
  • 再獲取用戶名密碼徘公,進行校驗牲证,校驗通過則把請求傳入下一個過濾器

經(jīng)過一系列的過濾器最終傳入到ExceptionTranslationFilter,其源碼如下:

image-20201221145829717

從圖中源碼可以看出,在該過濾器中主要是對異常進行處理关面,如果沒有異常坦袍,過濾器則直接放行到下一個過濾器FilterSecurityInterceptor過濾器

FilterSecurityInterceptor 位于過濾器鏈的最底部,一個方法級別的過濾器等太,其源碼如下:

image-20201221150115865
image-20201221150138195

從源碼可以看出在請求放行之前需要先執(zhí)行之前所有的過濾器捂齐,才會進行放行。

總結如下:

image-20201221151819316

5.加載

從上述中缩抡,知道Spring Security本質就是一個過濾器鏈奠宜,通過不同的過濾器組合使用從而實現(xiàn)認證與授權。

==那么這些過濾器是如何被加載的呢,與Spring容器又存在什么關系呢压真?==

Spring Security主要是用過DelegatingFilterProxy去管理過濾器實例娩嚼。

當然該類也是一個過濾器,使用該類最大的好處就是可以通過Spring容器來管理 Servler Filter的生命周期

如果過濾器需要Spring容器中的實例滴肿,也可以直接注入

該類部分源碼如下:

image-20201221153204453

在該類的源碼中岳悟,發(fā)現(xiàn)在doFilter方法中會調用initDelegate方法,該方法源碼如下:

image-20201221153434869

該方法的主要作用就是從Spring容器中拿到代理過濾器實例對象泼差,當該方法執(zhí)行完畢.

那么doFilter方法緊接著就會調用invokeDelegate,該方法的作用就是讓代理過濾器(FilterChainProxy)去執(zhí)行doFilter方法,其源碼如下:

image-20201221154126973

從上圖源碼可知,在doFilter方法中調用了doFilterInternal方法竿音,該方法源碼如下:

image-20201221154303050

在源碼中:

List<Filter> filters = getFilters(fwRequest);

該句代碼的意思就是將Spring Security中所有的過濾器全部加載到過濾器鏈中。這樣就把所有的過濾器加載進來了

總結:

image-20201221161002884

6 認證

6.1 簡介

==思考:==

通過之前的HelloWorld例子知道用戶名為user,密碼則是啟動時隨機產(chǎn)生的一段加密字符串

但是在開發(fā)中拴驮,用戶名密碼都需要自定義或者從數(shù)據(jù)庫表查詢賬號跟密碼,那么這些在操作

SpringSecurity中如何實現(xiàn)柴信?

實現(xiàn)上述問題一共有三種方式:

  • 通過配置文件
  • 通過配置類
  • 自定義實現(xiàn)類

接下來就讓挨個來實現(xiàn)這三種方式

6.2 配置文件

  • 創(chuàng)建SpringBoot項目(spring-security-config-file)套啤,并且導入Spring Security依賴, pom.xml部分內容如下:

    <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
  • 通過配置指定用戶名密碼

    server.port=8081
    spring.security.user.name=lisi
    spring.security.user.password=123456
    
  • 創(chuàng)建Controller,內容如下:

    package com.briup.security.web;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/file")
    public class ConfigFileController {
    
        @GetMapping("/hello")
        public String hello() {
            return "Hello Security";
        }
    }
    
    
  • 啟動并且進行測試

    image-20201223155209867

    啟動后并沒有給我們產(chǎn)生密碼,這是因為制定了用戶名密碼随常,所以就不會產(chǎn)生密碼

    訪問地址: http://127.0.0.1:8081/config/hello

    image-20201223155707855

    輸入配置文件配置的用戶名密碼即可看到返回的結果,如下:

    image-20201223155752230

    注意:這種方式只能用在學習階段潜沦,真正開發(fā)項目不會用這個

6.3 配置類

  • 創(chuàng)建SpringBoot項目(spring-security-config-class),并且導入Spring Security依賴, pom.xml部分內容如下:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
  • 創(chuàng)建配置類SecurityConfig绪氛,內容如下:

    package com.briup.security.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     *  Security 配置類 必須繼承  WebSecurityConfigurerAdapter
     *  同時必須加上 @Configuration注解
     */
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        /**
         * 重寫該方法唆鸡,并且通過 auth 參數(shù)設置用戶名密碼
         * @param auth
         * @throws Exception
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            // 對密碼進行加密
            String password = passwordEncoder().encode("123456");
            // 設置用戶名與密碼 以及 角色 由于這里只是學習,沒有用戶名和密碼枣察,
            // 因此直接寫死為admin
            auth.inMemoryAuthentication()
                .withUser("lisi")
                .password(password)
                .roles("adimin");
        }
    
        /**
         * 配置加密 解密實例
         * @return
         */
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
    
    

    了解內容(start)

    PasswordEncoder接口 是 Security提供用來對密碼進行加密的接口争占,源碼如下:

    image-20201223161654828

    BCryptPasswordEncoder是該接口的實現(xiàn)類,使用算法將接口三個方法全部實現(xiàn)

    因此使用該實現(xiàn)類的實例就可以對密碼進行加密

    了解內容(end)

  • 創(chuàng)建Controller序目,內容如下:

    package com.briup.security.web;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/class")
    public class ConfigFileController {
    
        @GetMapping("/hello")
        public String hello() {
            return "Hello Security";
        }
    }
    
    
  • 啟動測試

    注意:將項目端口設置為8082

    訪問地址:http://127.0.0.1:8082/class/hello

    image-20201223162746551

    輸入配置類配置好的用戶名密碼即可看到返回的結果臂痕,如下:

    image-20201223162826317

    注意:這種方式在實際開發(fā)項目中也不會用到,只用作學習階段

6.4 自定義

  • 簡介

    實際開發(fā)中猿涨,更多的是用戶名密碼甚至包括角色是從數(shù)據(jù)庫中查詢出來握童,而且在登錄的時候會有一些用戶自定義的邏輯存在,例如 判斷賬號的狀態(tài)等等

    但是上述兩種方式叛赚,用戶均不可以添加自定義邏輯澡绩,認證走的都是Security本身的那一套邏輯,因此急需要一套用戶可以自己定義認證邏輯的流程俺附。

    Spring Security中就要想自定義邏輯肥卡,只需要實現(xiàn)UserDetailsSerivice接口即可

  • 準備工作

    • 新建賬號表,存儲賬號數(shù)據(jù)昙读,以便認證時用戶名密碼從表中查詢

      -- ----------------------------
      -- Table structure for account
      -- ----------------------------
      DROP TABLE IF EXISTS `account`;
      CREATE TABLE `account`  (
        `id` bigint(20) NOT NULL COMMENT '主鍵',
        `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用戶名',
        `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密碼',
        PRIMARY KEY (`id`) USING BTREE
      ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
      
      -- ----------------------------
      -- Records of account
      -- ----------------------------
      INSERT INTO `account` VALUES (1, 'lisi', '123321');
      
    • 創(chuàng)建SpringBoot項目(spring-security-config-account),pom.xml內容如下:

      
      <dependencies>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-data-jpa</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-security</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
      
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <scope>runtime</scope>
          </dependency>
          <dependency>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
              <optional>true</optional>
          </dependency>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-test</artifactId>
              <scope>test</scope>
              <exclusions>
                  <exclusion>
                      <groupId>org.junit.vintage</groupId>
                      <artifactId>junit-vintage-engine</artifactId>
                  </exclusion>
              </exclusions>
          </dependency>
          <dependency>
              <groupId>org.springframework.security</groupId>
              <artifactId>spring-security-test</artifactId>
              <scope>test</scope>
          </dependency>
      </dependencies>
      

      數(shù)據(jù)操作框架為Spring Data JPA

    • application.yml

      src/main/resources下新增application.yml召调,內容如下:

      server:
        port: 9999
      spring:
        datasource:
          driver-class-name: com.mysql.cj.jdbc.Driver
          # 數(shù)據(jù)庫地址
          url: jdbc:mysql://172.16.0.154:3306/test?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
          username: root
          password: root
      
    • 啟動類內容如下:

      @SpringBootApplication
      public class SpringSecurityConfigAccountApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(SpringSecurityConfigAccountApplication.class, args);
          }
      
      
          /*
              加密實例
          */
          @Bean
          public PasswordEncoder passwordEncoder() {
              return new BCryptPasswordEncoder();
          }
      }
      
      
    • POJO類 內容如下:

      package com.briup.security.bean;
      
      import lombok.Data;
      
      import javax.persistence.Entity;
      import javax.persistence.Id;
      import javax.persistence.Table;
      import java.io.Serializable;
      
      @Data
      @Table(name = "account")
      @Entity
      public class Account implements Serializable {
          @Id
          private Long id;
          private String username;
          private String password;
      }
      
    • DAO層 內容如下

      package com.briup.security.dao;
      
      import com.briup.security.bean.Account;
      import org.springframework.data.jpa.repository.JpaRepository;
      
      public interface AccountDao extends JpaRepository<Account,Long> {
          Account  findByUsername(String username);
      }
      
      
  • 服務層開發(fā)

    要想讓Spring Security走自定義登錄邏輯流程,就只需要實現(xiàn)UserDetailsService接口,然后通過配置類進行指定即可唠叛。

    創(chuàng)建MyDetailService類只嚣,內容如下:

    package com.briup.security.service;
    
    import com.briup.security.bean.Account;
    import com.briup.security.dao.AccountDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Service;
    
    import java.util.Objects;
    
    @Service("myDetailService")
    public class MyDetailService implements UserDetailsService {
    
        @Autowired
        private AccountDao accountDao;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            Account account = accountDao.findByUsername(username);
            if (Objects.isNull(account)) {
                throw new UsernameNotFoundException("用戶名不存在");
            }
            User user =
                    new User(account.getUsername(),
                            passwordEncoder.encode(account.getPassword()),
                            AuthorityUtils.createAuthorityList("admin"));
            return user;
        }
    }
    
    

    代碼解釋:

    image-20201228204643115

    之所以實現(xiàn)這個接口,是因為SpringSecurity默認走的登錄邏輯流程就是UserDetailsService接口實現(xiàn)類對象的登錄邏輯艺沼,從下圖可以看出該接口的實現(xiàn)類有多個

    image-20201228204940508

    如果用戶不實現(xiàn)該接口册舞,那么登錄邏輯默認就是其他實現(xiàn)類實例的登錄邏輯

    image-20201228205131336

    返回的UserDetails,該接口主要包含一些用戶信息障般,其部分源碼如下:

    public interface UserDetails extends Serializable {}
      /**
           *  返回獲取用戶的所有權限
           */
          Collection<? extends GrantedAuthority> getAuthorities();
      
          /**
           * 返回取用戶密碼
           */
          String getPassword();
      
          /**
           * 返回獲取
           */
          String getUsername();
      
          /**
           * 判斷賬戶是否為過期
           */
          boolean isAccountNonExpired();
      
          /**
           * 判斷賬戶是被否鎖定
           */
          boolean isAccountNonLocked();
      
          /**
           * 憑證(密碼) 是否過期
           */
          boolean isCredentialsNonExpired();
      
          /**
           * 賬戶是否禁用 
           */
          boolean isEnabled();
    }
      
    

    這是一個接口调鲸,因此最后返回其實現(xiàn)類對象User如下圖:

    image-20201228210502115

    由于設計表時,并沒有設計賬戶權限和是否過期等等挽荡,因此全部設置為null,權限集合為admin

    注意:雖然上圖中的User對象藐石,只設置了User對象的用戶名,密碼以及角色權限定拟,但是查看其構造器源碼于微,在源碼中設置了幫助用戶設置了其他權限,如下

    image-20201228210808083
  • 配置

    新建SecurityConfig配置類青自,內容如下:

    package com.briup.security.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        @Qualifier("myDetailService")
        private UserDetailsService userDetailsService;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            /**
             * 設置認證邏輯為用戶自定義認證邏輯
             * 設置密碼加密處理器為 BCryptPasswordEncoder
             */
            auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder);
        }
    }
    
    
  • web層開發(fā)

    新建Controller株依,內容如下:

    package com.briup.security.web;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/test")
    public class TestController {
    
        @GetMapping("/hello")
        public String hello() {
            return "hello security";
        }
    
    }
    
  • 測試

    image-20201229142451654
    image-20201229142604812

    當輸入的賬號與密碼錯誤時,則直接報錯

    image-20201229142642963
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末延窜,一起剝皮案震驚了整個濱河市恋腕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逆瑞,老刑警劉巖荠藤,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異获高,居然都是意外死亡商源,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門谋减,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牡彻,“玉大人,你說我怎么就攤上這事出爹∽穑” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵严就,是天一觀的道長总寻。 經(jīng)常有香客問我,道長梢为,這世上最難降的妖魔是什么渐行? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任轰坊,我火速辦了婚禮,結果婚禮上祟印,老公的妹妹穿的比我還像新娘肴沫。我一直安慰自己,他們只是感情好蕴忆,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布颤芬。 她就那樣靜靜地躺著,像睡著了一般套鹅。 火紅的嫁衣襯著肌膚如雪站蝠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天卓鹿,我揣著相機與錄音菱魔,去河邊找鬼。 笑死吟孙,一個胖子當著我的面吹牛豌习,可吹牛的內容都是我干的。 我是一名探鬼主播拔疚,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼既荚!你這毒婦竟也來了稚失?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤恰聘,失蹤者是張志新(化名)和其女友劉穎句各,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晴叨,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡凿宾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了兼蕊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初厚。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖孙技,靈堂內的尸體忽然破棺而出产禾,到底是詐尸還是另有隱情,我是刑警寧澤牵啦,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布亚情,位于F島的核電站,受9級特大地震影響哈雏,放射性物質發(fā)生泄漏楞件。R本人自食惡果不足惜衫生,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望土浸。 院中可真熱鬧罪针,春花似錦、人聲如沸栅迄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毅舆。三九已至西篓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間憋活,已是汗流浹背岂津。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悦即,地道東北人吮成。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像辜梳,于是被迫代替她去往敵國和親粱甫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容