spring security 核心 --authentication

什么是Spring Security驗證?

  1. 提示用戶輸入用戶名和密碼進行登錄饵蒂。

  2. 該系統(tǒng) (成功) 驗證該用戶名的密碼正確。

  3. 獲取該用戶的環(huán)境信息 (他們的角色列表等).

  4. 為用戶建立安全的環(huán)境最仑。

  5. 用戶進行迹栓,可能執(zhí)行一些操作,這是潛在的保護的訪問控制機制踏枣,檢查所需權(quán)限七兜,對當(dāng)前的安全的環(huán)境信息的操作丸凭。

前三個項目構(gòu)成的驗證過程,所以我們將看看這些是如何發(fā)生在Spring Security中的腕铸。

  1. 用戶名和密碼進行組合成一個實例UsernamePasswordAuthenticationToken (一個Authentication接口的實例, 我們之前看到的).

  2. 令牌傳遞到AuthenticationManager實例進行驗證惜犀。

  3. 該AuthenticationManager完全填充Authentication實例返回成功驗證。

  4. 安全環(huán)境是通過調(diào)用 SecurityContextHolder.getContext().setAuthentication(…?), 傳遞到返回的驗證對象建立的恬惯。

從這一點上來看向拆,用戶被認(rèn)為是被驗證的。spring security 驗證的經(jīng)典例子

import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

public class AuthenticationExample {
private static AuthenticationManager am = new SampleAuthenticationManager();

public static void main(String[] args) throws Exception {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    while(true) {
    System.out.println("Please enter your username:");
    String name = in.readLine();
    System.out.println("Please enter your password:");
    String password = in.readLine();
    try {
        Authentication request = new UsernamePasswordAuthenticationToken(name, password);
        Authentication result = am.authenticate(request);
        SecurityContextHolder.getContext().setAuthentication(result);
        break;
    } catch(AuthenticationException e) {
        System.out.println("Authentication failed: " + e.getMessage());
    }
    }
    System.out.println("Successfully authenticated. Security context contains: " +
            SecurityContextHolder.getContext().getAuthentication());
}
}

class SampleAuthenticationManager implements AuthenticationManager {
static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();

static {
    AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
}
public Authentication authenticate(Authentication auth) throws AuthenticationException {
    if (auth.getName().equals(auth.getCredentials())) {
    return new UsernamePasswordAuthenticationToken(auth.getName(),
        auth.getCredentials(), AUTHORITIES);
    }
    throw new BadCredentialsException("Bad Credentials");
}
}

直接設(shè)置SecurityContextHolder的內(nèi)容
事實上酪耳,Spring Security不介意你如何把Authentication對象包含在SecurityContextHolder內(nèi)浓恳。唯一的關(guān)鍵要求是SecurityContextHolder包含Authentication在AbstractSecurityInterceptor之前(我們會看到更多的版本)需要用戶授權(quán)操作刹缝。
你可以(很多用戶都這樣做)寫一個自己的過濾器或MVC控制器來提供驗證系統(tǒng)的交互,這些都不是基于Spring Security的颈将。比如梢夯,你也許使用容器管理認(rèn)證,從ThreadLocal或JNDI里獲得當(dāng)前用戶信息晴圾∷淘遥或者,你的公司可能有一個遺留系統(tǒng)死姚,它是一個企業(yè)標(biāo)準(zhǔn)人乓,你不能控制它。這種情況下都毒,很容易讓Spring Security工作色罚,也能提供驗證能力。你所需要的就是寫一個過濾器(或等價物)從指定位置讀取第三方用戶信息账劲,把它放到SecurityContextHolder里戳护。在這種情況下,你還需要考慮的事情通常是由內(nèi)置的認(rèn)證基礎(chǔ)設(shè)施自動照顧瀑焦。


spring security 支持很多種的認(rèn)證模式腌且,這些驗證絕大多數(shù)都是由第三方提供,或由相關(guān)的標(biāo)準(zhǔn)組織榛瓮,如互聯(lián)網(wǎng)工程任務(wù)組開發(fā)铺董。并且spring security 也提供自己的一組認(rèn)證功能。
從這些大量的認(rèn)證模式中抽象封裝就有了spring security的認(rèn)證模塊

常見的身份驗證有:

  1. HTTP BASIC 認(rèn)證頭 (基于 IETF RFC-based 標(biāo)準(zhǔn))
  2. HTTP Digest 認(rèn)證頭 ( IETF RFC-based 標(biāo)準(zhǔn))
  3. HTTP X.509 客戶端證書交換 ( IETF RFC-based 標(biāo)準(zhǔn))
  4. LDAP (一個非常常見的方法來跨平臺認(rèn)證需要, 尤其是在大型環(huán)境)
  5. Form-based authentication (用于簡單的用戶界面)
  6. OpenID 認(rèn)證
  7. Authentication based on pre-established request headers (such as Computer Associates Siteminder) 根據(jù)預(yù)先建立的請求有進行驗證
  8. JA-SIG Central Authentication Service (CAS禀晓,一個開源的SSO系統(tǒng) )
  9. Transparent authentication context propagation for Remote Method Invocation (RMI) and HttpInvoker (Spring遠(yuǎn)程協(xié)議)
  10. Automatic "remember-me" authentication (你可以勾選一個框以避免預(yù)定的時間段再認(rèn)證)
  11. Anonymous authentication (讓每一個未經(jīng)驗證的訪問自動假設(shè)為一個特定的安全標(biāo)識)
  12. Run-as authentication (在一個訪問應(yīng)該使用不同的安全標(biāo)識時非常有用)
  13. Java Authentication and Authorization Service (JAAS)
  14. JEE container autentication (所以如果愿你以可以任然使用容器管理的認(rèn)證)

身份驗證的一些理解
首先柄粹,http basic 和http digest ,http 的基本和摘要兩種認(rèn)證模式,這兩種模式是http 協(xié)議規(guī)范里面的兩種認(rèn)證機制匆绣,瀏覽器對這兩種機制都會有一個很好的支持。
基本認(rèn)證模式

基本認(rèn)證模式
客戶向服務(wù)器發(fā)送請求什黑,服務(wù)器返回401(未授權(quán))崎淳,要求認(rèn)證。401消息的頭里面帶了挑戰(zhàn)信息愕把。realm用以區(qū)分要不同認(rèn)證的部分拣凹。客戶端收到401后恨豁,將用戶名密碼和挑戰(zhàn)信息用BASE64加密形成證書嚣镜,發(fā)送回服務(wù)器認(rèn)證。語法如下:
challenge = "Basic" realm
credentials = "Basic" basic-credentials
示例:
認(rèn)證頭: WWW-Authenticate: Basic realm="zhouhh@mydomain.com"
證書:Authorization: Basic QsdfgWGHffuIcaNlc2FtZQ== 【虎.無名橘蜜,格式如Authorization:Basic base64(username:password)菊匿。付呕。。但是沒定義如何處理realm信息跌捆,簡單處理徽职,可以針對每個realm分別有一組user:pass信息。進一步佩厚,可以走md5摘要姆钉,但這些已經(jīng)超出標(biāo)準(zhǔn),估計不被瀏覽器支持抄瓦。

摘要模式和基本模式差不多潮瓶,這兩個模式的核心都是認(rèn)證頭和證書,只是摘要要復(fù)雜一些钙姊,并且摘要模式是一個md5 摘要毯辅,而basic 只是用base64 編碼了一下,basic 的使用需要配合https 協(xié)議摸恍,要不然基本就是明文傳輸悉罕。

為了防止重放攻擊,采用摘要訪問認(rèn)證立镶。在客戶發(fā)送請求后壁袄,收到一個401(未授權(quán))消息,包含一個Challenge媚媒。消息里面有一個唯一的字符串:nonce嗜逻,每次請求都不一樣$哉伲客戶將用戶名密碼和401消息返回的挑戰(zhàn)一起加密后傳給服務(wù)器栈顷。這樣即使有竊聽,他也無法通過每次認(rèn)證嵌巷,不能重放攻擊萄凤。Http并不是一個安全的協(xié)議。其內(nèi)容都是明文傳輸搪哪。因此不要指望Http有多安全靡努。語法如下:
challenge = "Digest" digest-challenge
digest-challenge = 1#( realm | [ domain ] | nonce | [opaque] |[stale] | [algorithm] | [qop-options] | [auth-param] )
domain = "domain" "=" <"> URI ( 1*SP URI ) <">
URI = absoluteURI | abs_path
nonce = "nonce" "=" nonce-value
nonce-value = quoted-string
opaque = "opaque" "=" quoted-string
stale = "stale" "=" ( "true" | "false" )
algorithm = "algorithm" "=" ( "MD5" | "MD5-sess" | token )
qop-options = "qop" "=" <"> 1#qop-value <">
qop-value = "auth" | "auth-int" | token
realm:讓客戶知道使用哪個用戶名和密碼的字符串。不同的領(lǐng)域可能密碼不一樣晓折。至少告訴用戶是什么主機做認(rèn)證惑朦,他可能會提示用哪個用戶名登錄,類似一個Email漓概。
domain:一個URI列表漾月,指示要保護的域∥刚洌可能是一個列表梁肿。提示用戶這些URI采用一樣的認(rèn)證蜓陌。如果為空或忽略則為整個服務(wù)器。
nonce:隨機字符串栈雳,每次401都不一樣护奈。跟算法有關(guān)。算法類似Base64加密:time-stamp H(time-stamp ":" ETag ":" private-key) 哥纫。time-stamp為服務(wù)器時鐘霉旗,ETag為請求的Etag頭。private-key為服務(wù)器知道的一個值蛀骇。
opaque:服務(wù)器產(chǎn)生的由客戶下去請求時原樣返回厌秒。最好是Base64串或十六進制字符串。
auth-param:為擴展用的擅憔,現(xiàn)階段忽略鸵闪。
其他域請參考RFC2617。授權(quán)頭語法:
credentials = "Digest" digest-response
digest-response = 1#( username | realm | nonce | digest-uri | response | [ algorithm ] | [cnonce] |
[opaque] | [message-qop] | [nonce-count] | [auth-param] )
username = "username" "=" username-value
username-value = quoted-string
digest-uri = "uri" "=" digest-uri-value
digest-uri-value = request-uri ; As specified by HTTP/1.1
message-qop = "qop" "=" qop-value
cnonce = "cnonce" "=" cnonce-value
cnonce-value = nonce-value
nonce-count = "nc" "=" nc-value
nc-value = 8LHEX
response = "response" "=" request-digest
request-digest = <"> 32LHEX <">
LHEX = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f"
response:加密后的密碼
digest-uri:拷貝Request-Line暑诸,用于Proxy
cnonce:如果qop設(shè)置蚌讼,才設(shè)置,用于雙向認(rèn)證个榕,防止攻擊篡石。
nonce-count:如果服務(wù)器看到同樣的計數(shù),就是一次重放西采。
示例:
401響應(yīng): HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest
realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
再次請求:
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"

下面是一個http basic 的事例凰萨,是轉(zhuǎn)載別的博客上的:
在瀏覽網(wǎng)頁時候,瀏覽器會彈出一個登錄驗證的對話框械馆,如下圖胖眷,這就是使用HTTP基本認(rèn)證。


20171130134526044.png

1霹崎、 客戶端發(fā)送http request 給服務(wù)器,服務(wù)器驗證該用戶是否已經(jīng)登錄驗證過了珊搀,如果沒有的話,
服務(wù)器會返回一個401 Unauthozied給客戶端尾菇,并且在Response 的 header “WWW-Authenticate” 中添加信息食棕。 如下


20171130134726538.png

2、:瀏覽器在接受到401 Unauthozied后错沽,會彈出登錄驗證的對話框。用戶輸入用戶名和密碼后眶拉,
瀏覽器用BASE64編碼后千埃,放在Authorization header中發(fā)送給服務(wù)器。如下圖:
20171130134815100.png

openId和 Oauth 很像都是用于提供第三方登錄忆植。


SecurityContextHolder, SecurityContext和Authentication 對象

最根本的對象是SecurityContextHolder放可。我們把當(dāng)前應(yīng)用程序的當(dāng)前安全環(huán)境的細(xì)節(jié)存儲到它里邊了谒臼, 它也包含了應(yīng)用當(dāng)前使用的主體細(xì)節(jié)。默認(rèn)情況下SecurityContextHolder使用ThreadLocal存儲這些信息耀里, 這意味著蜈缤,安全環(huán)境在同一個線程執(zhí)行的方法一直是有效的, 即使這個安全環(huán)境沒有作為一個方法參數(shù)傳遞到那些方法里冯挎。這種情況下使用ThreadLocal是非常安全的底哥,只要記得在處理完當(dāng)前主體的請求以后,把這個線程清除就行了房官。當(dāng)然趾徽,Spring Security自動幫你管理這一切了, 你就不用擔(dān)心什么了翰守。

有些程序并不適合使用ThreadLocal孵奶,因為它們處理線程的特殊方法。比如Swing客戶端也許希望Java Virtual Machine里所有的線程 都使用同一個安全環(huán)境蜡峰。SecurityContextHolder可以配置啟動策略來指定你希望上下文怎么被存儲了袁。對于一個獨立的應(yīng)用程序,你會使用SecurityContextHolder.MODE_GLOBAL策略湿颅。其他程序可能也想由安全線程產(chǎn)生的線程也承擔(dān)同樣的安全標(biāo)識载绿。這是通過使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL實現(xiàn)。你可以通過兩種方式更改默認(rèn)的SecurityContextHolder.MODE_THREADLOCAL模式肖爵。第一個是設(shè)置系統(tǒng)屬性卢鹦,第二個是調(diào)用SecurityContextHolder的靜態(tài)方法。大多數(shù)應(yīng)用程序不需要修改默認(rèn)值劝堪,但是如果你想要修改冀自,可以看一下SecurityContextHolder的JavaDocs中的詳細(xì)信息了解更多。

當(dāng)前用戶獲取信息

我們在SecurityContextHolder內(nèi)存儲目前與應(yīng)用程序交互的主要細(xì)節(jié)秒啦。Spring Security使用一個Authentication對象來表示這些信息熬粗。 你通常不需要創(chuàng)建一個自我認(rèn)證的對象,但它是很常見的用戶查詢的Authentication對象。你可以使用以下代碼塊-從你的應(yīng)用程序的任何部分-獲得當(dāng)前身份驗證的用戶的名稱余境,例如:

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}

通過調(diào)用getContext()返回的對象是SecurityContext接口的實例驻呐。這是保存在線程本地存儲中的對象。我們將在下面看到芳来,大多數(shù)的認(rèn)證機制以Spring Security返回UserDetails實例為主含末。

The UserDetailsService
從上面的代碼片段中還可以看出一件事,就是你可以從Authentication對象中獲得安全主體即舌。這個安全主體就是一個Object佣盒。大多數(shù)情況下,可以強制轉(zhuǎn)換成UserDetails對象 顽聂。 UserDetails是一個Spring Security的核心接口肥惭。它代表一個主體盯仪,是擴展的,而且是為特定程序服務(wù)的蜜葱。 想一下UserDetails章節(jié)全景,在你自己的用戶數(shù)據(jù)庫和如何把Spring Security需要的數(shù)據(jù)放到SecurityContextHolder里。為了讓你自己的用戶數(shù)據(jù)庫起作用牵囤,我們常常把UserDetails轉(zhuǎn)換成你系統(tǒng)提供的類爸黄,這樣你就可以直接調(diào)用業(yè)務(wù)相關(guān)的方法了(比如 getEmail(), getEmployeeNumber()等等)。

現(xiàn)在奔浅,你可能想知道馆纳,我應(yīng)該什么時候提供這個UserDetails對象呢?我怎么做呢?我想你說這個東西是聲明式的,我不需要寫任何代碼汹桦,怎么辦?簡單的回答是鲁驶,這里有一個特殊的接口叫UserDetailsService。這個接口里的唯一的一個方法舞骆,接收String類型的用戶名參數(shù)钥弯,返回UserDetails:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

這是Spring Security用戶加載信息的最常用的方法并且每當(dāng)需對用戶的信息時你會看到它使用的整個框架。

成功認(rèn)證后督禽,UserDetails用于構(gòu)建存儲在SecurityContextHolder(詳見 以下)的Authentication對象脆霎。好消息是,我們提供了一些UserDetailsService的實現(xiàn)狈惫,包括一個使用內(nèi)存映射(InMemoryDaoImpl)而另一個使用JDBC(JdbcDaoImpl)睛蛛。大多數(shù)用戶傾向于寫自己的,常常放到已有的數(shù)據(jù)訪問對象(DAO)上使用這些實現(xiàn)胧谈,表示他們的雇員忆肾,客戶或其他企業(yè)應(yīng)用中的用戶。記住這個優(yōu)勢菱肖,無論你用UserDetailsService返回的什么數(shù)據(jù)都可以通過SecurityContextHolder獲得客冈,就像上面的代碼片段講的一樣。

GrantedAuthority

除了主體稳强,另一個Authentication提供的重要方法是getAuthorities()场仲。這個方法提供了GrantedAuthority對象數(shù)組。毫無疑問退疫,GrantedAuthority是賦予到主體的權(quán)限渠缕。這些權(quán)限通常使用角色表示,比如ROLE_ADMINISTRATOR或ROLE_HR_SUPERVISOR褒繁。這些角色會在后面亦鳞,對web驗證,方法驗證和領(lǐng)域?qū)ο篁炞C進行配置。Spring Security的其他部分用來攔截這些權(quán)限蚜迅,期望他們被表現(xiàn)出現(xiàn)。GrantedAuthority對象通常是使用UserDetailsService讀取的俊抵。

通常情況下谁不,GrantedAuthority對象是應(yīng)用程序范圍下的授權(quán)。它們不會特意分配給一個特定的領(lǐng)域?qū)ο蠡栈濉R虼松才粒悴荒茉O(shè)置一個GrantedAuthority,讓他有權(quán)限展示編號54的Employee對象谎替,因為如果有成千上萬的這種授權(quán)偷溺,你會很快用光內(nèi)存(或者,至少钱贯,導(dǎo)致程序花費大量時間去驗證一個用戶)挫掏。當(dāng)然,Spring Security被明確設(shè)計成處理常見的需求秩命,但是你最好別因為這個目的使用項目領(lǐng)域模型安全功能尉共。

Spring Security主要由以下幾部分組成的:

  • SecurityContextHolder, 提供幾種訪問 SecurityContext的方式。

  • SecurityContext, 保存Authentication信息和請求對應(yīng)的安全信息弃锐。

  • Authentication, 展示Spring Security特定的主體袄友。

  • GrantedAuthority, 反應(yīng),在應(yīng)用程序范圍你霹菊,賦予主體的權(quán)限剧蚣。

  • UserDetails,通過你的應(yīng)用DAO,提供必要的信息旋廷,構(gòu)建Authentication對象鸠按。

  • UserDetailsService, 創(chuàng)建一個UserDetails,傳遞一個 String類型的用戶名(或者證書ID或其他).


The AuthenticationManager, ProviderManager and AuthenticationProvider

public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
}
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
        InitializingBean {
    ...
    private List<AuthenticationProvider> providers = Collections.emptyList();
    private AuthenticationManager parent;
    private boolean eraseCredentialsAfterAuthentication = true;

    public ProviderManager(List<AuthenticationProvider> providers) {
        this(providers, null);
    }
...
    // ~ Methods
    // ========================================================================================================
    ...
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        Authentication result = null;
        boolean debug = logger.isDebugEnabled();

        for (AuthenticationProvider provider : getProviders()) {
            if (!provider.supports(toTest)) {
                continue;
            }

            if (debug) {
                logger.debug("Authentication attempt using "
                        + provider.getClass().getName());
            }

            try {
                result = provider.authenticate(authentication);

                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            }
            catch (AccountStatusException e) {
                prepareException(e, authentication);
                // SEC-546: Avoid polling additional providers if auth failure is due to
                // invalid account status
                throw e;
            }
            catch (InternalAuthenticationServiceException e) {
                prepareException(e, authentication);
                throw e;
            }
            catch (AuthenticationException e) {
                lastException = e;
            }
        }

        if (result == null && parent != null) {
            // Allow the parent to try.
            try {
                result = parent.authenticate(authentication);
            }
            catch (ProviderNotFoundException e) {
                // ignore as we will throw below if no other exception occurred prior to
                // calling parent and the parent
                // may throw ProviderNotFound even though a provider in the child already
                // handled the request
            }
            catch (AuthenticationException e) {
                lastException = e;
            }
        }

        if (result != null) {
            if (eraseCredentialsAfterAuthentication
                    && (result instanceof CredentialsContainer)) {
                // Authentication is complete. Remove credentials and other secret data
                // from authentication
                ((CredentialsContainer) result).eraseCredentials();
            }

            eventPublisher.publishAuthenticationSuccess(result);
            return result;
        }

        // Parent was null, or didn't authenticate (or throw an exception).

        if (lastException == null) {
            lastException = new ProviderNotFoundException(messages.getMessage(
                    "ProviderManager.providerNotFound",
                    new Object[] { toTest.getName() },
                    "No AuthenticationProvider found for {0}"));
        }

        prepareException(lastException, authentication);

        throw lastException;
    }
...
    /**
     * Copies the authentication details from a source Authentication object to a
     * destination one, provided the latter does not already have one set.
     *
     * @param source source authentication
     * @param dest the destination authentication object
     */
    private void copyDetails(Authentication source, Authentication dest) {
        if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
            AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;

            token.setDetails(source.getDetails());
        }
    }
...
}
public interface AuthenticationProvider {
    // ~ Methods
    // ========================================================================================================
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

    boolean supports(Class<?> authentication);
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柳洋,一起剝皮案震驚了整個濱河市待诅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌熊镣,老刑警劉巖卑雁,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绪囱,居然都是意外死亡测蹲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門鬼吵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扣甲,“玉大人,你說我怎么就攤上這事×鹜冢” “怎么了启泣?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長示辈。 經(jīng)常有香客問我寥茫,道長,這世上最難降的妖魔是什么矾麻? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任纱耻,我火速辦了婚禮,結(jié)果婚禮上险耀,老公的妹妹穿的比我還像新娘弄喘。我一直安慰自己,他們只是感情好甩牺,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布蘑志。 她就那樣靜靜地躺著,像睡著了一般柴灯。 火紅的嫁衣襯著肌膚如雪卖漫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天赠群,我揣著相機與錄音羊始,去河邊找鬼。 笑死查描,一個胖子當(dāng)著我的面吹牛突委,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冬三,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼匀油,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了勾笆?” 一聲冷哼從身側(cè)響起敌蚜,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窝爪,沒想到半個月后弛车,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡蒲每,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年纷跛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邀杏。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡贫奠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唤崭,我是刑警寧澤拷恨,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站谢肾,受9級特大地震影響挑随,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜勒叠,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望膏孟。 院中可真熱鬧眯分,春花似錦、人聲如沸柒桑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽魁淳。三九已至飘诗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間界逛,已是汗流浹背昆稿。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留息拜,地道東北人溉潭。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像少欺,于是被迫代替她去往敵國和親喳瓣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

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