SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—六蚯根、SpringSecurity整合jwt

目錄

SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—一、項目簡介和開發(fā)環(huán)境準備
SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—二浩聋、日志、接口文檔等實現(xiàn)
SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—三臊恋、主要頁面及接口實現(xiàn)
SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—四衣洁、整合SpringSecurity(上)
SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—五、整合SpringSecurity(下)
SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—六抖仅、SpringSecurity整合jwt
SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—七坊夫、處理一些問題
SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—八、AOP記錄用戶撤卢、異常日志
SpringSecurity權(quán)限管理系統(tǒng)實戰(zhàn)—九环凿、數(shù)據(jù)權(quán)限的配置

前言

最近是真的懶,感覺我每個月都有那么幾天什么都不想干放吩。智听。

畫風一轉(zhuǎn),前幾天的lpl忍界大戰(zhàn)是真的精彩渡紫,虛假的電競春晚:RNG vs IG 到推。真正的電競春晚 TES vs IG。TES自從阿水和kasra加入之后惕澎,狀態(tài)直接起飛莉测,在我看來TES將是s10奪冠熱門之一。不過這一次木葉村戰(zhàn)勝了曉組織唧喉。

本以為會打滿三局捣卤,沒想到ig直接2:0帶走忍抽。rookie線上壓制了新皇knight,確實永遠可以相信宋義進腌零,或許是因為?小鈺采訪吧梯找。

這兩把我最沒想到的是kasra被寧王壓著打,幾乎沒有節(jié)奏益涧,寶藍在哪都是阿水的噩夢。這波啊驯鳖,這波是盜版打贏了正版闲询,puff小小的證明了自己。

最后還是希望lpl的飯圈粉少一點浅辙,peace


1.jpg

進入正題

一扭弧、無狀態(tài)登錄

  • 有狀態(tài)登錄

    我們知道在原始的項目中我們是通過session和cookie來實現(xiàn)用戶的識別認證。但是這樣做無疑會增加服務器的壓力记舆,服務的保存了大量的數(shù)據(jù)鸽捻。如果業(yè)務需要擴展,搭建了集群的話泽腮,還需要將session共享御蒲。

  • 無狀態(tài)登錄

    而什么是無狀態(tài)登錄呢,簡而言之诊赊,就是服務的不需要再保存任何的用戶信息厚满,而是用戶自己攜帶者信息去訪問服務端,服務端通過這些信息來識別客戶端身份碧磅。這樣一來碘箍,有狀態(tài)登錄的缺點都被解決了,但是這同樣也會帶來新問題鲸郊。比如token信息無法在服務端注銷丰榴,必須要等其自己過期,占用更多的空間(意味著需要更多帶寬)秆撮,修改密碼后原本的token在沒過期時仍然可用訪問系統(tǒng)等四濒。

二、JWT介紹

1像吻、什么是jwt

JWT是 Json Web Token 的縮寫峻黍。它是基于 RFC 7519 標準定義的一種可以安全傳輸?shù)?小巧 和 自包含 的JSON對象。由于數(shù)據(jù)是使用數(shù)字簽名的拨匆,所以是可信任的和安全的姆涩。JWT可以使用HMAC算法對secret進行加密或者使用RSA的公鑰私鑰對來進行簽名。

我們來看一下jwt長什么樣

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJjb2Rlcm15IiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU5NjA4MDM5OX0.rfDtzMus50uAFnqMw1tm3c_ZYbmUNkIRqMkeJ0510PAH-RCUWtZkfNPTDYAGVVfDU6jmdEkGyNYvGy3UrNq5pA

JSON Web 令牌以緊湊的形式由三個部分組成惭每,由點分隔骨饿,它們包括:

  • 頭部
  • 負載
  • 簽名

頭部(Header)

jwt的頭部承載兩部分信息:

  • 聲明類型亏栈,這里是jwt
  • 聲明加密的算法 通常直接使用 HMAC SHA256

像這樣

{
  'typ': 'JWT',
  'alg': 'HS256'
}

載荷(Payload)

這個部分用來承載要傳遞的數(shù)據(jù),他的默認字段有

  • iss:發(fā)行人
  • exp:到期時間
  • sub:主題
  • aud:用戶
  • nbf:在此之前不可用
  • iat:發(fā)布時間
  • jti:JWT ID用于標識該JWT

除以上默認字段外宏赘,我們還可以自定義私有字段绒北,例如

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

簽名(Signature)

Signature 部分是對前兩部分的簽名,防止數(shù)據(jù)篡改察署。

2闷游、JWT工作流程

  • 用戶發(fā)起登錄請求
  • 服務端驗證身份,將用戶信息贴汪,標識等信息打包成jwt token返回給客戶端
  • 用戶拿到token脐往,攜帶token發(fā)送請求給服務端
  • 服務的驗證token是否可用,可用便根據(jù)其y業(yè)務邏輯返回相應結(jié)果扳埂。

3业簿、簡單實現(xiàn)

首先我們在maven中引入以下依賴

        <!--jjwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

新建JwtTest來測試一下

/**
 * @author codermy
 * @createTime 2020/7/30
 */
public class JwtTest {
    public static void main(String[] args) {
        String token = Jwts.builder()
                //用戶名
                .setSubject("codermy")
                //自定義屬性 放入用戶擁有請求權(quán)限
                .claim("authorities","admin")
                // 設置失效時間為1分鐘
                .setExpiration(new Date(System.currentTimeMillis()+1000*60))
                // 簽名算法和密鑰
                .signWith(SignatureAlgorithm.HS512, "java")
                .compact();
        System.out.println(token);
    }

輸出

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJjb2Rlcm15IiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU5NjA4MDM5OX0.rfDtzMus50uAFnqMw1tm3c_ZYbmUNkIRqMkeJ0510PAH-RCUWtZkfNPTDYAGVVfDU6jmdEkGyNYvGy3UrNq5pA

我們再來解析

    //解析token
        Claims claims = Jwts.parser()
                .setSigningKey("java")
                .parseClaimsJws(token)
                .getBody();
        System.out.println(claims);
        //獲取用戶名
        String username = claims.getSubject();
        System.out.println("username:"+username);
        //獲取權(quán)限
        String authority = claims.get("authorities").toString();
        System.out.println("權(quán)限:"+authority);
        System.out.println("到期時間:" + claims.getExpiration());

輸出

{sub=codermy, authorities=admin, exp=1596082316}
username:codermy
權(quán)限:admin
到期時間:Thu Jul 30 12:11:56 CST 2020

三、整合JWT

后端實現(xiàn)

其實jwt本身很好理解阳懂,無非就就是一把鑰匙梅尤,可用打開對應的鎖,這不過這把鑰匙稍微特殊點岩调,它還帶了主人的一些信息巷燥。難理解的是要將它符合業(yè)務邏輯的整合進框架中。我自己就被繞了好久才明白誊辉。

我這里寫了一個Jwt的工具類矾湃,用于生成和解析jwt

/**
 * @author codermy
 * @createTime 2020/7/23
 */
@Component
public class JwtUtils {
    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "created";
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private  Long expiration;
    // 創(chuàng)建token
    public  String generateToken(String username) {
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, secret)
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .compact();

    }
    // 從token中獲取用戶名
    public  String getUserNameFromToken(String token){
        return getTokenBody(token).getSubject();
    }

    // 是否已過期
    public  boolean isExpiration(String token){
        return getTokenBody(token).getExpiration().before(new Date());
    }

    private  Claims getTokenBody(String token){
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

}

然后我們可以將jwt的一些信息寫在yml中,使得可以靈活的配置堕澄。application.yml中添加如下配置

jwt:
  tokenHeader: Authorization #JWT存儲的請求頭
  secret: my-springsecurity-plus #JWT加解密使用的密鑰
  expiration: 604800 #JWT的超期限時間(60*60*24*7)
  tokenHead: 'Bearer ' #JWT負載中拿到開頭邀跃,空格別忘了

我們照著jwt的工作流程來,首先是登錄成功后客戶端會返回一個jwt token

所以我們首先自定義一個MyAuthenticationSuccessHandler繼承AuthenticationSuccessHandler蛙紫,這是登錄成功后的處理器

/**
 * @author codermy
 * @createTime 2020/8/1
 * 登錄成功
 */
@Component
@Slf4j
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    private JwtUtils jwtUtils;
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

        JwtUserDto userDetails = (JwtUserDto)authentication.getPrincipal();//拿到登錄用戶信息
        String jwtToken = jwtUtils.generateToken(userDetails.getUsername());//生成token
        Result result = Result.ok().message("登錄成功").jwt(jwtToken);
        System.out.println(JSON.toJSONString(result));//用于測試
        httpServletResponse.setCharacterEncoding("utf-8");//修改編碼格式
        httpServletResponse.setContentType("application/json");
        httpServletResponse.getWriter().write(JSON.toJSONString(result));//輸出結(jié)果
        httpServletResponse.sendRedirect("/api/admin");//重定向到api/admin頁面拍屑。我這里路由名取的不是很好
    }
}

然后我們再寫一個jwt的攔截器,讓每個請求都需要驗證jwt token

/**
 * @author codermy
 * @createTime 2020/7/30
 */
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private JwtUtils jwtUtils;
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String authHeader = request.getHeader(this.tokenHeader);//拿到requset中的head
        if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
            String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "
            String username = jwtUtils.getUserNameFromToken(authToken);//解析token獲取用戶名
            log.info("checking username:{}", username);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                if (userDetails != null) {//判斷是否存在這個給用戶
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    log.info("authenticated user:{}", username);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }
        chain.doFilter(request, response);
    }
    }

這里為了之后結(jié)果更直觀坑傅,自定義一個AuthenticationEntryPoint僵驰,用于在未登錄是訪問接口返回json而不是login.html

/**
 * @author codermy
 * @createTime 2020/8/1
 * 當未登錄或者token失效訪問接口時,自定義的返回結(jié)果
 */
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");//設置編碼格式
        response.setContentType("application/json");
        response.getWriter().println(JSON.toJSONString(Result.error().message("尚未登錄唁毒,或者登錄過期   " + authException.getMessage())));
        response.getWriter().flush();
    }
}

將上述方法加入到SpringSecurityConfig中

/**
 * @author codermy
 * @createTime 2020/7/15
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private VerifyCodeFilter verifyCodeFilter;
    @Autowired
    MyAuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    @Autowired
    private RestfulAccessDeniedHandler accessDeniedHandler;
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }

    /**
     * 身份認證接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers(HttpMethod.GET,
                        "/swagger-resources/**",
                        "/PearAdmin/**",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/swagger-ui.html",
                        "/webjars/**",
                        "/v2/**");//放行靜態(tài)資源
    }

    /**
     * anyRequest          |   匹配所有請求路徑
     * access              |   SpringEl表達式結(jié)果為true時可以訪問
     * anonymous           |   匿名可以訪問
     * denyAll             |   用戶不能訪問
     * fullyAuthenticated  |   用戶完全認證可以訪問(非remember-me下自動登錄)
     * hasAnyAuthority     |   如果有參數(shù)蒜茴,參數(shù)表示權(quán)限,則其中任何一個權(quán)限可以訪問
     * hasAnyRole          |   如果有參數(shù)浆西,參數(shù)表示角色粉私,則其中任何一個角色可以訪問
     * hasAuthority        |   如果有參數(shù),參數(shù)表示權(quán)限近零,則其權(quán)限可以訪問
     * hasIpAddress        |   如果有參數(shù)诺核,參數(shù)表示IP地址抄肖,如果用戶IP和參數(shù)匹配,則可以訪問
     * hasRole             |   如果有參數(shù)窖杀,參數(shù)表示角色漓摩,則其角色可以訪問
     * permitAll           |   用戶可以任意訪問
     * rememberMe          |   允許通過remember-me登錄的用戶訪問
     * authenticated       |   用戶登錄后可訪問
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.csrf().disable()//關(guān)閉csrf
                .sessionManagement()// 基于token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .httpBasic().authenticationEntryPoint(restAuthenticationEntryPoint)//未登陸時返回 JSON 格式的數(shù)據(jù)給前端入客,否則是html
                .and()
                .authorizeRequests()
                .antMatchers("/captcha").permitAll()//任何人都能訪問這個請求
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")//登錄頁面 不設限訪問
                .loginProcessingUrl("/login")//攔截的請求
                .successHandler(authenticationSuccessHandler) // 登錄成功處理器
                .permitAll()
                // 防止iframe 造成跨域
                .and()
                .headers()
                .frameOptions()
                .disable()
                .and();

        // 禁用緩存
        http.headers().cacheControl();

        // 添加JWT攔截器
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);


}

我這里直接貼了完整的代碼管毙,因為有添加也有刪除,不是很好描述痊项,大家對比著之前的來看锅风,都添加了注釋。

現(xiàn)在我們重啟項目鞍泉,用admin賬號來登錄。登錄成功后發(fā)現(xiàn)頁面并沒有跳轉(zhuǎn)到我們想去的頁面肮帐,但是控制臺打印出了我們想要的jwt信息

{"code":200,"data":[],"jwt":"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTU5NjI1OTgyOCwiZXhwIjoxNTk2ODY0NjI4fQ.Khn5t6WjOsuG6R2if1Q_gAeNq-zTamIAO32b1UVc6L8-6_IAHMaCeWr-v7H2-7Hob0SSmmK23dv71_da-YK8hw","msg":"登錄成功","success":true}

這是為什么呢咖驮?

著很好理解,因為我們的jwt攔截器已經(jīng)起了作用训枢,而我們原本的前端頁面是沒有把jwt token添加在header上的托修,所以認為沒有登錄,重定向到了登錄頁面恒界。

但是我們現(xiàn)在可以借助postman來測試睦刃,postman是一個測試api的工具,大家可以自行百度十酣,這里不做過多介紹涩拙。

在我們未攜帶jwt token信息時,訪問http://localhost:8080/api/menu接口耸采,就會報如下錯誤

2.png

我們在header中添加上兴泥,之前登錄成功控制臺打印的token信息(因為我們添加了圖片驗證碼,所以登錄不是很方便用postman虾宇,我們可以在瀏覽器中登錄或者先把驗證碼的攔截器去除)

3.png

加上了token信息之后再去訪問http://localhost:8080/api/menu接口搓彻,發(fā)現(xiàn)已經(jīng)可以正常訪問了

4.png

我們再嘗試用test用戶登錄后獲取到jwt token訪問該接口,會報如下錯誤


5.png

修改Swagger配置

直接貼代碼

/**
 * @author codermy
 * @createTime 2020/7/10
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;


    @Bean
    public Docket createRestApi() {
        ParameterBuilder ticketPar = new ParameterBuilder();
        List<Parameter> pars = new ArrayList<>();
        ticketPar.name(tokenHeader).description("token")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .defaultValue(tokenHead + " ")
                .required(true)
                .build();
        pars.add(ticketPar.build());
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(webApiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.codermy.myspringsecurityplus.controller"))
                .paths(PathSelectors.any())
                .paths(Predicates.not(PathSelectors.regex("/error.*")))

                .build()
                .globalOperationParameters(pars);
    }
    /**
     * 該套 API 說明,包含作者嘱朽、簡介旭贬、版本、等信息
     * @return
     */
    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("my-springsecurity-plus-API文檔")
                .description("本文檔描述了my-springsecurity-plus接口定義")
                .version("1.0.5")
                .build();
    }

}

現(xiàn)在再swagger中就可以添加token測試了


6.png

前端適配

那么我們現(xiàn)在已經(jīng)簡單的實現(xiàn)了jwt的無狀態(tài)登錄功能搪泳,需要做的就是讓前端的請求都帶上jwt token稀轨。

。森书。靶端。研究了半天沒弄懂谎势,所以暫時先擱置,下一章解決它杨名。有知道怎么設置請求頭的小伙伴也可以留言告訴我

所以本章結(jié)束的代碼是不能正常在瀏覽器運行的脏榆,但是可以在postman和swagger中測試(如果想運行,在SpringSecurityConfig中添加上.rememberMe()即可)

giteegithub中可獲取源代碼台谍,與本系列文章同步更新

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末须喂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子趁蕊,更是在濱河造成了極大的恐慌坞生,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掷伙,死亡現(xiàn)場離奇詭異是己,居然都是意外死亡,警方通過查閱死者的電腦和手機任柜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門卒废,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宙地,你說我怎么就攤上這事摔认。” “怎么了宅粥?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵参袱,是天一觀的道長。 經(jīng)常有香客問我秽梅,道長抹蚀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任风纠,我火速辦了婚禮况鸣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘竹观。我一直安慰自己镐捧,他們只是感情好,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布臭增。 她就那樣靜靜地躺著懂酱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪誊抛。 梳的紋絲不亂的頭發(fā)上列牺,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音拗窃,去河邊找鬼瞎领。 笑死泌辫,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的九默。 我是一名探鬼主播震放,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼驼修!你這毒婦竟也來了殿遂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤乙各,失蹤者是張志新(化名)和其女友劉穎墨礁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耳峦,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡恩静,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹲坷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜕企。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖冠句,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幸乒,我是刑警寧澤懦底,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站罕扎,受9級特大地震影響聚唐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腔召,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一杆查、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧臀蛛,春花似錦亲桦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抡柿,卻和暖如春舔琅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洲劣。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工备蚓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留课蔬,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓郊尝,卻偏偏與公主長得像二跋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子虚循,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359