SpringSecurity自定義認(rèn)證

一. 前言

學(xué)習(xí)了SpringSecurity的使用抄淑,以及跟著源碼分析了一遍認(rèn)證流程,掌握了這個(gè)登錄認(rèn)證流程贺拣,才能更方便我們做自定義操作。

下面我們來(lái)學(xué)習(xí)下怎么實(shí)現(xiàn)多種登錄方式根吁,比如新增加一種郵箱驗(yàn)證碼登錄的形式,但SpringSecurity默認(rèn)的Usernamepassword方式不影響合蔽。

二. 自定義郵件驗(yàn)證碼認(rèn)證

0. 說(shuō)明

自定義一個(gè)郵箱驗(yàn)證碼的認(rèn)證击敌,將郵箱號(hào)碼作為key,驗(yàn)證碼作為value存放到Redis中緩存拴事。

1. 回顧

首先回顧下之前源碼分析的認(rèn)證流程沃斤,如下圖:

flow.jpg

2. 設(shè)計(jì)思路

  1. 首先前端是填寫(xiě)郵箱,點(diǎn)擊獲取驗(yàn)證碼

  2. 輸入獲取到的驗(yàn)證碼刃宵,點(diǎn)擊登錄按鈕衡瓶,發(fā)送登錄接口(/emial/login,此處不能使用默認(rèn)的/login,因?yàn)槲覀儗儆跀U(kuò)展)

  3. 自定義過(guò)濾器EmailCodeAuthenticationFilter(類(lèi)似UsernamepasswordAuthenticationFilter),獲取郵箱號(hào)碼與驗(yàn)證碼

  4. 將郵箱號(hào)碼與驗(yàn)證碼封裝為一個(gè)需要認(rèn)證的自定義Authentication對(duì)象EmailCodeAuthenticationToken(類(lèi)似UsernamepasswordAuthenticationToken)

  5. EmailCodeAuthenticationToken傳給AuthenticationManager接口的authenticate方法認(rèn)證

  6. 因?yàn)?code>AuthenticationManager的默認(rèn)實(shí)現(xiàn)類(lèi)為ProviderManager,而ProviderManager又是委托給了AuthenticationProvider牲证,因此

    自定義一個(gè)AuthenticationProvider接口的實(shí)現(xiàn)類(lèi)EmailCodeAuthenticationProvider,實(shí)現(xiàn)authenticate方法認(rèn)證

  7. 認(rèn)證成功與認(rèn)證失敗的處理:一種是直接在過(guò)濾器EmailCodeAuthenticationFilter中重寫(xiě)successfulAuthenticationunsuccessfulAuthentication哮针,另一種是實(shí)現(xiàn)AuthenticationSuccessHandlerAuthenticationFailureHandler進(jìn)行處理

  8. 總歸一句:照貓畫(huà)瓢

總結(jié)

需要實(shí)現(xiàn)以下幾個(gè)類(lèi):

  • 過(guò)濾器EmailCodeAuthenticationFilter
  • Authentication對(duì)象EmailCodeAuthenticationToken
  • AuthenticationProvider類(lèi)EmailCodeAuthenticationProvider
  • 自定義認(rèn)證成功與認(rèn)證失敗的Handler

3. 代碼實(shí)現(xiàn)

  1. 自定義Authentication對(duì)象(這里是EmailCodeAuthenticationToken)

    public class EmailCodeAuthenticationToken extends AbstractAuthenticationToken {
    
        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
        // 郵箱賬號(hào)
        private final Object principal;
        // 郵箱驗(yàn)證碼
        private Object credentials;
    
        /**
         * 沒(méi)有經(jīng)過(guò)驗(yàn)證時(shí),權(quán)限位空坦袍,setAuthenticated設(shè)置為不可信令牌
         * @param principal
         * @param credentials
         */
        public EmailCodeAuthenticationToken(Object principal, Object credentials) {
            super(null);
            this.principal = principal;
            this.credentials = credentials;
            setAuthenticated(false);
        }
    
        /**
         * 已認(rèn)證后十厢,將權(quán)限加上,setAuthenticated設(shè)置為可信令牌
         * @param principal
         * @param credentials
         * @param authorities
         */
        public EmailCodeAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            this.principal = principal;
            this.credentials = credentials;
            super.setAuthenticated(true);
        }
    
        @Override
        public Object getCredentials() {
            return this.credentials;
        }
    
        @Override
        public Object getPrincipal() {
            return this.principal;
        }
    
        @Override
        public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
            Assert.isTrue(!isAuthenticated,
                          "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
            super.setAuthenticated(false);
        }
    
        @Override
        public void eraseCredentials() {
            super.eraseCredentials();
            this.credentials = null;
        }
    }
    

    說(shuō)明:

    模仿UsernamepasswordAuthenticationToken定義捂齐,繼承AbstractAuthenticationToken蛮放,這里注意的是要定義兩個(gè)構(gòu)造器,分別對(duì)應(yīng)未認(rèn)證和已認(rèn)證的Token辛燥,已認(rèn)證的調(diào)用super.setAuthenticated(true);

  2. 自定義Filter(這里是EmailCodeAuthenticationFilter)

    public class EmailCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
        // 前端傳來(lái)的參數(shù)名
        private final String SPRING_SECURITY_EMAIL_KEY = "email";
        private final String SPRING_SECURITY_EMAIL_CODE_KEY = "email_code";
    
        // 自定義的路徑匹配器筛武,攔截Url為:/email/login
        private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/email/login",
                                                                                                                "POST");
    
        // 是否僅POST方式
        private boolean postOnly = true;
    
        public EmailCodeAuthenticationFilter() {
            super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
        }
    
        /**
         * 認(rèn)證方法,在父類(lèi)的doFilter中調(diào)用
         * @param request
         * @param response
         * @return
         * @throws AuthenticationException
         * @throws IOException
         * @throws ServletException
         */
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
            if (this.postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not support : " + request.getMethod());
            }
            System.out.println("email attemptAuthentication");
            // 獲取郵箱號(hào)碼
            String email = obtainEmail(request);
            email = (email != null) ? email : "";
            email = email.trim();
            // 獲取郵箱驗(yàn)證碼
            String emailCode = obtainEmailCode(request);
            emailCode = (emailCode != null) ? emailCode : "";
            // 構(gòu)造Token
            EmailCodeAuthenticationToken authRequest = new EmailCodeAuthenticationToken(email, emailCode);
            setDetails(request, authRequest);
            // 使用AuthenticationManager來(lái)進(jìn)行認(rèn)證
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    
        /**
         * 獲取請(qǐng)求中email參數(shù)
         * @param request
         * @return
         */
        @Nullable
        protected String obtainEmail(HttpServletRequest request) {
            return request.getParameter(this.SPRING_SECURITY_EMAIL_KEY);
        }
    
        /**
         * 獲取請(qǐng)求中驗(yàn)證碼參數(shù)email_code
         * @param request
         * @return
         */
        @Nullable
        protected String obtainEmailCode(HttpServletRequest request) {
            return request.getParameter(this.SPRING_SECURITY_EMAIL_CODE_KEY);
        }
    
        protected void setDetails(HttpServletRequest request, EmailCodeAuthenticationToken authRequest) {
            authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
        }
    }
    

    說(shuō)明:

    模仿UsernamepasswordAuthentionFilter實(shí)現(xiàn)自定義的過(guò)濾器挎塌,核心是attemptAuthentication方法.

  3. 自定義AuthenticationProvider(這里是EmailCodeAuthenticationProvider)

    public class EmailCodeAuthenticationProvider implements AuthenticationProvider {
        protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    
        private  EmailCodeUserDetailsService emailCodeUserDetailsService;
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            Assert.isInstanceOf(EmailCodeAuthenticationToken.class, authentication,
                                () -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
                                                               "Only UsernamePasswordAuthenticationToken is supported"));
            // 此時(shí)的authentication還沒(méi)認(rèn)證徘六,獲取郵箱號(hào)碼
            EmailCodeAuthenticationToken unAuthenticationToken = (EmailCodeAuthenticationToken) authentication;
            // 做校驗(yàn)
            UserDetails user = this.emailCodeUserDetailsService.loadUserByEmail(unAuthenticationToken);
            if (user == null) {
                throw new InternalAuthenticationServiceException("EmailCodeUserDetailsService returned null, which is an interface contract violation");
            }
            System.out.println("authentication successful!");
    
            Object principalToReturn = user;
            return createSuccessAuthentication(principalToReturn, authentication, user);
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
            return EmailCodeAuthenticationToken.class.isAssignableFrom(authentication);
        }
    
        protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
            EmailCodeAuthenticationToken result = new EmailCodeAuthenticationToken(principal,
                                                                                   authentication.getCredentials(), user.getAuthorities());
            result.setDetails(authentication.getDetails());
            return result;
        }
    
        public void setEmailCodeUserDetailsService(EmailCodeUserDetailsService emailCodeUserDetailsService) {
            this.emailCodeUserDetailsService = emailCodeUserDetailsService;
        }
    }
    

    說(shuō)明:

    Provider是真正做認(rèn)證的地方,這里調(diào)用emailCodeUserDetailsService服務(wù)去執(zhí)行驗(yàn)證榴都,因?yàn)橐玫竭@個(gè)Service待锈,所以提供了一個(gè)set方法setEmailCodeUserDetailsService用于注入。這里的這個(gè)service是我們自定義的嘴高,可以不用實(shí)現(xiàn)UserDetailsService竿音, Service里的邏輯可以自定義

  4. 自定義認(rèn)證成功與失敗的Handler

    public class EmailCodeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            response.setContentType("text/plain;charset=UTF-8");
            response.getWriter().write(authentication.getName());
        }
    }
    
    public class EmailCodeAuthenticationFailureHandler implements AuthenticationFailureHandler {
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            response.setContentType("text/plain;charset=UTF-8");
            response.getWriter().write("郵箱驗(yàn)證碼錯(cuò)誤!");
        }
    }
    

    說(shuō)明:

    這里的是認(rèn)證成功或失敗后的處理,需要實(shí)現(xiàn)對(duì)應(yīng)的接口以及方法拴驮。這里的邏輯只是簡(jiǎn)單測(cè)試春瞬,具體邏輯以后根據(jù)業(yè)務(wù)邏輯去編寫(xiě)。

  5. 添加自定義認(rèn)證的配置

    為了讓我們自定義的認(rèn)證生效套啤,需要將我們的Filter和Provider加入到SpringSecurity的配置中宽气。這里我們使用apply這個(gè)方法將其他一些配置合并到SpringSecurity的配置中,形成插件化。比如: httpSecurity.apply(new xxxxConfig());

    因此我們可以將我們的配置單獨(dú)放到一個(gè)配置類(lèi)中萄涯。

    public class EmailCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    
        // 注入email驗(yàn)證服務(wù)
        @Autowired
        private EmailCodeUserDetailsService emailCodeUserDetailsService;
    
        @Override
        public void configure(HttpSecurity http) {
            // 配置Filter
            EmailCodeAuthenticationFilter emailCodeAuthenticationFilter = new EmailCodeAuthenticationFilter();
            // 設(shè)置AuthenticationManager
            emailCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
            // 設(shè)置認(rèn)證成功處理Handler
            emailCodeAuthenticationFilter.setAuthenticationSuccessHandler(new EmailCodeAuthenticationSuccessHandler());
            // 設(shè)置認(rèn)證失敗處理Handler
            emailCodeAuthenticationFilter.setAuthenticationFailureHandler(new EmailCodeAuthenticationFailureHandler());
    
            // 配置Provider
            EmailCodeAuthenticationProvider emailCodeAuthenticationProvider = new EmailCodeAuthenticationProvider();
            // 設(shè)置email驗(yàn)證服務(wù)
            emailCodeAuthenticationProvider.setEmailCodeUserDetailsService(emailCodeUserDetailsService);
    
            // 將過(guò)濾器添加到過(guò)濾器鏈路中
            http.authenticationProvider(emailCodeAuthenticationProvider).addFilterAfter(emailCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        }
    }
    

    注意:

    這里需要注意的是绪氛,一定要將AuthenticationManager提供給Filter,如果沒(méi)有這一步涝影,那么在Filter中進(jìn)行認(rèn)證的時(shí)候無(wú)法找到對(duì)應(yīng)的Provider枣察,因?yàn)锳uthenticationManger就是管理Provider的。
    http.getSharedObject(AuthenticationManager.class)解釋?zhuān)?/p>

    SharedObject是在配置中進(jìn)行共享的一些對(duì)象燃逻,HttpSecurity共享了一些非常有用的對(duì)象可以供外部使用序目,比如AuthenticationManager

    最后在SpringSecurity的主配置中加入我們的自定義配置:

    @Configuration
    public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private EmailCodeAuthenticationSecurityConfig emailCodeAuthenticationSecurityConfig;
    
        @Autowired
        private DefaultUserDetailsService defaultUserDetailsService;
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        @Override
        protected AuthenticationManager authenticationManager() throws Exception {
            return super.authenticationManager();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(defaultUserDetailsService);
        }
    
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/getEmailCode", "/**/*.html");
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
                .and()
                .apply(emailCodeAuthenticationSecurityConfig)
                .and()
                .csrf()
                .disable();
        }
    }
    

    說(shuō)明:

    因?yàn)檫@里使用了數(shù)據(jù)庫(kù)保存用戶(hù)信息,所以在SpringSecurity的默認(rèn)表單登錄里唆樊,修改了UserDetailService宛琅,在這里進(jìn)行校驗(yàn),所以在主配置中要設(shè)置UserDetailService:auth.userDetailsService(defaultUserDetailsService);

  6. 其他一些文件

    查看我上傳的gitee源碼吧逗旁,整個(gè)工程都上傳了。

  7. 前端頁(yè)面實(shí)現(xiàn)

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>登錄</title>
    
            <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
            <link rel="stylesheet"  integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
            <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
            <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
            <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
    
            <style>
                body {
                    background-color: gray;
                }
                .login-div {
                    width: 400px;
                    /* height: 200px; */
                    margin: 0 auto;
                    margin-top: 200px;
                    border: 1px solid black;
                    padding: 10px;
                }
            </style>
        </head>
        <body>
            <div class="login-div">
                <ul class="nav nav-tabs" role="tablist">
                    <li class="active">
                        <a href="#usernameLogin"   data-toggle="tab">用戶(hù)名登錄</a>
                    </li>
                    <li>
                        <a href="#emailLogin" data-toggle="tab">郵箱驗(yàn)證碼登錄</a>
                    </li>
                </ul>
    
                <!-- 用戶(hù)名登錄 -->
                <div class="tab-content">
                    <div class="tab-pane active" id="usernameLogin">
                        <form action="/login" method="POST">
                            <div class="form-group">
                                <label>用戶(hù)名</label>
                                <input type="text" class="form-control" placeholder="Username" name="username">
                            </div>
                            <div class="form-group">
                                <label>密碼</label>
                                <input type="password" class="form-control"  placeholder="Password" name="password">
                            </div>
    
                            <div class="checkbox">
                                <label>
                                    <input type="checkbox" name="rememberType"> 記住我
                                </label>
                            </div>
                            <button type="submit" class="btn btn-default">登錄</button>
                        </form>
                    </div>
                    <!-- 郵箱登錄 -->
                    <div class="tab-pane" id="emailLogin">            
                        <form action="/email/login" method="POST">
                            <div class="form-group" >
                                <label>郵箱地址</label>
                                <input type="email" class="form-control" placeholder="Email" name="email" id="email">
                            </div>
                            <div class="form-group">
                                <label>驗(yàn)證碼</label>
                                <input type="text" class="form-control"  placeholder="Code" name="email_code">
                            </div>
    
                            <div class="form-group">
                                <label>
                                    <button type="button" class="btn btn-default" id="getCode">獲取驗(yàn)證碼</button>
                                    <span id="showCode" style="margin-left: 20px;"></span>
                                </label>
                            </div>
                            <button type="submit" class="btn btn-default">登錄</button>
                        </form>
                    </div>
                </div>
    
            </div>
    
            <script>
                $('#nav a').on('click', function(e) {
                    e.preventDefault();
                    $(this).tab('show');
                }); 
    
                $('#getCode').on('click', function() {
                    $.ajax({
                        type: "GET",
                        url: "/getEmailCode",
                        data: {
                            email: $('#email').val()
                        },
                        // dataType: "dataType",
                        success: function (response) {
                            $('#showCode').text(response);
                        }
                    });
                });
            </script>
        </body>
    </html>
    

    說(shuō)明:

    前端頁(yè)面只是簡(jiǎn)單的顯示使用兩種方式來(lái)登錄的操作舆瘪,一些輸入校驗(yàn)什么的沒(méi)有詳細(xì)實(shí)現(xiàn)片效,所以這里默認(rèn)各位大佬都是正常操作哈。

    這個(gè)前端支持兩種登錄方式英古,用戶(hù)名密碼登錄方式使用的SpringSecurity默認(rèn)的UsernamepasswordAuthenticationFilter淀衣,郵箱驗(yàn)證碼使用的是自定義的EmailCodeAuthenticationFilter,在郵箱登錄頁(yè)面召调,點(diǎn)擊獲取驗(yàn)證碼按鈕膨桥,會(huì)請(qǐng)求服務(wù)器獲取一個(gè)隨機(jī)的字符串作為驗(yàn)證碼,并且存入Redis中唠叛,有效期60s(記住我功能在這里沒(méi)有實(shí)現(xiàn))

    demo-emailLogin.jpg

    demo-usernameLogin.jpg
  8. 數(shù)據(jù)庫(kù)操作

    因?yàn)槟壳爸皇亲远x認(rèn)證只嚣,不涉及授權(quán),所以只有一個(gè)用戶(hù)表

    CREATE TABLE `user` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `username` VARCHAR(32) DEFAULT NULL,
      `password` VARCHAR(255) DEFAULT NULL,
      `email` VARCHAR(255) DEFAULT NULL,
      `enabled` TINYINT(1) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
    
    INSERT INTO `user` VALUES ('1', 'root', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq','123456@qq.com', '1');
    

    隨便插入一個(gè)用戶(hù)艺沼,密碼是123册舞,數(shù)據(jù)庫(kù)的是經(jīng)過(guò)加密的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末障般,一起剝皮案震驚了整個(gè)濱河市调鲸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挽荡,老刑警劉巖藐石,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異定拟,居然都是意外死亡于微,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)角雷,“玉大人祸穷,你說(shuō)我怎么就攤上這事∩兹” “怎么了雷滚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吗坚。 經(jīng)常有香客問(wèn)我祈远,道長(zhǎng),這世上最難降的妖魔是什么商源? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任车份,我火速辦了婚禮,結(jié)果婚禮上牡彻,老公的妹妹穿的比我還像新娘扫沼。我一直安慰自己,他們只是感情好庄吼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布缎除。 她就那樣靜靜地躺著,像睡著了一般总寻。 火紅的嫁衣襯著肌膚如雪器罐。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天渐行,我揣著相機(jī)與錄音轰坊,去河邊找鬼。 笑死祟印,一個(gè)胖子當(dāng)著我的面吹牛肴沫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播旁理,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼樊零,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了孽文?” 一聲冷哼從身側(cè)響起驻襟,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芋哭,沒(méi)想到半個(gè)月后沉衣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡减牺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年豌习,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了存谎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肥隆,死狀恐怖既荚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栋艳,我是刑警寧澤恰聘,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站吸占,受9級(jí)特大地震影響晴叨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矾屯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一兼蕊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧件蚕,春花似錦孙技、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至纽绍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間势似,已是汗流浹背拌夏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留履因,地道東北人障簿。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像栅迄,于是被迫代替她去往敵國(guó)和親站故。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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