Spring security (一)架構(gòu)框架-Component、Service魁袜、Filter分析

??想要深入spring security的authentication (身份驗證)和access-control(訪問權(quán)限控制)工作流程桩撮,必須清楚spring security的主要技術(shù)點包括關(guān)鍵接口、類以及抽象類如何協(xié)同工作進(jìn)行authentication 和access-control的實現(xiàn)峰弹。

1.spring security 認(rèn)證和授權(quán)流程

常見認(rèn)證和授權(quán)流程可以分成:

  1. A user is prompted to log in with a username and password (用戶用賬密碼登錄)
  2. The system (successfully) verifies that the password is correct for the username(校驗密碼正確性)
  3. The context information for that user is obtained (their list of roles and so on).(獲取用戶信息context店量,如權(quán)限)
  4. A security context is established for the user(為用戶創(chuàng)建security context)
  5. The user proceeds, potentially to perform some operation which is potentially protected by an access control mechanism which checks the required permissions for the operation against the current security context information.(訪問權(quán)限控制,是否具有訪問權(quán)限)

1.1 spring security 認(rèn)證

上述前三點為spring security認(rèn)證驗證環(huán)節(jié):

  1. 通常通過AbstractAuthenticationProcessingFilter過濾器將賬號密碼組裝成Authentication實現(xiàn)類UsernamePasswordAuthenticationToken鞠呈;
  2. 將token傳遞給AuthenticationManager驗證是否有效融师,而AuthenticationManager通常使用ProviderManager實現(xiàn)類來檢驗;
  3. AuthenticationManager認(rèn)證成功后將返回一個擁有詳細(xì)信息的Authentication object(包括權(quán)限信息蚁吝,身份信息旱爆,細(xì)節(jié)信息,但密碼通常會被移除)窘茁;
  4. 通過SecurityContextHolder.getContext().getAuthentication().getPrincipal()將Authentication設(shè)置到security context中怀伦。

1.2 spring security訪問授權(quán)

  1. 通過FilterSecurityInterceptor過濾器入口進(jìn)入;
  2. FilterSecurityInterceptor通過其繼承的抽象類的AbstractSecurityInterceptor.beforeInvocation(Object object)方法進(jìn)行訪問授權(quán)山林,其中涉及了類AuthenticationManager房待、AccessDecisionManager、SecurityMetadataSource等驼抹。

根據(jù)上述描述的過程桑孩,我們接下來主要去分析其中涉及的一下Component、Service砂蔽、Filter洼怔。

2.核心組件(Core Component )

2.1 SecurityContextHolder

??SecurityContextHolder提供對SecurityContext的訪問,存儲security context(用戶信息左驾、角色權(quán)限等)镣隶,而且其具有下列儲存策略即工作模式:

  1. SecurityContextHolder.MODE_THREADLOCAL(默認(rèn)):使用ThreadLocal,信息可供此線程下的所有的方法使用诡右,一種與線程綁定的策略安岂,此天然很適合Servlet Web應(yīng)用。

  2. SecurityContextHolder.MODE_GLOBAL:使用于獨立應(yīng)用

  3. SecurityContextHolder.MODE_INHERITABLETHREADLOCAL:具有相同安全標(biāo)示的線程

修改SecurityContextHolder的工作模式有兩種方法 :

  1. 設(shè)置一個系統(tǒng)屬性(system.properties) : spring.security.strategy;
  2. 調(diào)用SecurityContextHolder靜態(tài)方法setStrategyName()

在默認(rèn)ThreadLocal策略中帆吻,SecurityContextHolder為靜態(tài)方法獲取用戶信息為:

  Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
   if (principal instanceof UserDetails) {      
        String username = ((UserDetails)principal).getUsername();
       
   } else {
        String username = principal.toString();
       
   }

但是一般不需要自身去獲取域那。
其中g(shù)etAuthentication()返回一個Authentication認(rèn)證主體,接下來分析Authentication、UserDetails細(xì)節(jié)次员。

2.2 Authentication

??Spring Security使用一個Authentication對象來描述當(dāng)前用戶的相關(guān)信息,其包含用戶擁有的權(quán)限信息列表败许、用戶細(xì)節(jié)信息(身份信息、認(rèn)證信息)淑蔚。Authentication為認(rèn)證主體在spring security中時最高級別身份/認(rèn)證的抽象市殷,常見的實現(xiàn)類UsernamePasswordAuthenticationToken。Authentication接口源碼:

public interface Authentication extends Principal, Serializable { 
    //權(quán)限信息列表,默認(rèn)GrantedAuthority接口的一些實現(xiàn)類
    Collection<? extends GrantedAuthority> getAuthorities(); 
    //密碼信息
    Object getCredentials();
    //細(xì)節(jié)信息刹衫,web應(yīng)用中的實現(xiàn)接口通常為 WebAuthenticationDetails醋寝,它記錄了訪問者的ip地址和sessionId的值
    Object getDetails();
    //通常返回值為UserDetails實現(xiàn)類
    Object getPrincipal();
    boolean isAuthenticated();
    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

前面兩個組件都涉及了UserDetails,以及GrantedAuthority其到底是什么呢带迟?2.3小節(jié)分析音羞。

2.3 UserDetails&GrantedAuthority

??UserDetails提供從應(yīng)用程序的DAO或其他安全數(shù)據(jù)源構(gòu)建Authentication對象所需的信息,包含GrantedAuthority仓犬。其官方實現(xiàn)類為User嗅绰,開發(fā)者可以實現(xiàn)其接口自定義UserDetails實現(xiàn)類。其接口源碼:

 public interface UserDetails extends Serializable {

     Collection<? extends GrantedAuthority> getAuthorities();

     String getPassword();

     String getUsername();

     boolean isAccountNonExpired();

     boolean isAccountNonLocked();

     boolean isCredentialsNonExpired();

     boolean isEnabled();
}

??UserDetails與Authentication接口功能類似婶肩,其實含義即是Authentication為用戶提交的認(rèn)證憑證(賬號密碼)办陷,UserDetails為系統(tǒng)中用戶正確認(rèn)證憑證,在UserDetailsService中的loadUserByUsername方法獲取正確的認(rèn)證憑證律歼。
??其中在getAuthorities()方法中獲取到GrantedAuthority列表是代表用戶訪問應(yīng)用程序權(quán)限范圍,此類權(quán)限通常是“role(角色)”啡专,例如ROLE_ADMINISTRATOR或ROLE_HR_SUPERVISOR险毁。GrantedAuthority接口常見的實現(xiàn)類SimpleGrantedAuthority。

3. 核心服務(wù)類(Core Services)

3.1 AuthenticationManager们童、ProviderManager以及AuthenticationProvider

??AuthenticationManager是認(rèn)證相關(guān)的核心接口畔况,是認(rèn)證一切的起點。但常見的認(rèn)證流程都是AuthenticationManager實現(xiàn)類ProviderManager處理慧库,而且ProviderManager實現(xiàn)類基于委托者模式維護(hù)AuthenticationProvider 列表用于不同的認(rèn)證方式跷跪。例如:

  1. 使用賬號密碼認(rèn)證方式DaoAuthenticationProvider實現(xiàn)類(繼承了AbstractUserDetailsAuthenticationProvide抽象類),其為默認(rèn)認(rèn)證方式齐板,進(jìn)行數(shù)據(jù)庫庫獲取認(rèn)證數(shù)據(jù)信息吵瞻。
  2. 游客身份登錄認(rèn)證方式AnonymousAuthenticationProvider實現(xiàn)類
  3. 從cookies獲取認(rèn)證方式RememberMeAuthenticationProvider實現(xiàn)類

??AuthenticationProvider為

ProviderManager源碼分析:

public Authentication authenticate(Authentication authentication)
        throws AuthenticationException {
    Class<? extends Authentication> toTest = authentication.getClass();
    AuthenticationException lastException = null;
    Authentication result = null;
    //AuthenticationProvider列表依次認(rèn)證
    for (AuthenticationProvider provider : getProviders()) {
        if (!provider.supports(toTest)) {
            continue;
        }
        try {
            //每個AuthenticationProvider進(jìn)行認(rèn)證
            result = provider.authenticate(authentication)
            if (result != null) {
                copyDetails(authentication, result);
                break;
            }
        }
        ....
        catch (AuthenticationException e) {
            lastException = e;
        }
    }
    //進(jìn)行父類AuthenticationProvider進(jìn)行認(rèn)證
    if (result == null && parent != null) {
        // Allow the parent to try.
        try {
            result = parent.authenticate(authentication);
        }
        catch (AuthenticationException e) {
            lastException = e;
        }
    }
       // 如果有Authentication信息,則直接返回
    if (result != null) {
        if (eraseCredentialsAfterAuthentication
                && (result instanceof CredentialsContainer)) {
                //清除密碼
            ((CredentialsContainer) result).eraseCredentials();
        }
        //發(fā)布登錄成功事件
        eventPublisher.publishAuthenticationSuccess(result);
        return result;
    }
        //如果都沒認(rèn)證成功甘磨,拋出異常
    if (lastException == null) {
        lastException = new ProviderNotFoundException(messages.getMessage(
                "ProviderManager.providerNotFound",
                new Object[] { toTest.getName() },
                "No AuthenticationProvider found for {0}"));
    }
    prepareException(lastException, authentication);
    throw lastException;
    }  

??ProviderManager 中的AuthenticationProvider列表橡羞,會依照次序去認(rèn)證,默認(rèn)策略下济舆,只需要通過一個AuthenticationProvider的認(rèn)證卿泽,即可被認(rèn)為是登錄成功,而且AuthenticationProvider認(rèn)證成功后返回一個Authentication實體滋觉,并為了安全會進(jìn)行清除密碼签夭。如果所有認(rèn)證器都無法認(rèn)證成功齐邦,則ProviderManager 會拋出一個ProviderNotFoundException異常。

3.2 UserDetailsService

??UserDetailsService接口作用是從特定的地方獲取認(rèn)證的數(shù)據(jù)源(賬號第租、密碼)侄旬。如何獲取到系統(tǒng)中正確的認(rèn)證憑證,通過loadUserByUsername(String username)獲取認(rèn)證信息煌妈,而且其只有一個方法:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;  

其常見的實現(xiàn)類從數(shù)據(jù)獲取的JdbcDaoImpl實現(xiàn)類儡羔,從內(nèi)存中獲取的InMemoryUserDetailsManager實現(xiàn)類,不過我們可以實現(xiàn)其接口自定義UserDetailsService實現(xiàn)類璧诵,如下:

public class CustomUserService implements UserDetailsService {
 @Autowired
 //用戶mapper
 private UserInfoMapper userInfoMapper;
 @Autowired
 //用戶權(quán)限mapper
 private PermissionInfoMapper permissionInfoMapper;
 @Override
 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    UserInfoDTO userInfo = userInfoMapper.getUserInfoByUserName(username);
    if (userInfo != null) {
        List<PermissionInfoDTO> permissionInfoDTOS = permissionInfoMapper.findByAdminUserId(userInfo.getId());
        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
        //組裝權(quán)限GrantedAuthority object
        for (PermissionInfoDTO permissionInfoDTO : permissionInfoDTOS) {
            if (permissionInfoDTO != null && permissionInfoDTO.getPermissionName() != null) {
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(
                        permissionInfoDTO.getPermissionName());
                grantedAuthorityList.add(grantedAuthority);
            }
        }
        //返回用戶信息
        return new User(userInfo.getUserName(), userInfo.getPasswaord(), grantedAuthorityList);
    }else {
        //拋出用戶不存在異常
        throw new UsernameNotFoundException("admin" + username + "do not exist");
      }
    }
}   

3.3 AccessDecisionManager&SecurityMetadataSource

??AccessDecisionManager是由AbstractSecurityInterceptor調(diào)用汰蜘,負(fù)責(zé)做出最終的訪問控制決策。

AccessDecisionManager接口源碼:

 //訪問控制決策
  void decide(Authentication authentication, Object secureObject,Collection<ConfigAttribute> attrs) 
        throws AccessDeniedException;
  //是否支持處理傳遞的ConfigAttribute
  boolean supports(ConfigAttribute attribute);
  //確認(rèn)class是否為AccessDecisionManager
  boolean supports(Class clazz);

??SecurityMetadataSource包含著AbstractSecurityInterceptor訪問授權(quán)所需的元數(shù)據(jù)(動態(tài)url之宿、動態(tài)授權(quán)所需的數(shù)據(jù))族操,在AbstractSecurityInterceptor授權(quán)模塊中結(jié)合AccessDecisionManager進(jìn)行訪問授權(quán)。其涉及了ConfigAttribute比被。
SecurityMetadataSource接口:

Collection<ConfigAttribute> getAttributes(Object object)
        throws IllegalArgumentException;

Collection<ConfigAttribute> getAllConfigAttributes();

boolean supports(Class<?> clazz);

我們還可以自定義SecurityMetadataSource數(shù)據(jù)源色难,實現(xiàn)接口FilterInvocationSecurityMetadataSource。例:

public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    public List<ConfigAttribute> getAttributes(Object object) {
        FilterInvocation fi = (FilterInvocation) object;
        String url = fi.getRequestUrl();
        String httpMethod = fi.getRequest().getMethod();
        List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

        // Lookup your database (or other source) using this information and populate the
        // list of attributes

        return attributes;
    }

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

3.4 PasswordEncoder

??為了存儲安全等缀,一般要對密碼進(jìn)行算法加密枷莉,而spring security提供了加密PasswordEncoder接口。其實現(xiàn)類有使用BCrypt hash算法實現(xiàn)的BCryptPasswordEncoder尺迂,SCrypt hashing 算法實現(xiàn)的SCryptPasswordEncoder實現(xiàn)類笤妙,實現(xiàn)類內(nèi)部實現(xiàn)可看源碼分析。而PasswordEncoder接口只有兩個方法:

public interface PasswordEncoder {
    //密碼加密
    String encode(CharSequence rawPassword);
    //密碼配對
    boolean matches(CharSequence rawPassword, String encodedPassword);
} 

4 核心 Security 過濾器(Core Security Filters)

4.1 FilterSecurityInterceptor

??FilterSecurityInterceptor是Spring security授權(quán)模塊入口噪裕,該類根據(jù)訪問的用戶的角色蹲盘,權(quán)限授權(quán)訪問那些資源(訪問特定路徑應(yīng)該具備的權(quán)限)。
??FilterSecurityInterceptor封裝FilterInvocation對象進(jìn)行操作膳音,所有的請求到了這一個filter召衔,如果這個filter之前沒有執(zhí)行過的話,那么首先執(zhí)行其父類AbstractSecurityInterceptor提供的InterceptorStatusToken token = super.beforeInvocation(fi)祭陷,在此方法中使用AuthenticationManager獲取Authentication中用戶詳情,使用ConfigAttribute封裝已定義好訪問權(quán)限詳情颗胡,并使用AccessDecisionManager.decide()方法進(jìn)行訪問權(quán)限控制。
FilterSecurityInterceptor源碼分析:

public void invoke(FilterInvocation fi) throws IOException, ServletException {
    if ((fi.getRequest() != null)
            && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
            && observeOncePerRequest) {
        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    }
    else {
        // first time this request being called, so perform security checking
        if (fi.getRequest() != null && observeOncePerRequest) {
            fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
        }
        //回調(diào)其繼承的抽象類AbstractSecurityInterceptor的方法
        InterceptorStatusToken token = super.beforeInvocation(fi);

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

        super.afterInvocation(token, null);
    }
}

AbstractSecurityInterceptor源碼分析:

protected InterceptorStatusToken beforeInvocation(Object object) {
    ....
    //獲取所有訪問權(quán)限(url-role)屬性列表(已定義在數(shù)據(jù)庫或者其他地方)
    Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
            .getAttributes(object);
    ....
    //獲取該用戶訪問信息(包括url哑蔫,訪問權(quán)限)
    Authentication authenticated = authenticateIfRequired();

    // Attempt authorization
    try {
        //進(jìn)行授權(quán)訪問
        this.accessDecisionManager.decide(authenticated, object, attributes);
    }catch
    ....
}

4.2 UsernamePasswordAuthenticationFilter

??UsernamePasswordAuthenticationFilter使用username和password表單登錄使用的過濾器,也是最為常用的過濾器闸迷。其源碼:

public Authentication attemptAuthentication(HttpServletRequest request,
    HttpServletResponse response) throws AuthenticationException {
     //獲取表單中的用戶名和密碼
     String username = obtainUsername(request);
     String password = obtainPassword(request);
     ...
     username = username.trim();
     //組裝成username+password形式的token
     UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
     username, password);
     // Allow subclasses to set the "details" property
     setDetails(request, authRequest);
     //交給內(nèi)部的AuthenticationManager去認(rèn)證嵌纲,并返回認(rèn)證信息
     return this.getAuthenticationManager().authenticate(authRequest);
}   

??其主要代碼為創(chuàng)建UsernamePasswordAuthenticationToken的Authentication實體以及調(diào)用AuthenticationManager進(jìn)行authenticate認(rèn)證,根據(jù)認(rèn)證結(jié)果執(zhí)行successfulAuthentication或者unsuccessfulAuthentication腥沽,無論成功失敗逮走,一般的實現(xiàn)都是轉(zhuǎn)發(fā)或者重定向等處理,不再細(xì)究AuthenticationSuccessHandler和AuthenticationFailureHandle今阳。興趣的可以研究一下其父類AbstractAuthenticationProcessingFilter過濾器师溅。

4.3 AnonymousAuthenticationFilter

AnonymousAuthenticationFilter是匿名登錄過濾器,它位于常用的身份認(rèn)證過濾器(如UsernamePasswordAuthenticationFilter盾舌、BasicAuthenticationFilter墓臭、RememberMeAuthenticationFilter)之后,意味著只有在上述身份過濾器執(zhí)行完畢后妖谴,SecurityContext依舊沒有用戶信息窿锉,AnonymousAuthenticationFilter該過濾器才會有意義——基于用戶一個匿名身份。
AnonymousAuthenticationFilter源碼分析:

public class AnonymousAuthenticationFilter extends GenericFilterBean implements
    InitializingBean {
    ...
    public AnonymousAuthenticationFilter(String key) {
        this(key, "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
    }
        ...
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {

        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            //創(chuàng)建匿名登錄Authentication的信息
            SecurityContextHolder.getContext().setAuthentication(
                    createAuthentication((HttpServletRequest) req));
                    ...
        }

        chain.doFilter(req, res);
    }
    //創(chuàng)建匿名登錄Authentication的信息方法
    protected Authentication createAuthentication(HttpServletRequest request) {
        AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,
            principal, authorities);
        auth.setDetails(authenticationDetailsSource.buildDetails(request));
        return auth;
    }
}

4.4 SecurityContextPersistenceFilter

??SecurityContextPersistenceFilter的兩個主要作用便是request來臨時膝舅,創(chuàng)建SecurityContext安全上下文信息和request結(jié)束時清空SecurityContextHolder嗡载。源碼后續(xù)分析。

小節(jié)總結(jié):

. AbstractAuthenticationProcessingFilter:主要處理登錄
. FilterSecurityInterceptor:主要處理鑒權(quán)

總結(jié)

??經(jīng)過上面對核心的Component仍稀、Service洼滚、Filter分析,初步了解了Spring Security工作原理以及認(rèn)證和授權(quán)工作流程琳轿。Spring Security認(rèn)證和授權(quán)還有很多負(fù)責(zé)的過程需要深入了解判沟,所以下次會對認(rèn)證模塊和授權(quán)模塊進(jìn)行更具體工作流程分析以及案例呈現(xiàn)。最后以上純粹個人結(jié)合博客和官方文檔總結(jié)崭篡,如有錯請指出!

最后可關(guān)注公眾號吧秕,一起學(xué)習(xí)琉闪。加群,每天會分享干貨砸彬,還有學(xué)習(xí)視頻領(lǐng)鹊弑小!

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砂碉,一起剝皮案震驚了整個濱河市蛀蜜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌增蹭,老刑警劉巖滴某,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異霎奢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)帝美,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門晤硕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舞箍,“玉大人,你說我怎么就攤上這事抵知∪碜澹” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵掖疮,是天一觀的道長颗祝。 經(jīng)常有香客問我螺戳,道長,這世上最難降的妖魔是什么盖腿? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任翩腐,我火速辦了婚禮膏燃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘等龙。我一直安慰自己,他們只是感情好而咆,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布暴备。 她就那樣靜靜地躺著,像睡著了一般浅妆。 火紅的嫁衣襯著肌膚如雪障癌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天康辑,我揣著相機(jī)與錄音疮薇,去河邊找鬼我注。 笑死,一個胖子當(dāng)著我的面吹牛励七,可吹牛的內(nèi)容都是我干的奔缠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼剿另,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谚攒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤野蝇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绕沈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡赠摇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年藕帜,在試婚紗的時候發(fā)現(xiàn)自己被綠了洽故。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡时甚,死狀恐怖荒适,靈堂內(nèi)的尸體忽然破棺而出嚣鄙,到底是詐尸還是另有隱情,我是刑警寧澤舅列,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布帐要,位于F島的核電站弥奸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盛霎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一期揪、第九天 我趴在偏房一處隱蔽的房頂上張望凤薛。 院中可真熱鬧,春花似錦缤苫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至掸宛,卻和暖如春招拙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饰序。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工求豫, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留诉稍,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓蚤告,卻偏偏與公主長得像服爷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子心褐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

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