Spring Security

Spring Security 學(xué)習(xí)

Spring Security是一種基于Spring AOP和Servlet規(guī)范中的FIlter實現(xiàn)的安全框架
是為給予Spring應(yīng)用程序提供聲明式安全保護(hù)的安全性框架甚疟,它能夠在Web請求級別和方法調(diào)用級別處理身份認(rèn)證和授權(quán)稻爬,并且因為基于Spring所以Spring Securitychongfenliyongle依賴注入和面向切面的技術(shù)。

Spring Security從兩個方面解決問題

  1. 它使用servlet規(guī)范中的Filter保護(hù)Web請求并限制URL級別的訪問。
  2. Spring Security還能夠使用Spring AOP保護(hù)方法的調(diào)用——借助于對象代理和適用通知涩咖,能夠確保只有具備適當(dāng)權(quán)限的用戶才能訪問安全保護(hù)的方法

Spring Security 命名空間的引入可以簡化我們的開發(fā)苞也,它涵蓋了大部分 Spring Security 常用的功能。它的設(shè)計是基于框架內(nèi)大范圍的依賴的棒厘,可以被劃分為以下幾塊纵穿。

  • Web/Http 安全:這是最復(fù)雜的部分。通過建立 filter 和相關(guān)的 service bean 來實現(xiàn)框架的認(rèn)證機(jī)制奢人。當(dāng)訪問受保護(hù)的 URL 時會將用戶引入登錄界面或者是錯誤提示界面谓媒。
    業(yè)務(wù)對象或者方法的安全:控制方法訪問權(quán)限的。
  • AuthenticationManager:處理來自于框架其他部分的認(rèn)證請求何乎。
  • AccessDecisionManager:為 Web 或方法的安全提供訪問決策句惯。會注冊一個默認(rèn)的,但是我們也可以通過普通 bean 注冊的方式使用自定義的 AccessDecisionManager支救。
  • AuthenticationProvider:AuthenticationManager 是通過它來認(rèn)證用戶的抢野。
  • UserDetailsService:跟 AuthenticationProvider 關(guān)系密切,用來獲取用戶信息的各墨。

通過Spring Security使用Spring MVC Web應(yīng)用程序集成指孤,只是在web.xml聲明 DelegatingFilterProxy 作為一個Servlet過濾器來攔截任何傳入的請求。

DelegatingFilterProxy是一個特殊的Servlet Filter贬堵,他本身做的工作并不多恃轩,只是將工作委托給一個javax.servlet.Filter實現(xiàn)類,這個實現(xiàn)類作為一個<bean>注冊在Spring的應(yīng)用上下文中

傳統(tǒng)配置DelegatingFilterProxy過濾器

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>
        org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
</filter>

編寫簡單的安全性配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extend WebSecurityConfigurerAdapter {
}

顧名思義@EnableWebSecurity注解將會啟用Web安全功能黎做。但是它本身并沒有什么用處叉跛,Spring Security必須配置在一個實現(xiàn)了WebSecurityConfigurerAdapter的bean中,或者拓展WebSecurityConfigurerAdapter蒸殿。

All-Security項目——Maven管理

項目結(jié)構(gòu)

項目分為5個Model分別為主模塊筷厘,APP安全模塊,瀏覽器安全模塊宏所,安全模塊核心酥艳,安全模塊的Demo

<modules>
<module>../SecurityApp</module>
<module>../SecurityBrowser</module>
<module>../SecurityCore</module>
<module>../SecurityDemo</module>
</modules>

我們可以看到在主模塊的pom.xml的文件中,管理了剩余的4個Model并且將其作為自己的子Model

使用Maven的dependencyManagement管理統(tǒng)一版本號

<dependencyManagement>
<dependencies>
  <dependency>
    <groupId>io.spring.platform</groupId>
    <artifactId>platform-bom</artifactId>
    <version>Cairo-SR2</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Finchley.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencies>
</dependencyManagement>

可以看到其中的兩個依賴都是從Spring 官網(wǎng)的項目中引下來的管理整個項目版本號的兩個依賴楣铁,分別為Spring IO和Spring Cloud

在導(dǎo)入Spring Cloud的時候需要注意每個版本的Spring Cloud管理的Spring Boot項目的版本不同玖雁,可能會因為與別的其他依賴產(chǎn)生版本沖突

接著我們舉其中的一個例子來看
現(xiàn)在我們來看Demo的Model中的pom.xml

<parent>
    <artifactId>bsb-security</artifactId>
    <groupId>com.bsb.security</groupId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../Security/pom.xml</relativePath>
</parent>

其中有這些結(jié)點,這些節(jié)點的意思就是該Model作為主模塊的子Model進(jìn)行管理盖腕,并且引用主模塊的pom中的依賴

項目的逐步搭建

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SecurityProperties securityProperties;

    @Bean
    public static PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.formLogin()
                .loginPage("/authentication/require")
                .loginProcessingUrl("/authentication/form")
                .and()
                .authorizeRequests()
                .antMatchers("/authentication/require",
                        securityProperties.getBrowser().getLoginPage()).permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();
    }
}

上面這個SpringSecurity的配置類首先繼承WebSecurityConfigurerAdapter并且重寫參數(shù)為HttpSecurity的方法赫冬,可以看到這一整個方法都是由一系列的鏈?zhǔn)秸{(diào)用來重寫的這個configure方法浓镜,下面我們來淺淺地解讀一下這個configure方法

  • 使用formLogin方法使得整個配置了SpringScurity的Rest服務(wù)開啟表單登錄認(rèn)證
  • 接著調(diào)用loginPage指定登錄頁,這里使用一個url來表示劲厌,并且通過一個Controller去接收這個登錄認(rèn)證請求
@RestController
public class BrowserSecurityController {

private Logger logger = LoggerFactory.getLogger(getClass());

private RequestCache requestCache = new HttpSessionRequestCache();

private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

@Autowired
private SecurityProperties securityProperties;

/**
 * 當(dāng)需要身份認(rèn)證時跳轉(zhuǎn)到這里
 * @param request
 * @param response
 * @return
 */
@RequestMapping("/authentication/require")
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {

    SavedRequest savedRequest = requestCache.getRequest(request, response);

    if (savedRequest != null) {
        String targetUrl = savedRequest.getRedirectUrl();
        logger.info("引發(fā)跳轉(zhuǎn)的請求是 " + targetUrl);
        if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
            logger.info(securityProperties.getBrowser().getLoginPage());
            redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
        }
    }

    return new SimpleResponse("訪問服務(wù)需要身份認(rèn)證膛薛,請引導(dǎo)用戶到登錄頁面");
}

}

  • 通過ResquestMapping映射到這個請求的url上,這個Controller的作用是补鼻,通過判斷是對數(shù)據(jù)的請求還是對html的靜態(tài)頁面的請求哄啄,對應(yīng)使用不用的登陸頁

讀取.yml文件中的屬性

如何讀取.yml這種配置文件中的屬性呢,Spring為我們提供了一個解決策略
因為我在項目中使用的是.yml作為項目的配置文件风范,這種配置文件在我看來咨跌,有幾個好處,層次比較清晰硼婿,并且結(jié)構(gòu)清晰锌半,配置使用的是K-V形式的配置,看一下我的SpringBoot項目中的.yml配置

spring:
    datasource:
        driver-class-name: com.mysql.jdbc.Driver
        name: root
        password: xxxxxx
        url: jdbc:mysql://localhost:3306/securityDemo?useSSL=false
session:
    store-type: none
output:
    ansi:
        enabled: always
server:
    port: 8060

bsb:
    security:
        browser:
            loginPage: /demo-signIn.html  

可以看到.yml這種配置文件有天然的樹狀結(jié)構(gòu)寇漫,并且通過類似父子結(jié)點能夠更好地去尋找配置的結(jié)點進(jìn)行修改或者查找

現(xiàn)在刊殉,我們就要來為上面安全配置類通過不同的條件,分配不同的認(rèn)證頁面州胳,我們來回顧一下上面的安全配置類

 http.formLogin()
                .loginPage("/authentication/require")
                .loginProcessingUrl("/authentication/form")
                .and()
                .authorizeRequests()
                .antMatchers("/authentication/require",
                        securityProperties.getBrowser().getLoginPage()).permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();

現(xiàn)在有兩個身份認(rèn)證的表單

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>signIn</title>
</head>
<body>
    <h2>標(biāo)準(zhǔn)登錄頁面</h2>
    <h2>表單登錄</h2>
    <form action="/authentication/form" method="post">
        <table>
            <tr>
                <td>用戶名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密碼:</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td colspan="2"><button type="submit">登錄</button> </td>
            </tr>
        </table>
    </form>
</body>
</html> 
                                標(biāo)準(zhǔn)登錄頁
      
      
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo-signIn</title>
</head>
<body>
    <h1>demo-signIn</h1>
</body>
</html>
                                demo登錄頁(為了簡單簡寫一下)

現(xiàn)在我們希望一切對html靜態(tài)頁面請求的身份驗證頁面都展示為demo登錄頁记焊,一切對數(shù)據(jù)請求的認(rèn)證頁面都展示為標(biāo)準(zhǔn)登錄頁

現(xiàn)在我們希望由.yml來配置不同的登錄頁,首先我們來封裝幾個類

public class BrowserProperties {

    private String loginPage = "/signIn.html";


    public String getLoginPage() {
       return loginPage;
    }

    public void setLoginPage(String loginPage) {
       this.loginPage = loginPage;
    }
}

首先封裝BrowserProperties類栓撞,其中只有一個成員變量就是loginPage并且添加getter/setter方法遍膜,并且為loginPage指定默認(rèn)的值為/signIn.html 這個就是我們的標(biāo)準(zhǔn)登錄頁

@ConfigurationProperties(prefix = "bsb.security")
public class SecurityProperties {

    private BrowserProperties browser = new BrowserProperties();

    public BrowserProperties getBrowser() {
        return browser;
    }

    public void setBrowser(BrowserProperties browserProperties) {
        this.browser = browserProperties;
    }
}

其次我們封裝的這個類是SecurityProperties類,其中引用一個BrowserProperties對象腐缤,并且這個對象的名稱為browser捌归,并且在這個類上我們使用Spring的注解 @ConfigurationProperties指定它是一個Spring的配置文件的讀取類,并且前綴為bsb.security看到這里大家或許能理解為什么要這么寫了岭粤,當(dāng)然如果只是封裝這兩個類,那么這個相當(dāng)于工具類的配置文件讀取的工作是完成不了的

@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {
}

最后一個類特笋,這個類使用Spring支持的兩個注解

  • @Configuration 告訴Spring這個類是一個Java配置類剃浇,其中可能會配置一些Bean進(jìn)行注入
  • @EnableConfigurationProperties(SecurityProperties.class) 開啟Spring的配置讀取并指定配置讀取類也就是我們剛才配置的SecurityProperties類

這個時候我們再回來看一下我們的Controller

@RequestMapping("/authentication/require")
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {

    SavedRequest savedRequest = requestCache.getRequest(request, response);

    if (savedRequest != null) {
        String targetUrl = savedRequest.getRedirectUrl();
        logger.info("引發(fā)跳轉(zhuǎn)的請求是 " + targetUrl);
        if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
            logger.info(securityProperties.getBrowser().getLoginPage());
            redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
        }
    }

    return new SimpleResponse("訪問服務(wù)需要身份認(rèn)證,請引導(dǎo)用戶到登錄頁面");
}

這個Controller中的一個映射請求url的方法通過判斷請求的url后綴是否為.html猎物,重定向到不同的登錄頁虎囚,因為我們在SecurityCoreConfig類上指定了讀取配置的類并且指定其為Java配置類,所以我們可以通過使用@AutoWired方式注入進(jìn)來并且成功讀取配置類

@Autowired
private SecurityProperties securityProperties;

securityProperties.getBrowser().getLoginPage();

bsb:
    security:
      browser:
        loginPage: /demo-signIn.html

可以看出來這其實就是按照yml這種樹狀結(jié)構(gòu)一級一級進(jìn)行讀取蔫磨,并且獲取到我們在配置類中設(shè)定的loginPage并且通過Controller的判斷成功重定向到不同的身份認(rèn)證頁

認(rèn)證步驟

看到了上面的一些簡單配置淘讥,我們現(xiàn)在來分析一下Spring Security的認(rèn)證步驟

如果我們不像上面那樣為SpringBoot創(chuàng)建的服務(wù)配置一個我們需要的安全配置類的話,就是說當(dāng)Spring Boot只是存在于我們的依賴中堤如,這個時候訪問我們的服務(wù)會有什么效果呢

訪問localhos:8060/user服務(wù)的時候

這個時候我們能夠看到在url上Spring Security 為我們重定向到了localhost:8060/login頁面蒲列,并且這個頁面很丑窒朋,沒錯這就是Spring Security默認(rèn)的認(rèn)證頁面,如果需要進(jìn)一步地去訪問我們的服務(wù)蝗岖,就必須通過這一關(guān)默認(rèn)的身份驗證

接下來我們還可以看到侥猩,開啟服務(wù)之后在idea的控制臺打印了這樣一句之前沒有過的話

Using generated security password: 135610b7-f01a-49c9-b11f-1e987da36f0c

這句話就是告訴我們本次服務(wù)開啟的時候,需要通過認(rèn)證的密碼是這一串密碼抵赢,接下來我們試一下(默認(rèn)的認(rèn)證用戶為user)

認(rèn)證
認(rèn)證成功后重定向到之前訪問的服務(wù)

我們可以看到在通過了Spring Security的默認(rèn)安全認(rèn)證之后我們順利地訪問到了我們的服務(wù)并且成功地返回了我們的響應(yīng)

如何通過自己的配置讓Spring Security使用我們自己的安全配置

通過繼承WebSecurityConfigurerAdapter 類重寫其中的configure方法欺劳,并且在其中通過鏈?zhǔn)秸{(diào)用進(jìn)行身份認(rèn)證,經(jīng)過上面模塊的說明铅鲤,我們可以看到使用自己的配置類進(jìn)行配置之后的安全模塊的啟用

Spring Security的工作原理(過濾器鏈)

過濾器鏈

我們可以看到前面的兩個過濾器

  • UsernamePasswordAuthticationFilter 表單登錄

    這個過濾器使用用戶表單登錄提交的username/password進(jìn)行校驗划提,如果提交了用戶名密碼,這個過濾器就會嘗試著用過濾到的username/password進(jìn)行校驗邢享,如果這個過濾器攔截到的請求沒有攜帶username/password參數(shù)腔剂,那么這個過濾器就會將請求移交給下一個過濾器進(jìn)行處理

  • BasicAuthenticationFilter 默認(rèn)的basic登錄

  • FilterSecurityInterceptor 這個攔截器作為Spring Security安全認(rèn)證的最后一環(huán)守門人,他會進(jìn)行最終的身份驗證去判斷是否能夠訪問Rest的服務(wù)

  • ExceptionTranslationFilter 用來捕獲FilterSecurityInterceptor根據(jù)認(rèn)證結(jié)果拋出的異常驼仪,并且做出相應(yīng)處理

如上圖掸犬,其中綠色的過濾器我們可以通過代碼的控制來控制其是否啟用,但是藍(lán)色绪爸,橙色這種攔截去和過濾器我們沒有辦法進(jìn)行控制湾碎,這些攔截器和過濾器會一直存在于過濾器鏈上進(jìn)行他們的工作

我們可以通過在每個過濾器源碼打斷點debug來觀察一次完整的安全認(rèn)證是怎么被處理的

  • 如果我們直接通過瀏覽器去訪問Rest服務(wù)的話,這個時候會直接進(jìn)入到最后的橙色FilterSecurityInterceptor 攔截器奠货,因為在這個過程中我們沒有攜帶任何關(guān)于username以及password的數(shù)據(jù)介褥,所以自然前面的綠色攔截器就沒有了作用

  • 并且這個時候拋出一個異常,異常拋出之后由ExceptionTranslationFilter 過濾器递惋,并且對這個異常進(jìn)行處理柔滔,實際上就是一個重定向到Spring Security默認(rèn)的認(rèn)證頁上進(jìn)行身份認(rèn)證

  • 這個時候可以看到調(diào)試的斷點到了UsernamePasswordAuthticationFilter 中,因為這個時候已經(jīng)使用了默認(rèn)的登錄認(rèn)證頁萍虽,并且通過UsernamePasswordAuthticationFilter 來認(rèn)證用戶的登錄請求

  • 最終還是到了FilterSecurityInterceptor 攔截器睛廊,這個時候,這個攔截器攔截到的已經(jīng)不是對認(rèn)證的請求了杉编,已經(jīng)是對Rest服務(wù)的請求了超全,這個時候我們可以看一下FilterSecurityInterceptor 的源碼

         InterceptorStatusToken token = super.beforeInvocation(fi);
    
         try {
             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
         } finally {
             super.finallyInvocation(token);
         }
    

這個時候如果身份認(rèn)證通過的話,就會這個攔截器就會調(diào)用下一個鏈進(jìn)行真正的對Rest服務(wù)的訪問

自定義的用戶認(rèn)證邏輯實現(xiàn)

上面我們說到的所有的認(rèn)證邏輯都是基于Spring Security的默認(rèn)實現(xiàn)邓馒,那么我們?nèi)绾瓮ㄟ^自定義的認(rèn)證邏輯實現(xiàn)用戶的認(rèn)證呢

UserDetailsService接口

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.security.core.userdetails;

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

我們可以看到在這個Spring官方提供的接口中只有一個方法嘶朱,接收一個var1的String變量作為參數(shù)(并且作為用戶名),并且可能會拋出UsernameNotFoundException異常

我們看一下UserDetails接口

package org.springframework.security.core.userdetails;

import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
}

其中包括了一些我們對平時項目的一些數(shù)據(jù)的封裝,包括用戶名密碼光酣,用戶是否被鎖住疏遏,是否解凍是否可以使用

實現(xiàn)UserDetails接口

@Component
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;


    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public UserDetails loadUserByUsername(String username) throws     UsernameNotFoundException {
    //        根據(jù)用戶名查找用戶信息
        logger.info(username);
        String passwordEn = passwordEncoder.encode("123456");
        logger.info("密碼為 " + passwordEn);
        return new User(username, passwordEncoder.encode("123456"),     AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

我們可以通過使用@AutoWired注解注入一些Mybaits或者JPA的DAO對象來實現(xiàn)根據(jù)數(shù)據(jù)庫中已有的記錄實現(xiàn)我們自己邏輯的功能

我們可以看到上述代碼的最后返回了一個User對象,這個User對象不是我們自己封裝的pojo對象,而是Spring官方提供的一個User類财异,大概看一下

public class User implements UserDetails, CredentialsContainer {
    private static final long serialVersionUID = 500L;
    private static final Log logger = LogFactory.getLog(User.class);
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;
    …………
}

其中也有很多的用戶信息的封裝倘零,并且重要的是實現(xiàn)了UserDetails接口
我們可以看一下這個User類的其中一個構(gòu)造器

public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
    this(username, password, true, true, true, true, authorities);
}

這個構(gòu)造器提供了三個參數(shù),用戶名密碼以及該用戶的授權(quán)宝当,一旦返回該User實例视事,也就說明我們自己實現(xiàn)的自定義的用戶認(rèn)證邏輯成功,并且我們可以通過我們自己的安全配置來進(jìn)行對用戶授權(quán)的驗證

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庆揩,一起剝皮案震驚了整個濱河市俐东,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌订晌,老刑警劉巖虏辫,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锈拨,居然都是意外死亡砌庄,警方通過查閱死者的電腦和手機(jī)艺蝴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門盹舞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人佛纫,你說我怎么就攤上這事缝彬∶妊妫” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵谷浅,是天一觀的道長扒俯。 經(jīng)常有香客問我,道長一疯,這世上最難降的妖魔是什么撼玄? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮墩邀,結(jié)果婚禮上掌猛,老公的妹妹穿的比我還像新娘。我一直安慰自己磕蒲,他們只是感情好留潦,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辣往,像睡著了一般。 火紅的嫁衣襯著肌膚如雪殖卑。 梳的紋絲不亂的頭發(fā)上站削,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音孵稽,去河邊找鬼许起。 笑死十偶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的园细。 我是一名探鬼主播惦积,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼猛频!你這毒婦竟也來了狮崩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤鹿寻,失蹤者是張志新(化名)和其女友劉穎睦柴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毡熏,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡坦敌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了痢法。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狱窘。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖财搁,靈堂內(nèi)的尸體忽然破棺而出蘸炸,到底是詐尸還是另有隱情,我是刑警寧澤妇拯,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布幻馁,位于F島的核電站,受9級特大地震影響越锈,放射性物質(zhì)發(fā)生泄漏仗嗦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一甘凭、第九天 我趴在偏房一處隱蔽的房頂上張望稀拐。 院中可真熱鬧,春花似錦丹弱、人聲如沸德撬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜓洪。三九已至,卻和暖如春坯苹,著一層夾襖步出監(jiān)牢的瞬間隆檀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留恐仑,地道東北人泉坐。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像裳仆,于是被迫代替她去往敵國和親腕让。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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