Shiro在前后臺分離架構(gòu)項目中的應(yīng)用

Shiro是Apache的強大靈活的開源安全框架

能提供認證侨糟、授權(quán)碍扔、企業(yè)會話管理、安全加密秕重、緩存等功能不同。

與Spring Security的比較

Apache Shiro Spring Security
簡單靈活 復(fù)雜、笨重
可脫離Spring 必須依賴Spring
粒度較粗 粒度更細

Shiro的幾個關(guān)鍵要素

  • Subject

    主體(官方解釋,不明白為毛要命名為主體二拐,一眼看到這么個東西讓人很難理解)服鹅,其實很簡單,Subject就是應(yīng)用和Shiro管理器交流的橋梁百新,基本上所有對權(quán)限的操作都是通過Subject進行的企软,比如登錄,比如注銷饭望,Subject就可以看成是Shiro里的用戶仗哨。

  • SecurityManager

    安全管理器,所有與安全相關(guān)的操作都會由SecurityManager來處理铅辞,而且厌漂,通過查看源碼可以看到,Subject的所有操作都是借助于SecurityManager來完成的斟珊,它是Shiro的核心桩卵。

  • Realm

    域(這個概念也是比較抽象的),可以有一個或多個倍宾,Shiro中所有的安全驗證數(shù)據(jù)都是由Realm提供的雏节,而且Shiro不知道應(yīng)用的權(quán)限存儲以何種方式存儲,所以我們一般都需要實現(xiàn)自己的Realm高职;可以這樣看钩乍,Subject提供驗證數(shù)據(jù)入口,Realm提供驗證的數(shù)據(jù)源怔锌,而真正的驗證功能由Shiro的認證器來完成寥粹。

  • Authenticator

    認證器,負責主體認證的埃元,即認證器都用來實現(xiàn)用戶在什么情況下算是認證通過了涝涤。

  • Authrizer

    授權(quán)器,或者訪問控制器岛杀,用來對主體(Subject)進行授權(quán)阔拳,覺得主體有哪些操作的權(quán)限,能訪問應(yīng)用中的那些功能类嗤。

  • SessionManager

    Session管理器糊肠,但是這個地方的Session與當初學習Servlet時接觸到的Session基本類似,但是這個Session是由Shiro自己去維護的遗锣,與Web環(huán)境無關(guān)货裹,可以應(yīng)用到Web環(huán)境中,也可以應(yīng)用到普通的JavaSE環(huán)境精偿。

  • SessionDAO

    數(shù)據(jù)訪問對象弧圆,用于會話的CRUD赋兵,比如將Session存儲到Redis,或者數(shù)據(jù)庫搔预,或者內(nèi)存毡惜,都可以通過SessionDAO來實現(xiàn),可以使用默認的SessionDAO斯撮,也可以自定義實現(xiàn)经伙。

  • CacheManager

    緩存控制器,用來管理用戶勿锅、角色帕膜、權(quán)限等的緩存。

  • Cryptography

    密碼模塊溢十,Shiro提供了一些常見的加密組件用于密碼加密/解密垮刹。

Shiro內(nèi)置的過濾器

  • anon,authBasic,authc,user,logout
  • perms,roles,ssl,port
過濾器簡稱過濾器簡稱 對應(yīng)的java類
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter
logout org.apache.shiro.web.filter.authc.LogoutFilter

Shiro在前后臺分離架構(gòu)的項目中的應(yīng)用

Shiro在傳統(tǒng)web項目中的應(yīng)用與前后臺分離項目中的區(qū)別

傳統(tǒng)項目中,前后臺在一個工程里张弛,頁面的跳轉(zhuǎn)荒典,請求的訪問,一般都是由后臺來控制吞鸭,中間不需要做太多的轉(zhuǎn)換寺董。

而在前后臺分離項目中,前后臺在不同的工程里刻剥,也在不同的服務(wù)器上遮咖,頁面的跳轉(zhuǎn)由前端路由來控制(其實也沒啥頁面的跳轉(zhuǎn),隨著前端框架如雨后竹筍一般的冒出來造虏,前端應(yīng)用都往單頁面應(yīng)用的方向發(fā)展)御吞,后臺只負責提供數(shù)據(jù)以及安全驗證,對于頁面的東西后臺已經(jīng)不做關(guān)注漓藕。在這種情況下陶珠,在使用Shiro時就需要有一些自定義的東西了。

需要關(guān)注的幾個點

  • 通過Redis存儲Session
  • 由Shiro來跳轉(zhuǎn)的請求地址
  • 配置不需要驗證的請求接口

具體實現(xiàn)

作為一個SpringBoot洗腦流享钞,不管是什么新東西揍诽,最先想到的就是通過SpringBoot來集成。這里通過SpringBoot嫩与,集成Shiro寝姿、Swagger(模擬前臺通過JSON請求后臺)、Redis(暫時只存儲Session)划滋,使用Swagger來模擬請求,測試Shiro的權(quán)限控制埃篓。

以下的集成相關(guān)東西处坪,都是建立于一個完整的SpringBoot Demo。

  • 集成Redis

    引入Redis依賴

    <!-- Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    

    引入第三方Redis序列化工具

    <!-- 高效的序列化庫kyro -->
    <dependency>
        <groupId>com.esotericsoftware</groupId>
        <artifactId>kryo-shaded</artifactId>
        <version>4.0.0</version>
    </dependency>
    

    注: Kryo是一個快速高效的Java序列化框架,旨在提供快速同窘、高效和易用的API玄帕。無論文件、數(shù)據(jù)庫或網(wǎng)絡(luò)數(shù)據(jù)Kryo都可以隨時完成序列化裤纹。Kryo還可以執(zhí)行自動深拷貝(克隆)、淺拷貝(克录榛恪)擂找。這是對象到對象的直接拷貝浩销,非對象->字節(jié)->對象的拷貝撼嗓。在后面的文章會分析一下Redis各種序列化方式的效率粉捻。

    配置Redis連接(為了方便測試杏头,使用Redis單機版即可)

    spring:
      redis:
        database: 0
        host: localhost
        password:  # Redis服務(wù)器若設(shè)置密碼,此處必須配置
        port: 6379
        timeout: 10000 # 連接超時時間(毫秒)
        pool:
          max-active: 8 # 連接池最大連接數(shù)(使用負數(shù)表示沒有限制)
          max-idle: 8 # 連接池中的最大空閑連接
          min-idle: 0 # 連接池中的最小空閑連接
          max-wait: -1 # 連接池最大阻塞等待時間(使用負數(shù)表示沒有限制)
    
  • Swagger的集成

    為了不重復(fù)造輪子醇王,使用swagger-spring-boot-starter(一個大牛自己針對Swagger封裝的一個SpringBoot的Starter自動配置模塊)即可。

    <!-- swagger API集成 -->
    <dependency>
        <groupId>com.spring4all</groupId>
        <artifactId>swagger-spring-boot-starter</artifactId>
        <version>1.7.1.RELEASE</version>
    </dependency>
    

    在使用Shiro之后仁连,由于默認情況下饭冬,資源都會被Shiro攔截并徘,所以需要對Swagger的資源手動做加載,并使用@EnableSwagger2Doc打開Swagger自動配置姐直,并且在下面shiro攔截器配置時,將swagger相關(guān)資源配置為anno才睹。

    @Configuration
    @EnableSwagger2Doc
    public class SwaggerConfiguration extends WebMvcConfigurerAdapter {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
            registry.addResourceHandler("swagger-ui.html")
                    .addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**")
                    .addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
    }
    

    配置Swagger

    swagger:
      title: 測試Demo
      description: 測試Demo
      version: 1.0.RELEASE
      license: Apache License, Version 2.0
      license-url: https://www.apache.org/licenses/LICENSE-2.0.html
      terms-of-service-url: https://github.com/dyc87112/spring-boot-starter-swagger
      base-package: com.example
      base-path: /**
      exclude-path: /error, /ops/**
    
  • Shiro集成

    引入Shiro官方提供的與Spring類項目集成的依賴包

    <!-- shiro begin -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>${shiro.version}</version>
    </dependency>
    <!-- shiro ehcache -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-ehcache</artifactId>
        <version>${shiro.version}</version>
    </dependency>
    

    除了上面這兩個依賴包之外,以便于以后項目做集群坞琴,使用Redis存儲Shiro的安全驗證信息哨查,所以在Github上翻了翻,找到了下面shiro-redis包置济,它很好的完成了Redis與Shiro的集成解恰,不需要開發(fā)人員自己去編碼锋八,實現(xiàn)Shiro的SessionDAO接口浙于。

    <!-- shiro與Redis整合的開源插件 -->
    <dependency>
        <groupId>org.crazycake</groupId>
        <artifactId>shiro-redis</artifactId>
        <version>3.0.0</version>
    </dependency>
    

    還沒完护盈,Shiro的常規(guī)配置還需要通過JavaConfig的方式去配置(以SpringBoot自動配置的方式實現(xiàn)),廢話少說羞酗,下面代碼見真章腐宋。

    shiro的相關(guān)攔截規(guī)則配置

    security:
      shiro:
        filter:
          anon:   # 不需要Shiro攔截的請求URL
            - /api/v1/**  # swagger接口文檔
            - /swagger-ui.html
            - /webjars/**
            - /swagger-resources/**
            - /user/login   # 登錄接口
            - /user/noLogin   # 未登錄提示信息接口
          authc:   # 需要Shiro攔截的請求URL
            - /**
        loginUrl: /user/login   # 登錄接口
        noAccessUrl: /user/noLogin   # 未登錄時跳轉(zhuǎn)URL
        globalSessionTimeout: 30  # 登錄過期時長
    

    自定義的Shiro屬性配置類ShiroProperties.java

    @Data
    @ConfigurationProperties(prefix = "security.shiro")
    public class ShiroProperties {
        /**
         * 登錄Url
         */
        private String loginUrl;
        /**
         * 沒權(quán)限訪問時的轉(zhuǎn)發(fā)Url(做未登錄提示信息用)
         */
        private String noAccessUrl;
        /**
         * Shiro請求攔截規(guī)則配置(Shiro的攔截器規(guī)則,常用的anon和authc)
         */
        private Map<String, List<String>> filter;
        /**
         * Shiro Session 過期時間(分鐘)
         */
        private Long globalSessionTimeout = 30L;
    }
    

    為解決前后臺分離架構(gòu)的項目下檀轨,未登錄時訪問系統(tǒng)的跳轉(zhuǎn)及對應(yīng)的提示信息Shiro原有邏輯為未登錄則跳轉(zhuǎn)到登錄Url胸竞,在前后臺分離架構(gòu)下,此種方式顯然不能滿足要求参萄,只能修改authc默認過濾器處理流程卫枝,通過將請求轉(zhuǎn)發(fā)到一個新的Url,給出未登錄提示信息讹挎,由前臺去控制路由跳轉(zhuǎn)到登錄頁面

    @Slf4j
    public class SelfDefinedFormAuthenticationFilter extends FormAuthenticationFilter {
        // 沒有權(quán)限訪問的提示信息跳轉(zhuǎn)URL
        private String noAccessUrl;
        public String getNoAccessUrl() {
            return noAccessUrl;
        }
        public SelfDefinedFormAuthenticationFilter setNoAccessUrl(String noAccessUrl) {
            this.noAccessUrl = noAccessUrl;
            return this;
        }
        // 重寫跳轉(zhuǎn)到登錄URL的邏輯校赤,改為轉(zhuǎn)發(fā)到未登錄URL
        @Override
        protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
            String noAccessUrl = getNoAccessUrl();
            try {
                request.getRequestDispatcher(noAccessUrl).forward(request, response);
            } catch (ServletException e) {
                e.getMessage();
            }
        }
    }
    

    自定義Realm,提供登錄驗證數(shù)據(jù)及授權(quán)邏輯

    @Slf4j
    @Component
    public class SelfDefinedShiroRealm extends AuthorizingRealm {
        /**
         * 授權(quán)
         * @param principals
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            return authorizationInfo;
        }
        /**
         * 認證
         * @param token
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
                throws AuthenticationException {
            String username = (String) token.getPrincipal();
            log.info(username);
            SimpleAuthenticationInfo authorizationInfo = new SimpleAuthenticationInfo(
                    new User(username, "123"),
                    username,
                    getName()
            );
            return authorizationInfo;
        }
    }
    

    新建配置類筒溃,配置Shiro相關(guān)配置马篮。

    @Configuration
    @EnableConfigurationProperties(ShiroProperties.class)
    public class ShiroConfiguration {
        @Autowired
        private RedisProperties redisProperties;
        @Autowired
        private ShiroProperties shiroProperties;
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //獲取filters
            Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
            //將自定義 的FormAuthenticationFilter注入shiroFilter中
            filters.put("authc", new SelfDefinedFormAuthenticationFilter().
                    setNoAccessUrl(shiroProperties.getNoAccessUrl()));
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
            //注意過濾器配置順序 不能顛倒
            Map<String, List<String>> filterMap = shiroProperties.getFilter();
            filterMap.forEach((filter, urls) -> {
                urls.forEach(url -> {
                    filterChainDefinitionMap.put(url, filter);
                });
            });
            // 配置shiro默認登錄界面地址,前后端分離中登錄界面跳轉(zhuǎn)應(yīng)由前端路由控制怜奖,后臺僅返回json數(shù)據(jù)
    shiroFilterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl());
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
        /**
         * 憑證匹配器(密碼需要加密時浑测,可使用)
         * @return
         */
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher() {
            HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
            // 設(shè)置加密算法 Md5Hash
            hashedCredentialsMatcher.setHashAlgorithmName("md5");
            // 設(shè)置散列加密次數(shù) 如:2=md5(md5(aaa))
            hashedCredentialsMatcher.setHashIterations(2);
            return hashedCredentialsMatcher;
        }
        @Bean
        public SecurityManager securityManager(
                AuthorizingRealm authorizingRealm,
                SessionManager sessionManager,
                RedisCacheManager redisCacheManager) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(authorizingRealm);
            // 自定義的Session管理
            securityManager.setSessionManager(sessionManager);
            // 自定義的緩存實現(xiàn)
            securityManager.setCacheManager(redisCacheManager);
            return securityManager;
        }
        /**
         * 自定義的SessionManager
         * @param redisSessionDAO
         * @return
         */
        @Bean
        public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
            SelfDefinedSessionManager sessionManager = new SelfDefinedSessionManager();
            sessionManager.setSessionDAO(redisSessionDAO);                sessionManager.setGlobalSessionTimeout(shiroProperties.getGlobalSessionTimeout() * 60 * 1000);
            return sessionManager;
        }
        /**
         * 配置shiro redisManager
         * 使用的是shiro-redis開源插件
         * @return
         */
        @Bean
        public RedisManager redisManager() {
            RedisManager redisManager = new RedisManager();
            redisManager.setHost(redisProperties.getHost());
            redisManager.setPort(redisProperties.getPort());
            redisManager.setTimeout(redisProperties.getTimeout());
            if (!ObjectUtils.isEmpty(redisProperties.getPassword())) {
                redisManager.setPassword(redisProperties.getPassword());
            }
            return redisManager;
        }
        /**
         * cacheManager 緩存 redis實現(xiàn)
         * 使用的是shiro-redis開源插件
         * @param redisManager
         * @return
         */
        @Bean
        public RedisCacheManager redisCacheManager(RedisManager redisManager) {
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setRedisManager(redisManager);
            redisCacheManager.setValueSerializer(new StringSerializer());
            return redisCacheManager;
        }
        /**
         * RedisSessionDAO shiro sessionDao層的實現(xiàn) redis實現(xiàn)
         * 使用的是shiro-redis開源插件
         * @param redisManager
         * @return
         */
        @Bean
        public RedisSessionDAO redisSessionDAO(RedisManager redisManager) {
            RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
            redisSessionDAO.setRedisManager(redisManager);
            return redisSessionDAO;
        }
        /**
         * 開啟shiro aop注解支持
         * @param securityManager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                    new AuthorizationAttributeSourceAdvisor();
          authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    }
    
  • 編寫簡單的Controller,測試一下

    UserController.java

      @Autowired
        private RedisSessionDAO redisSessionDAO;
        @ApiOperation("登錄")
        @PostMapping("/login")
        public Object login(@RequestBody User user) {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());
            try {
                // 登錄
                subject.login(token);
                // 登錄成功后歪玲,獲取菜單權(quán)限信息
                if (subject.isAuthenticated()) {
                    return "登錄成功";
                }
            } catch (IncorrectCredentialsException e) {
                return "密碼錯誤";
            } catch (LockedAccountException e) {
                return "登錄失敗迁央,該用戶已被凍結(jié)";
            } catch (AuthenticationException e) {
                return "該用戶不存在";
            } catch (Exception e) {
                return e.getMessage();
            }
            return "登錄失敗";
        }
        @ApiOperation("注銷")
        @PostMapping("/logout")
        public Object logout() {
            Subject subject = SecurityUtils.getSubject();
            redisSessionDAO.delete(subject.getSession());
            return "注銷成功";
        }
        @ApiOperation("未登錄提示信息接口")
        @RequestMapping("/noLogin")
        public Object noLogin() {
            return "未登錄,請先登錄再訪問";
        }
        @ApiOperation("需登錄才能訪問")
        @PostMapping("/home")
        public Object home() {
            return "這是主頁";
        }
    

    訪問http://localhost:8080/shiro/swagger-ui.html頁面滥崩,通過Swagger測試請求的攔截岖圈。

    1. 未登錄訪問/user/home

      返回信息“未登錄,請先登錄再訪問”夭委,代表請求成功攔截到了幅狮,未登錄不能正常訪問系統(tǒng)

    2. 訪問/user/login進行登錄,然后訪問/user/home

      入?yún)?

      {
          "userName":"admin",
          "password":"123"
      }
      

      出參:

      "登錄成功"
      

      然后訪問/user/home株灸,成功返回"這是主頁"

    3. 注銷后在訪問/user/home

      直接請求/user/logout崇摄,訪問/user/home,提示“未登錄慌烧,請先登錄再訪問”逐抑,表示成功注銷。

    注: /user/noLogin使用的是@RequestMapping("/noLogin")屹蚊,是為了保證所有請求方式(GET/POST/PUT/DELETE等)的未登錄請求都能轉(zhuǎn)發(fā)到此接口厕氨,從而正確返回未登錄提示信息进每。

以上相關(guān)源碼,請訪問https://github.com/ArtIsLong/shiro-spring-boot-starter.git


關(guān)注我的微信公眾號:FramePower
我會不定期發(fā)布相關(guān)技術(shù)積累命斧,歡迎對技術(shù)有追求田晚、志同道合的朋友加入,一起學習成長国葬!


微信公眾號
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贤徒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子汇四,更是在濱河造成了極大的恐慌接奈,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件通孽,死亡現(xiàn)場離奇詭異序宦,居然都是意外死亡,警方通過查閱死者的電腦和手機背苦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門互捌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人糠惫,你說我怎么就攤上這事疫剃。” “怎么了硼讽?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵巢价,是天一觀的道長。 經(jīng)常有香客問我固阁,道長壤躲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任备燃,我火速辦了婚禮碉克,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘并齐。我一直安慰自己漏麦,他們只是感情好,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布况褪。 她就那樣靜靜地躺著撕贞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪测垛。 梳的紋絲不亂的頭發(fā)上捏膨,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音,去河邊找鬼号涯。 笑死目胡,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的链快。 我是一名探鬼主播誉己,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼久又!你這毒婦竟也來了巫延?” 一聲冷哼從身側(cè)響起效五,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤地消,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后畏妖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脉执,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年戒劫,在試婚紗的時候發(fā)現(xiàn)自己被綠了半夷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡迅细,死狀恐怖巫橄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情茵典,我是刑警寧澤湘换,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站统阿,受9級特大地震影響彩倚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扶平,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一帆离、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧结澄,春花似錦哥谷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赎瑰,卻和暖如春王悍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背餐曼。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工压储, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鲜漩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓集惋,卻偏偏與公主長得像孕似,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子刮刑,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

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

  • doGetAuthorizationInfo和doGetAuthenticationInfo 這兩個方法雖然名字很...
    Kenny_Yu閱讀 1,445評論 1 1
  • 文章轉(zhuǎn)載自:http://blog.csdn.net/w1196726224/article/details/53...
    wangzaiplus閱讀 3,391評論 0 3
  • 少年嫩枝丫喉祭, 逢春木葉發(fā)。 四歷風云變雷绢, 摧折茁壯拔泛烙。
    村客閱讀 206評論 6 17
  • 這段時間的記錄總是斷斷續(xù)續(xù),其實并沒有多長時間在堅持翘紊,真是慚愧蔽氨。 前段時間每天要照顧群,要工作帆疟,還要花費很多時間跟...
    暖暖滴文子閱讀 268評論 2 3
  • 閑窗燈影雪徘徊鹉究, 人事音書漫往來。 多岐歲暮無常至踪宠, 枯樹朝待瓊花開自赔。
    無羈無邪閱讀 105評論 0 0