spring-security

一 web.xml配置文件

  • 配置2個(gè)filter
<filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  • spring security是在DelegatingFilterProxy中進(jìn)行處理
  • tomcat初始化DelegatingFilterProxy類(lèi)對(duì)象
  • 從spring管理的bean中獲取FilterChainProxy,賦值為Filter delegate
  • FilterChainProxy類(lèi)型bean通過(guò)xml標(biāo)簽定義腊瑟,解析后交給spring容器管理嫩痰。

二 spring xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.2.xsd">

    <http pattern="/" security="none"/>

    <http auto-config="false" use-expressions="true" entry-point-ref="ssoAuthenticationEntryPoint"
          security-context-repository-ref="myCookieSecurityContextRepository" create-session="stateless">
        <intercept-url pattern="/index.html" access="hasRole('ROLE_USER')"/>

        <!-- 管理員 -->
        <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>

        <custom-filter ref="ssoLogoutFilter" before="LOGOUT_FILTER"/>
        <access-denied-handler ref="accessDeniedHandler"/>

    </http>

    <authentication-manager alias="authenticationManager"/>
</beans:beans>
  • org.springframework.security.config.SecurityNamespaceHandler 注冊(cè)xml標(biāo)簽解析函數(shù)
  • HttpSecurityBeanDefinitionParser http標(biāo)簽解析函數(shù)
  • 一個(gè)http標(biāo)簽解析成一個(gè)DefaultSecurityFilterChain類(lèi)型過(guò)濾串,按匹配規(guī)則RequestMatcher requestMatcher對(duì)目標(biāo)url執(zhí)行一組List<Filter> filters過(guò)濾操作。
  • 所有的DefaultSecurityFilterChain用于初始化filterChainProxy屬性List<SecurityFilterChain> filterChains矗蕊。filterChainProxy對(duì)象交給spring容器管理
  • 第二個(gè)http標(biāo)簽初始化了一組filter疙驾,當(dāng)有請(qǐng)求時(shí)依次調(diào)用各filter的doFilter()函數(shù)處理請(qǐng)求。

三 filter

3.1 SecurityContextPersistenceFilter

  • 實(shí)例化
public SecurityContextPersistenceFilter(SecurityContextRepository repo) {
        this.forceEagerSessionCreation = false;
        this.repo = repo;
    }
  • http標(biāo)簽配置security-context-repository-ref="myCookieSecurityContextRepository"為構(gòu)造函數(shù)參數(shù)repo
  • 過(guò)濾處理函數(shù)
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if(request.getAttribute("__spring_security_scpf_applied") != null) {//只處理一次
            chain.doFilter(request, response);
        } else {
            boolean debug = this.logger.isDebugEnabled();
            request.setAttribute("__spring_security_scpf_applied", Boolean.TRUE);
            if(this.forceEagerSessionCreation) {
                HttpSession session = request.getSession();
                if(debug && session.isNew()) {
                    this.logger.debug("Eagerly created session: " + session.getId());
                }
            }

            HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
//從請(qǐng)求中獲取用戶認(rèn)證信息昏翰,cookie或session中保存的認(rèn)證信息
            SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
            boolean var13 = false;

            try {
                var13 = true;
//默認(rèn)threadlocal方式保存變量苍匆,保存filter處理前的認(rèn)證信息                SecurityContextHolder.setContext(contextBeforeChainExecution);
//執(zhí)行后續(xù)filter處理
                chain.doFilter(holder.getRequest(), holder.getResponse());
                var13 = false;
            } finally {
                if(var13) {//異常發(fā)生
                    SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
                    SecurityContextHolder.clearContext();
                    this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
                    request.removeAttribute("__spring_security_scpf_applied");
                    if(debug) {
                        this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
                    }

                }
            }

            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
            //清理緩存數(shù)據(jù)
            SecurityContextHolder.clearContext();
            //保存認(rèn)證信息
            this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
//刪除處理標(biāo)記            request.removeAttribute("__spring_security_scpf_applied");
            if(debug) {
                this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
            }

        }
    }

3.2 WebAsyncManagerIntegrationFilter

  • 支持異步處理
  • 只調(diào)用一次處理
  • 過(guò)濾處理
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);//初始化并注冊(cè)到request中
        SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor)asyncManager.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
        if(securityProcessingInterceptor == null) {//初始化注冊(cè)到asyncManager中
            asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, new SecurityContextCallableProcessingInterceptor());
        }

        filterChain.doFilter(request, response);
    }

3.3 LogoutFilter

  • 實(shí)例化
public LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {
        Assert.notEmpty(handlers, "LogoutHandlers are required");
       //退出處理函數(shù),清理session及cookie及緩存中的認(rèn)證信息
        this.handlers = Arrays.asList(handlers);
        Assert.isTrue(!StringUtils.hasLength(logoutSuccessUrl) || UrlUtils.isValidRedirectUrl(logoutSuccessUrl), logoutSuccessUrl + " isn't a valid redirect URL");
        SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
        if(StringUtils.hasText(logoutSuccessUrl)) {
//指定退出后的跳轉(zhuǎn)url            urlLogoutSuccessHandler.setDefaultTargetUrl(logoutSuccessUrl);
        }
        this.logoutSuccessHandler = urlLogoutSuccessHandler;
        this.setFilterProcessesUrl("/j_spring_security_logout");
    }
  • 過(guò)濾處理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if(!this.requiresLogout(request, response)) {//匹配函數(shù)未匹配
            chain.doFilter(request, response);
        } else {
            //獲取認(rèn)證信息
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Logging out user '" + auth + "' and transferring to logout destination");
            }

            Iterator i$ = this.handlers.iterator();

            while(i$.hasNext()) {//遍歷退出處理函數(shù)棚菊,進(jìn)行處理
                LogoutHandler handler = (LogoutHandler)i$.next();
                handler.logout(request, response, auth);
            }
            //使用sendRedirect()浸踩,跳轉(zhuǎn)到退出url
            this.logoutSuccessHandler.onLogoutSuccess(request, response, auth);
        }
    }
  • sendRedirect()與forward()

sendRedirect就是服務(wù)端根據(jù)邏輯,發(fā)送一個(gè)狀態(tài)碼(Location ,狀態(tài)碼320),告訴瀏覽器重新去請(qǐng)求那個(gè)地址统求,一般來(lái)說(shuō)瀏覽器會(huì)用剛才請(qǐng)求的所有參數(shù)重新請(qǐng)求检碗,所以session,request參數(shù)都可以獲取据块。

forward是服務(wù)器請(qǐng)求資源,服務(wù)器直接訪問(wèn)目標(biāo)地址的URL折剃,把那個(gè)URL的響應(yīng)內(nèi)容讀取過(guò)來(lái)另假,然后把這些內(nèi)容再發(fā)給瀏覽器,瀏覽器根本不知道服務(wù)器發(fā)送的內(nèi)容是從哪兒來(lái)的怕犁,所以它的地址欄中還是原來(lái)的地址边篮。

3.4 SecurityContextHolderAwareRequestFilter

  • 獲取servlet版本信息
private boolean isServlet3() {
        return ClassUtils.hasMethod(ServletRequest.class, "startAsync", new Class[0]);
    }
  • 根據(jù)servlet版本信息初始化請(qǐng)求工廠
    this.requestFactory = (HttpServletRequestFactory)(this.isServlet3()?this.createServlet3Factory(this.rolePrefix):new HttpServlet25RequestFactory(this.trustResolver, this.rolePrefix));
  • servlet3工廠
private HttpServletRequestFactory createServlet3Factory(String rolePrefix) {
        HttpServlet3RequestFactory factory = new HttpServlet3RequestFactory(rolePrefix);
        factory.setTrustResolver(this.trustResolver);
        factory.setAuthenticationEntryPoint(this.authenticationEntryPoint);
        factory.setAuthenticationManager(this.authenticationManager);
        factory.setLogoutHandlers(this.logoutHandlers);
        return factory;
    }
  • 注入認(rèn)證管理器AuthenticationManager
  • 注入認(rèn)證入口函數(shù)AuthenticationEntryPoint
  • 注入logout處理函數(shù)
  • 過(guò)濾處理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

//請(qǐng)求工廠創(chuàng)建一個(gè)請(qǐng)求        chain.doFilter(this.requestFactory.create((HttpServletRequest)req, (HttpServletResponse)res), res);
    }
  • 請(qǐng)求的類(lèi)關(guān)系圖


    image.png

3.5 AnonymousAuthenticationFilter

  • 支持匿名用戶認(rèn)證,用戶名 anonymousUser因苹,角色ROLE_ANONYMOUS
  • 過(guò)濾處理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        if(this.applyAnonymousForThisRequest((HttpServletRequest)req)) {
            if(SecurityContextHolder.getContext().getAuthentication() == null) {//無(wú)認(rèn)證信息
                SecurityContextHolder.getContext().setAuthentication(this.createAuthentication((HttpServletRequest)req));//創(chuàng)建匿名用戶認(rèn)證
                if(this.logger.isDebugEnabled()) {
                    this.logger.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
                }
            } else if(this.logger.isDebugEnabled()) {
                this.logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
            }
        }
//使用匿名用戶進(jìn)行后續(xù)處理
        chain.doFilter(req, res);
    }

3.6 ExceptionTranslationFilter

  • 異常處理
  • 實(shí)例化
public ExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint, RequestCache requestCache) {
        this.accessDeniedHandler = new AccessDeniedHandlerImpl();//禁止訪問(wèn)處理
        this.authenticationTrustResolver = new AuthenticationTrustResolverImpl();
        this.throwableAnalyzer = new ExceptionTranslationFilter.DefaultThrowableAnalyzer();//異常解析
        this.requestCache = new HttpSessionRequestCache();
        Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
        Assert.notNull(requestCache, "requestCache cannot be null");
//認(rèn)證入口苟耻,異常發(fā)生時(shí),調(diào)用認(rèn)證入口函數(shù)
        this.authenticationEntryPoint = authenticationEntryPoint;
        this.requestCache = requestCache;
    }
  • 過(guò)濾處理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;

        try {
            chain.doFilter(request, response);
            this.logger.debug("Chain processed normally");
        } catch (IOException var9) {//io異常不處理
            throw var9;
        } catch (Exception var10) {
//解析異常
            Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
            RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
            if(ase == null) {
                ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
            }

            if(ase == null) {//解析失敗扶檐,拋出異常
                if(var10 instanceof ServletException) {
                    throw (ServletException)var10;
                }

                if(var10 instanceof RuntimeException) {
                    throw (RuntimeException)var10;
                }

                throw new RuntimeException(var10);
            }
//解析成功凶杖,進(jìn)行處理
            this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
        }

    }
  • 異常解析結(jié)果處理
private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {
        if(exception instanceof AuthenticationException) {
            this.logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
//認(rèn)證異常,重新認(rèn)證
            this.sendStartAuthentication(request, response, chain, (AuthenticationException)exception);
        } else if(exception instanceof AccessDeniedException) {//權(quán)限異常
            if(this.authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {//匿名用戶款筑,重新認(rèn)證
                this.logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point", exception);
                this.sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException("Full authentication is required to access this resource"));
            } else {//調(diào)用認(rèn)證失敗處理函數(shù)處理
                this.logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);
                this.accessDeniedHandler.handle(request, response, (AccessDeniedException)exception);
            }
        }

    }

3.7 FilterSecurityInterceptor

  • doFilter處理
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);//初始化
        this.invoke(fi);
    }
  • invoke()
public void invoke(FilterInvocation fi) throws IOException, ServletException {
        if(fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } else {
            if(fi.getRequest() != null) {//只處理一次
                fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
            }
            //調(diào)用各模版函數(shù)
            InterceptorStatusToken token = super.beforeInvocation(fi);

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

            super.afterInvocation(token, (Object)null);
        }

    }
  • beforeInvocation()
protected InterceptorStatusToken beforeInvocation(Object object) {
        Assert.notNull(object, "Object was null");
        boolean debug = this.logger.isDebugEnabled();
        if(!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {
            throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + this.getSecureObjectClass());
        } else {
            //獲取url匹配pattern規(guī)則的`intercept-url`配置
            Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
            if(attributes != null && !attributes.isEmpty()) {
                if(debug) {
                    this.logger.debug("Secure object: " + object + "; Attributes: " + attributes);
                }

 //未認(rèn)證會(huì)創(chuàng)建匿名認(rèn)證信息               if(SecurityContextHolder.getContext().getAuthentication() == null) {
                    this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
                }
//檢查是否需要進(jìn)行認(rèn)證智蝠,一種是匿名認(rèn)證,一種是配置了重復(fù)認(rèn)證奈梳。
                Authentication authenticated = this.authenticateIfRequired();

                try {//授權(quán)校驗(yàn)處理杈湾,失敗則拋AccessDeniedException
                    this.accessDecisionManager.decide(authenticated, object, attributes);
                } catch (AccessDeniedException var7) {
                    this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var7));//發(fā)送通知事件
                    throw var7;//異常處理函數(shù)中處理
                }

                if(debug) {
                    this.logger.debug("Authorization successful");
                }
                //通知授權(quán)結(jié)果
                if(this.publishAuthorizationSuccess) {
                    this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));
                }

                Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
                if(runAs == null) {
                    if(debug) {
                        this.logger.debug("RunAsManager did not change Authentication object");
                    }

                    return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
                } else {
                    if(debug) {
                        this.logger.debug("Switching to RunAs Authentication: " + runAs);
                    }

                    SecurityContext origCtx = SecurityContextHolder.getContext();
                    SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
                    SecurityContextHolder.getContext().setAuthentication(runAs);
                    return new InterceptorStatusToken(origCtx, true, attributes, object);
                }
            } else if(this.rejectPublicInvocations) {
                throw new IllegalArgumentException("Secure object invocation " + object + " was denied as public invocations are not allowed via this interceptor. " + "This indicates a configuration error because the " + "rejectPublicInvocations property is set to 'true'");
            } else {
                if(debug) {
                    this.logger.debug("Public object - authentication not attempted");
                }

                this.publishEvent(new PublicInvocationEvent(object));
                return null;
            }
        }
    }
  • finallyInvocation
protected void finallyInvocation(InterceptorStatusToken token) {
        if(token != null && token.isContextHolderRefreshRequired()) {//配置每次都刷新緩存的認(rèn)證信息,默認(rèn)配置為否
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Reverting to original Authentication: " + token.getSecurityContext().getAuthentication());
            }

            SecurityContextHolder.setContext(token.getSecurityContext());
        }

    }
  • afterInvocation
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
        if(token == null) {
            return returnedObject;
        } else {
            this.finallyInvocation(token);
            if(this.afterInvocationManager != null) {
                try {
//處理后校驗(yàn)
                    returnedObject = this.afterInvocationManager.decide(token.getSecurityContext().getAuthentication(), token.getSecureObject(), token.getAttributes(), returnedObject);
                } catch (AccessDeniedException var5) {
                    AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token.getAttributes(), token.getSecurityContext().getAuthentication(), var5);
                    this.publishEvent(event);
                    throw var5;
                }
            }

            return returnedObject;
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末攘须,一起剝皮案震驚了整個(gè)濱河市漆撞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌于宙,老刑警劉巖浮驳,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異捞魁,居然都是意外死亡至会,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)谱俭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)奉件,“玉大人,你說(shuō)我怎么就攤上這事昆著∠孛玻” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵宣吱,是天一觀的道長(zhǎng)窃这。 經(jīng)常有香客問(wèn)我,道長(zhǎng)征候,這世上最難降的妖魔是什么杭攻? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮疤坝,結(jié)果婚禮上兆解,老公的妹妹穿的比我還像新娘。我一直安慰自己跑揉,他們只是感情好锅睛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著历谍,像睡著了一般现拒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上望侈,一...
    開(kāi)封第一講書(shū)人閱讀 51,610評(píng)論 1 305
  • 那天印蔬,我揣著相機(jī)與錄音,去河邊找鬼脱衙。 笑死侥猬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捐韩。 我是一名探鬼主播退唠,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼荤胁!你這毒婦竟也來(lái)了瞧预?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仅政,失蹤者是張志新(化名)和其女友劉穎垢油,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體已旧,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秸苗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了运褪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惊楼。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秸讹,靈堂內(nèi)的尸體忽然破棺而出檀咙,到底是詐尸還是另有隱情,我是刑警寧澤璃诀,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布弧可,位于F島的核電站,受9級(jí)特大地震影響劣欢,放射性物質(zhì)發(fā)生泄漏棕诵。R本人自食惡果不足惜裁良,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望校套。 院中可真熱鬧价脾,春花似錦、人聲如沸笛匙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)妹孙。三九已至秋柄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蠢正,已是汗流浹背骇笔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留机隙,地道東北人蜘拉。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像有鹿,于是被迫代替她去往敵國(guó)和親旭旭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子佳鳖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355