SpringSecurity認(rèn)證原理

認(rèn)證流程原理

認(rèn)證流程

SpringSecurity是基于Filter實(shí)現(xiàn)認(rèn)證和授權(quán),底層通過FilterChainProxy代理去調(diào)用各種Filter(Filter鏈),F(xiàn)ilter通過調(diào)用AuthenticationManager完成認(rèn)證 鞋既,通過調(diào)用AccessDecisionManager完成授權(quán)凉翻,SpringSecurity中核心的過濾器鏈詳細(xì)如下:


SecurityContextPersistenceFilter

Filter的入口和出口,它是用來將SecurityContext(認(rèn)證的上下文橡伞,里面有登錄成功后的認(rèn)證授權(quán)信息)對(duì)象持久到Session的Filter捶惜,同時(shí)會(huì)把SecurityContext設(shè)置給SecurityContextHolder方便我們獲取用戶認(rèn)證授權(quán)信息

UsernamePasswordAuthenticationFilter

默認(rèn)攔截“/login”登錄請(qǐng)求田藐,處理表單提交的登錄認(rèn)證,將請(qǐng)求中的認(rèn)證信息包括
username,password等封裝成UsernamePasswordAuthenticationToken售躁,然后調(diào)用
AuthenticationManager的認(rèn)證方法進(jìn)行認(rèn)證

BasicAuthenticationFilter

基本認(rèn)證坞淮,支持httpBasic認(rèn)證方式的Filter

RememberAuthenticationFilter

記住我功能實(shí)現(xiàn)的Filter

AnonymousAuthenticationFilter

匿名Filter茴晋,用來處理匿名訪問的資源陪捷,如果用戶未登錄,SecurityContext中沒有Authentication诺擅,
就會(huì)創(chuàng)建匿名的Token(AnonymousAuthenticationToken),然后通過
SecurityContextHodler設(shè)置到SecurityContext中市袖。

ExceptionTranslationFilter

用來捕獲FilterChain所有的異常,進(jìn)行處理烁涌,但是只會(huì)處理 AuthenticationException和AccessDeniedException苍碟,異常,其他的異常 會(huì)繼續(xù)拋出撮执。

FilterSecurityInterceptor

用來做授權(quán)的Filter,通過父類(AbstractSecurityInterceptor.beforeInvocation)調(diào)用
AccessDecisionManager.decide方法對(duì)用戶進(jìn)行授權(quán)微峰。

Security相關(guān)概念

Authentication

認(rèn)證對(duì)象,用來封裝用戶的認(rèn)證信息(賬戶狀態(tài)抒钱,用戶名蜓肆,密碼颜凯,權(quán)限等)所有提交給AuthenticationManager的認(rèn)證請(qǐng)求都會(huì)被封裝成一個(gè)Token的實(shí)現(xiàn),比如 最容易理解的UsernamePasswordAuthenticationToken仗扬,其中包含了用戶名和密碼

Authentication常用的實(shí)現(xiàn)類:

  • UsernamePasswordAuthenticationToken:用戶名密碼登錄的Token
  • AnonymousAuthenticationToken:針對(duì)匿名用戶的Token
  • RememberMeAuthenticationToken:記住我功能的的Token
AuthenticationManager

用戶認(rèn)證的管理類症概,所有的認(rèn)證請(qǐng)求(比如login)都會(huì)通過提交一個(gè)封裝了到了登錄信息的Token對(duì)象給 AuthenticationManager的authenticate()方法來實(shí)現(xiàn)認(rèn)證。AuthenticationManager會(huì) 調(diào)用AuthenticationProvider.authenticate進(jìn)行認(rèn)證早芭。認(rèn)證成功后彼城,返回一個(gè)包含了認(rèn) 證信息的Authentication對(duì)象。

AuthenticationProvider

認(rèn)證的具體實(shí)現(xiàn)類退个,一個(gè)provider是一種認(rèn)證方式的實(shí)現(xiàn)募壕,比如提交的用戶名密碼是通過和DB中查出的user記錄做比對(duì)實(shí)現(xiàn)的,那就有一個(gè)DaoProvider帜乞;如果是通過CAS請(qǐng)求單點(diǎn)登錄系統(tǒng)實(shí)現(xiàn)司抱,那就有一個(gè)CASProvider。按照Spring一貫的作風(fēng)黎烈, 主流的認(rèn)證方式它都已經(jīng)提供了默認(rèn)實(shí)現(xiàn)习柠,比如DAO、LDAP照棋、CAS资溃、OAuth2等。 前 面講了AuthenticationManager只是一個(gè)代理接口烈炭,真正的認(rèn)證就是由 AuthenticationProvider來做的溶锭。一個(gè)AuthenticationManager可以包含多個(gè)Provider, 每個(gè)provider通過實(shí)現(xiàn)一個(gè)support方法來表示自己支持那種Token的認(rèn)證符隙。 AuthenticationManager默認(rèn)的實(shí)現(xiàn)類是ProviderManager趴捅。

UserDetailService

用戶的認(rèn)證通過Provider來完成,而Provider會(huì)通過UserDetailService拿到數(shù)據(jù)庫(或 內(nèi)存)中的認(rèn)證信息然后和客戶端提交的認(rèn)證信息做校驗(yàn)霹疫。雖然叫Service,但是我更愿 意把它認(rèn)為是我們系統(tǒng)里經(jīng)常有的UserDao拱绑。

SecurityContext

當(dāng)用戶通過認(rèn)證之后,就會(huì)為這個(gè)用戶生成一個(gè)唯一的SecurityContext丽蝎,里面包含用 戶的認(rèn)證信息Authentication猎拨。通過SecurityContext我們可以獲取到用戶的標(biāo)識(shí) Principle和授權(quán)信息GrantedAuthrity。在系統(tǒng)的任何地方只要通過 SecurityHolder.getSecruityContext()就可以獲取到SecurityContext屠阻。在Shiro中通過 SecurityUtils.getSubject()到達(dá)同樣的目的

SpringSecurity認(rèn)證流程原理

  1. 請(qǐng)求過來會(huì)被過濾器鏈中的UsernamePasswordAuthenticationFilter攔截到红省,請(qǐng)求中的用戶名和密碼被封裝成UsernamePasswordAuthenticationToken(Authentication的實(shí)現(xiàn)類)

  2. 過濾器將UsernamePasswordAuthenticationToken提交給認(rèn)證管理器(AuthenticationManager)進(jìn)行認(rèn)證.

  3. AuthenticationManager委托AuthenticationProvider(DaoAuthenticationProvider)進(jìn)行認(rèn)證,AuthenticationProvider通過調(diào)用UserDetailsService獲取到數(shù)據(jù)庫中存儲(chǔ)的用戶信息(UserDetails)国觉,然后調(diào)用passwordEncoder密碼編碼器對(duì)UsernamePasswordAuthenticationToken中的密碼和UserDetails中的密碼進(jìn)行比較

  4. AuthenticationProvider認(rèn)證成功后封裝Authentication并設(shè)置好用戶的信息(用戶名吧恃,密碼,權(quán)限等)返回

  5. Authentication被返回到UsernamePasswordAuthenticationFilter,通過調(diào)用SecurityContextHolder工具把Authentication封裝成SecurityContext中存儲(chǔ)起來麻诀。然后UsernamePasswordAuthenticationFilter調(diào)用AuthenticationSuccessHandler.onAuthenticationSuccess做認(rèn)證成功后續(xù)處理操作

  6. 最后SecurityContextPersistenceFilter通過SecurityContextHolder.getContext()獲取到SecurityContext對(duì)象然后調(diào)用SecurityContextRepository將SecurityContext存儲(chǔ)起來痕寓,然后調(diào)用SecurityContextHolder.clearContext方法清理SecurityContext缸逃。

  • 注意:SecurityContext是一個(gè)和當(dāng)前線程綁定的工具,在代碼的任何地方都可以通過SecurityContextHolder.getContext()獲取到登陸信息厂抽。

認(rèn)證流程源碼跟蹤

SecurityContextPersistenceFilter

這個(gè)filter是整個(gè)filter鏈的入口和出口需频,請(qǐng)求開始會(huì)從SecurityContextRepository中 獲取SecurityContext對(duì)象并設(shè)置給SecurityContextHolder。在請(qǐng)求完成后將SecurityContextHolder持有的SecurityContext再保存到配置好的DecurityContextRepository中筷凤,同時(shí)清除SecurityContextHolder中的SecurityContext

  • 總結(jié)一下:

SecurityContextPersistenceFilter
作用就是請(qǐng)求來的時(shí)候?qū)苏J(rèn)證授權(quán)信息的SecurityContext對(duì)象從SecurityContextRepository中取出交給SecurityContextHolder工具類昭殉,方便我們通過SecurityContextHolder獲取SecurityContext從而獲取到認(rèn)證授權(quán)信息,請(qǐng)求走的時(shí)候又把SecurityContextHolder清空藐守,源碼如下:

public class SecurityContextPersistenceFilter extends GenericFilterBean {
  ...省略...
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  ...省略部分代碼...
  HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
            response);
  //從SecurityContextRepository獲取到SecurityContext 
    SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

    try {
     //把 securityContext設(shè)置到SecurityContextHolder挪丢,如果沒認(rèn)證通過,這個(gè)SecurtyContext就是空的
        SecurityContextHolder.setContext(contextBeforeChainExecution);
        //調(diào)用后面的filter卢厂,比如掉用usernamepasswordAuthenticationFilter實(shí)現(xiàn)認(rèn)證
        chain.doFilter(holder.getRequest(), holder.getResponse());

    }
    finally {
        //如果認(rèn)證通過了乾蓬,這里可以從SecurityContextHolder.getContext();中獲取到SecurityContext
        SecurityContext contextAfterChainExecution = SecurityContextHolder
                .getContext();
        // Crucial removal of SecurityContextHolder contents - do this before anything
        // else.
         //刪除SecurityContextHolder中的SecurityContext 
        SecurityContextHolder.clearContext();
        //把SecurityContext 存儲(chǔ)到SecurityContextRepository
        repo.saveContext(contextAfterChainExecution, holder.getRequest(),
                holder.getResponse());
        request.removeAttribute(FILTER_APPLIED);

        if (debug) {
            logger.debug("SecurityContextHolder now cleared, as request processing completed");
        }
    }
...省略...

UsernamePasswordAuthenticationFilter
它的作用是,攔截“/login”登錄請(qǐng)求慎恒,處理表單提交的登錄認(rèn)證任内,將請(qǐng)求中的認(rèn)證信息包括username,password等封裝成UsernamePasswordAuthenticationToken,然后調(diào)用
AuthenticationManager的認(rèn)證方法進(jìn)行認(rèn)證融柬。

public class UsernamePasswordAuthenticationFilter extends
        AbstractAuthenticationProcessingFilter {
    // ~ Static fields/initializers
    // =====================================================================================
    //從登錄請(qǐng)求中獲取參數(shù):username,password的名字
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    //默認(rèn)支持POST登錄
    private boolean postOnly = true;
    //默認(rèn)攔截/login請(qǐng)求死嗦,Post方式
    public UsernamePasswordAuthenticationFilter() {
        super(new AntPathRequestMatcher("/login", "POST"));
    }

    // ~ Methods
    // ========================================================================================================

    public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException {
            //判斷請(qǐng)求是否是POST
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        //獲取到用戶名和密碼
        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();
        //用戶名和密碼封裝Token
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);
        //設(shè)置details屬性
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        //調(diào)用AuthenticationManager().authenticate進(jìn)行認(rèn)證,參數(shù)就是Token對(duì)象
        return this.getAuthenticationManager().authenticate(authRequest);
    }

AuthenticationManager
請(qǐng)求通過UsernamePasswordAuthenticationFilter調(diào)用AuthenticationManager粒氧,默認(rèn)走的實(shí)現(xiàn)類是ProviderManager越除,它會(huì)找到能支持當(dāng)前認(rèn)證的AuthenticationProvider實(shí)現(xiàn)類調(diào)用器authenticate方法執(zhí)行認(rèn)證,認(rèn)證成功后會(huì)清除密碼外盯,然后拋出AuthenticationSuccessEvent事件

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
        InitializingBean {
        ...省略...
        //這里authentication 是封裝了登錄請(qǐng)求的認(rèn)證參數(shù)摘盆,
        //即:UsernamePasswordAuthenticationFilter傳入的Token對(duì)象
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        AuthenticationException parentException = null;
        Authentication result = null;
        Authentication parentResult = null;
        boolean debug = logger.isDebugEnabled();
        //找到所有的AuthenticationProvider ,選擇合適的進(jìn)行認(rèn)證
        for (AuthenticationProvider provider : getProviders()) {
            //是否支持當(dāng)前認(rèn)證
            if (!provider.supports(toTest)) {
                continue;
            }

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

            try {
                //調(diào)用provider執(zhí)行認(rèn)證
                result = provider.authenticate(authentication);

                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            }
                ...省略...
        }
        ...省略...
        //result就是Authentication 饱苟,使用的實(shí)現(xiàn)類依然是UsernamepasswordAuthenticationToken孩擂,
        //封裝了認(rèn)證成功后的用戶的認(rèn)證信息和授權(quán)信息
        if (result != null) {
            if (eraseCredentialsAfterAuthentication
                && (result instanceof CredentialsContainer)) {
            // Authentication is complete. Remove credentials and other secret data
            // from authentication
            //這里在擦除登錄密碼
            ((CredentialsContainer) result).eraseCredentials();
        }

        // If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
        // This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
        if (parentResult == null) {
            //發(fā)布事件
            eventPublisher.publishAuthenticationSuccess(result);
        }
        return result;
    }

DaoAuthenticationProvider
請(qǐng)求到達(dá)AuthenticationProvider,默認(rèn)實(shí)現(xiàn)是DaoAuthenticationProvider掷空,它的作用是根據(jù)傳入的Token中的username調(diào)用UserDetailService加載數(shù)據(jù)庫中的認(rèn)證授權(quán)信息(UserDetails)肋殴,然后使用PasswordEncoder對(duì)比用戶登錄密碼是否正確

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
        //密碼編碼器
        private PasswordEncoder passwordEncoder;
        //UserDetailsService 囤锉,根據(jù)用戶名加載UserDetails對(duì)象坦弟,從數(shù)據(jù)庫加載的認(rèn)證授權(quán)信息
        private UserDetailsService userDetailsService;
        //認(rèn)證檢查方法
        protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
        //獲取密碼
        String presentedPassword = authentication.getCredentials().toString();
        //通過passwordEncoder比較密碼,presentedPassword是用戶傳入的密碼官地,userDetails.getPassword()是從數(shù)據(jù)庫加載到的密碼
        //passwordEncoder編碼器不一樣比較密碼的方式也不一樣
        if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            logger.debug("Authentication failed: password does not match stored value");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
    }

    //檢索用戶酿傍,參數(shù)為用戶名和Token對(duì)象
    protected final UserDetails retrieveUser(String username,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        prepareTimingAttackProtection();
        try {
            //調(diào)用UserDetailsService的loadUserByUsername方法,
            //根據(jù)用戶名檢索數(shù)據(jù)庫中的用戶驱入,封裝成UserDetails 
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException(
                        "UserDetailsService returned null, which is an interface contract violation");
            }
            return loadedUser;
        }
        catch (UsernameNotFoundException ex) {
            mitigateAgainstTimingAttack(authentication);
            throw ex;
        }
        catch (InternalAuthenticationServiceException ex) {
            throw ex;
        }
        catch (Exception ex) {
            throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
        }
    }
    //創(chuàng)建認(rèn)證成功的認(rèn)證對(duì)象Authentication赤炒,使用的實(shí)現(xiàn)是UsernamepasswordAuthenticationToken,
    //封裝了認(rèn)證成功后的認(rèn)證信息和授權(quán)信息氯析,以及賬戶的狀態(tài)等
    @Override
    protected Authentication createSuccessAuthentication(Object principal,
            Authentication authentication, UserDetails user) {
        boolean upgradeEncoding = this.userDetailsPasswordService != null
                && this.passwordEncoder.upgradeEncoding(user.getPassword());
        if (upgradeEncoding) {
            String presentedPassword = authentication.getCredentials().toString();
            String newPassword = this.passwordEncoder.encode(presentedPassword);
            user = this.userDetailsPasswordService.updatePassword(user, newPassword);
        }
        return super.createSuccessAuthentication(principal, authentication, user);
    }
    ...省略...

這里提供了三個(gè)方法

  • additionalAuthenticationChecks:通過passwordEncoder比對(duì)密碼
  • retrieveUser:根據(jù)用戶名調(diào)用UserDetailsService加載用戶認(rèn)證授權(quán)信息
  • createSuccessAuthentication:登錄成功,創(chuàng)建認(rèn)證對(duì)象Authentication

然而你發(fā)現(xiàn) DaoAuthenticationProvider 中并沒有authenticate認(rèn)證方法莺褒,真正的認(rèn)證邏輯是通過父類AbstractUserDetailsAuthenticationProvider.authenticate方法完成的

AbstractUserDetailsAuthenticationProvider

public abstract class AbstractUserDetailsAuthenticationProvider implements
        AuthenticationProvider, InitializingBean, MessageSourceAware {
        //認(rèn)證邏輯
        public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
            //得到傳入的用戶名
            String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                : authentication.getName();
                //從緩存中得到UserDetails
            boolean cacheWasUsed = true;
            UserDetails user = this.userCache.getUserFromCache(username);
            if (user == null) {
            cacheWasUsed = false;

            try {
                //檢索用戶掩缓,底層會(huì)調(diào)用UserDetailsService加載數(shù)據(jù)庫中的UserDetails對(duì)象,保護(hù)認(rèn)證信息和授權(quán)信息
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            catch (UsernameNotFoundException notFound) {
                ...省略...
            }

            try {
                //前置檢查遵岩,主要檢查賬戶是否鎖定你辣,賬戶是否過期等
                preAuthenticationChecks.check(user);
                //比對(duì)密碼在這個(gè)方法里面比對(duì)的
                additionalAuthenticationChecks(user,
                    (UsernamePasswordAuthenticationToken) authentication);
            }
            catch (AuthenticationException exception) {
            ...省略...
            }
            //后置檢查
            postAuthenticationChecks.check(user);
    
            if (!cacheWasUsed) {
                //設(shè)置UserDetails緩存
                this.userCache.putUserInCache(user);
            }
    
            Object principalToReturn = user;
    
            if (forcePrincipalAsString) {
                principalToReturn = user.getUsername();
            }
            //認(rèn)證成功,創(chuàng)建Auhentication認(rèn)證對(duì)象
            return createSuccessAuthentication(principalToReturn, authentication, user);
}

UsernamePasswordAuthenticationFilter
認(rèn)證成功尘执,請(qǐng)求會(huì)重新回到UsernamePasswordAuthenticationFilter舍哄,然后會(huì)通過其父類AbstractAuthenticationProcessingFilter.successfulAuthentication方法將認(rèn)證對(duì)象封裝成SecurityContext設(shè)置到SecurityContextHolder中

protected void successfulAuthentication(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain, Authentication authResult)
            throws IOException, ServletException {

        if (logger.isDebugEnabled()) {
            logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
                    + authResult);
        }

        //認(rèn)證成功,吧Authentication 設(shè)置到SecurityContextHolder
        SecurityContextHolder.getContext().setAuthentication(authResult);
        //處理記住我業(yè)務(wù)邏輯
        rememberMeServices.loginSuccess(request, response, authResult);

        // Fire event
        if (this.eventPublisher != null) {
            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
                    authResult, this.getClass()));
        }
        //重定向登錄成功地址
        successHandler.onAuthenticationSuccess(request, response, authResult);
    }

然后后續(xù)請(qǐng)求又會(huì)回到SecurityContextPersistenceFilter誊锭,它就可以從SecurityContextHolder獲取到SecurityContext持久到SecurityContextRepository(默認(rèn)實(shí)現(xiàn)是HttpSessionSecurityContextRepository基于Session存儲(chǔ))

參考:https://blog.csdn.net/u014494148/article/details/108261616###

http://www.reibang.com/p/32fa221e03b7

https://blog.csdn.net/qq_22701869/article/details/103340878

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末表悬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子丧靡,更是在濱河造成了極大的恐慌蟆沫,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件温治,死亡現(xiàn)場離奇詭異饥追,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)罐盔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門但绕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人惶看,你說我怎么就攤上這事捏顺。” “怎么了纬黎?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵幅骄,是天一觀的道長。 經(jīng)常有香客問我本今,道長拆座,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任冠息,我火速辦了婚禮挪凑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逛艰。我一直安慰自己躏碳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布散怖。 她就那樣靜靜地躺著菇绵,像睡著了一般肄渗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咬最,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天翎嫡,我揣著相機(jī)與錄音,去河邊找鬼永乌。 笑死钝的,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的铆遭。 我是一名探鬼主播硝桩,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼枚荣!你這毒婦竟也來了碗脊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤橄妆,失蹤者是張志新(化名)和其女友劉穎衙伶,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體害碾,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡矢劲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了慌随。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芬沉。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖阁猜,靈堂內(nèi)的尸體忽然破棺而出丸逸,到底是詐尸還是另有隱情,我是刑警寧澤剃袍,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布黄刚,位于F島的核電站,受9級(jí)特大地震影響民效,放射性物質(zhì)發(fā)生泄漏憔维。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一畏邢、第九天 我趴在偏房一處隱蔽的房頂上張望业扒。 院中可真熱鬧,春花似錦棵红、人聲如沸凶赁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虱肄。三九已至,卻和暖如春交煞,著一層夾襖步出監(jiān)牢的瞬間咏窿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工素征, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留集嵌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓御毅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乔外,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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