shiro學習筆記

權(quán)限管理

為了實現(xiàn)對用戶訪問系統(tǒng)的控制,按照安全規(guī)則或安全策略控制用戶可以訪問且只能訪問自己被授權(quán)的資源纷宇。

用戶認證

為了驗證用戶訪問系統(tǒng)的合法性。

用戶授權(quán)

在用戶認證通過后,只能訪問被系統(tǒng)授權(quán)的資源招刨,授權(quán)過程可以理解為who對what(which)進行how操作

關(guān)鍵對象

  • subject:主體
    訪問系統(tǒng)資源的對象,權(quán)限管理需要對主體進行身份認證
  • principal:身份信息
    身份信息通常是唯一的哀军,一個主體可能會有多個身份信息计济,都有一個主身份信息(primary principal)
  • credential:憑證信息
    密碼、證書等排苍,主體在進行身份認證時需要身份信息和憑證信息
  • resource 資源
    必須具備相應權(quán)限才可以訪問的對象
  • permission 權(quán)限/許可
    主體需要相應權(quán)限才能訪問沦寂、操作相應資源

權(quán)限模型

  • 主體:賬戶、密碼
  • 角色:角色名稱
  • 權(quán)限:權(quán)限名稱淘衙、資源名稱传藏、資源訪問地址
  • 主體與角色的關(guān)系
  • 角色與權(quán)限的關(guān)系

權(quán)限控制

基于角色的訪問控制

RBAC(Role Based Access Control),基于角色的訪問控制

基于資源的訪問控制

RBAC(Resource Based Access Control)彤守,基于資源的訪問控制

權(quán)限粒度

  • 粗粒度權(quán)限管理:對資源類型的權(quán)限管理
  • 細粒度權(quán)限管理:對資源實例的權(quán)限管理

shiro架構(gòu)

shiro架構(gòu)
  • Subject:主體
  • SecurityManager:安全管理器毯侦,進行主體的認證和授權(quán)
  • Authenticator:用戶認證管理器
  • Authorizer:權(quán)限管理器
  • SessionManager:web應用中一般是用web容器對session管理,shiro也提供一套管理session的方式
  • SessionDao:對Session進行CRUD操作(可與redis集成管理session數(shù)據(jù))
  • CacheManager:緩存管理器具垫,主要對session和授權(quán)數(shù)據(jù)進行緩存
  • Cryptography:加密方式
  • Realm:存取認證侈离、授權(quán)相關(guān)數(shù)據(jù)(邏輯)
shiro緩存

當需要訪問受限資源時,會實時去查詢權(quán)限數(shù)據(jù)筝蚕,這樣的查詢是頻繁的卦碾,而權(quán)限信息又不是經(jīng)常變化的铺坞,所以需要配置緩存來提高性能。
緩存帶來的問題:當用戶不退出系統(tǒng)(正常退出洲胖、非正常退出)济榨,是不會清空緩存的,如果權(quán)限發(fā)生變更绿映,不能及時改變用戶所擁有的權(quán)限擒滑。

shiro會話

shiro支持通過SessionManager取代web容器來管理會話,可以通過配置SessionDao(對Session的CRUD)集成Reis集群來對session進行共享叉弦、更新丐一、刪除。

使用Spring集成Shiro

數(shù)據(jù)庫設計
DROP TABLE IF EXISTS users;
CREATE TABLE users (
  id       INT          NOT NULL AUTO_INCREMENT
  COMMENT '用戶編號',
  name     VARCHAR(255) NOT NULL
  COMMENT '用戶名稱',
  username VARCHAR(255) NOT NULL
  COMMENT '賬號',
  password VARCHAR(255) NOT NULL
  COMMENT '密碼',
  salt     VARCHAR(255) NOT NULL
  COMMENT '鹽',
  status   TINYINT      NOT NULL DEFAULT 1
  COMMENT '用戶狀態(tài) 0-無效淹冰,1-有效',
  PRIMARY KEY (id),
  UNIQUE KEY (username)
)
  ENGINE = INNODB
  DEFAULT CHARSET = utf8
  COMMENT = '用戶';

DROP TABLE IF EXISTS roles;
CREATE TABLE roles (
  id        INT          NOT NULL AUTO_INCREMENT
  COMMENT '角色編號',
  role_name VARCHAR(255) NOT NULL
  COMMENT '角色名稱',
  PRIMARY KEY (id)
)
  ENGINE = INNODB
  DEFAULT CHARSET = utf8
  COMMENT = '角色';

DROP TABLE IF EXISTS permission;
CREATE TABLE permission (
  id       INT          NOT NULL AUTO_INCREMENT
  COMMENT '權(quán)限編號',
  url      VARCHAR(255) NOT NULL
  COMMENT 'url地址',
  url_name VARCHAR(255) NOT NULL
  COMMENT 'url描述',
  perm     VARCHAR(255) NOT NULL
  COMMENT '權(quán)限標識符',
  PRIMARY KEY (id)
)
  ENGINE = INNODB
  DEFAULT CHARSET = utf8
  COMMENT = '權(quán)限';

DROP TABLE IF EXISTS user_roles;
CREATE TABLE user_roles (
  user_id INT NOT NULL
  COMMENT '用戶編號',
  role_id INT NOT NULL
  COMMENT '角色編號',
  PRIMARY KEY (user_id, role_id)
)
  ENGINE = INNODB
  DEFAULT CHARSET = utf8
  COMMENT = '用戶-角色';

DROP TABLE IF EXISTS role_permissions;
CREATE TABLE role_permissions (
  role_id       INT NOT NULL
  COMMENT '角色編號',
  permission_id INT NOT NULL
  COMMENT '權(quán)限編號',
  PRIMARY KEY (role_id, permission_id)
)
  ENGINE = INNODB
  DEFAULT CHARSET = utf8
  COMMENT = '角色-權(quán)限';
依賴

除了基本的Spring依賴库车,還需要shiro-spring、shiro-cache榄棵、aspectj凝颇。

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.11</version>
        </dependency>
spring-shiro配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- shiroFilter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!-- 登錄地址(登錄頁面地址,不攔截疹鳄,登錄失敗跳回該頁) -->
        <property name="loginUrl" value="/"/>
        <!-- 成功登錄跳轉(zhuǎn)地址 -->
        <property name="successUrl" value="/home"/>
        <!-- 自定義表單驗證filter配置 -->
        <property name="filters">
            <map>
                <entry key="authc" value-ref="authFormFilter" />
            </map>
        </property>
        <!-- 過濾器鏈定義拧略,由上往下順序執(zhí)行 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- 設置靜態(tài)資源匿名訪問 -->
                /resources/** = anon
                <!-- ajax登錄url,不攔截 -->
                /login = anon
                <!-- 配置登出url -->
                /logout = logout
                <!-- 此處可以配置權(quán)限瘪弓,也可在類或方法上標注
                /home = authc
                /query = perms[/query]
                /add = perms[/add]
                /update = perms[/update]
                /delete = perms[/delete]
                -->
            </value>
        </property>
    </bean>

    <!-- securityManager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="authRealm"/>
        <property name="cacheManager" ref="cacheManager"/>
        <property name="sessionManager" ref="sessionManager"/>
    </bean>

    <!-- 配置realm垫蛆,用于認證、授權(quán) -->
    <bean id="systemRealm" class="com.wch.ssm.shiro.realm.SystemRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!-- 配置憑證匹配器腺怯,加密方式和hash次數(shù) -->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="1"/>
    </bean>

    <!-- cacheManager -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:config/shiro-ehcache.xml"/>
    </bean>

    <!-- sessionManager -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- 設置session的失效時長 -->
        <property name="globalSessionTimeout" value="600000"/>
        <!-- 刪除失效的session -->
        <property name="deleteInvalidSessions" value="true"/>
    </bean>

    <!-- 配置自定義表單驗證過濾器 -->
    <bean id="authFormFilter" class="com.wch.ssm.shiro.AuthFormFilter"/>

</beans>
自定義Realm
/**
 * 自定義Realm袱饭,用于認證和授權(quán)
 */
public class AuthRealm extends AuthorizingRealm {

    @Resource
    private SecurityService securityService;

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthRealm.class);

    /**
     * 認證
     *
     * @param token token
     * @return AuthenticationInfo
     * @throws AuthenticationException AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        User user = securityService.getPasswordAndSalt(username);

        if (null == user) {
            throw new UnknownAccountException("不存在該賬戶!");
        }

        String name = user.getName();
        String password = user.getPassword();
        String salt = user.getSalt();
        if (null == name || null == password || null == salt) {
            throw new AccountException("賬戶異常呛占!");
        }

        // 身份信息虑乖,密碼(數(shù)據(jù)庫中加密后的密碼),salt晾虑,realmName
        return new SimpleAuthenticationInfo(user, password, ByteSource.Util.bytes(salt), this.getName());
    }

    /**
     * 授權(quán)
     *
     * @param principals principals
     * @return AuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = null;
        try {
            // 獲取身份信息
            User user = (User) principals.getPrimaryPrincipal();
            // 查詢權(quán)限信息
            Set<String> permissions = securityService.getStringPermissions(user.getId());
            info = new SimpleAuthorizationInfo();
            info.addStringPermissions(permissions);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return info;
    }

    /**
     * 用戶權(quán)限發(fā)生變動疹味,調(diào)用此方法清除緩存
     */
    public void clearCache() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }
}
控制器
    /**
     * 驗證登錄
     *
     * @return json data
     * @throws ShiroException ShiroException
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public @ResponseBody
    Result login(String username, String password) {
        try {
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // 登錄失敗:包括賬戶不存在帜篇、密碼錯誤等糙捺,都會拋出ShiroException
            SecurityUtils.getSubject().login(token);
            return Result.response(ResultEnum.SUCCESS);
        } catch (ShiroException e) {
            LOGGER.error("登錄失敗,{}笙隙,{}", e.getClass().getName(), e.getMessage());
            return Result.response(ResultEnum.FAIL);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            return Result.response(ResultEnum.FAIL);
        }
    }

    /**
     * successUrl
     * 使用注解 @RequiresAuthentication 來標注該訪問該url需要認證
     *
     * @param model model
     * @return Page
     */
    @RequestMapping("/home")
    @RequiresAuthentication
    public String home(Model model) {
        // 獲取在身份認證時放入的身份信息
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("name", user.getName());
        return "home";
    }

    /**
     * unauthorizedUrl洪灯,未授權(quán)時跳轉(zhuǎn)該url
     *
     * @return json
     */
    @ExceptionHandler(UnauthorizedException.class)
    @RequiresAuthentication
    public @ResponseBody
    String forbidden() {
        return "403";
    }

    /**
     * 使用 @RequiresPermissions 注解來標注訪問該url需要 "user:query" 權(quán)限
     *
     * @return json
     */
    @RequestMapping("/query")
    @RequiresPermissions("user:query")
    public @ResponseBody
    String query() {
        return "permit query.";
    }
登錄交互
<script type="text/javascript">
    $('#submit').click(function () {
        $.ajax({
            url: 'login',
            type: 'POST',
            data: {
                username: $('#username').val().trim(),
                password: $('#password').val().trim()
            },
            success: function (res) {
                if (res.code === 200) {
                    window.location.href = 'home'
                } else {
                    alert("Login Failed!");
                }
            }
        });
    });
</script>

使用SpringBoot集成shiro

配置ShiroConfig

對于需要配置權(quán)限的url,每個都配置注解是很不方便的竟痰,可以通過應用啟動時查詢持久化到數(shù)據(jù)庫中的權(quán)限配置來生成攔截器鏈签钩。
ShiroConfig加載到容器中時掏呼,查詢權(quán)限的Service可能還未注入,導致空指針異常边臼。因此在ShiroConfig中應使用手動注入的方式來獲取查詢權(quán)限Service哄尔。

獲取ApplicationContext

為了獲取ApplicationContext假消,ShiroConfig需要實現(xiàn)ApplicationContextAware接口柠并,實現(xiàn)setApplicationContext()方法。

    private ApplicationContext context;

    /**
     * 獲取ApplicationContext
     *
     * @param applicationContext applicationContext
     * @throws BeansException BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
適配權(quán)限標識

在自定義Realm中重寫的doGetAuthorizationInfo()方法富拗,返回類型SimpleAuthorizationInfo臼予,添加權(quán)限的方式是通過ddStringPermissions(Collection<String> permissions)添加權(quán)限的字符串形式,例如sys:add啃沪,但是在攔截器鏈中配置權(quán)限的要求是perms[sys:add]的形式粘拾,因此需要對權(quán)限標識進行適配。

     /**
     * 適配攔截器權(quán)限標識符
     *
     * @param perm perm
     * @return perms[]
     */
    private String adaptPerms(String perm) {
        StringBuilder sb = new StringBuilder();
        sb.append("perms[").append(perm).append("]");
        return sb.toString();
    }
配置攔截器鏈
    // 攔截器鏈创千,由上到下順序執(zhí)行
    Map<String, String> filterChain = new LinkedHashMap<>();

    // 動態(tài)添加權(quán)限
    SecurityService securityService = null;
    while (securityService == null) {
        securityService = (SecurityService) context.getBean("securityServiceImpl");
    }
    List<Permission> permissions = securityService.getPermissions();
    for (Permission permission : permissions) {
      filterChain.put(permission.getUrl(), this.adaptPerms(permission.getPerm()));
    }
完整配置
@Configuration
public class ShiroConfig implements ApplicationContextAware {

    private ApplicationContext context;

    /**
     * 配置realm缰雇,用于認證、授權(quán)
     *
     * @return Realm
     */
    @Bean
    public Realm authRealm() {
        // 憑證匹配器追驴,配置加密方式和hash次數(shù)
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(CommonConstants.HASH_CREDENTIAL_NAME);
        credentialsMatcher.setHashIterations(CommonConstants.HASH_ITERATIONS);

        AuthRealm authRealm = new AuthRealm();
        authRealm.setCredentialsMatcher(credentialsMatcher);
        return authRealm;
    }

    /**
     * 配置EhCache緩存管理器械哟,用于授權(quán)信息緩存
     *
     * @return CacheManager
     */
    private CacheManager getEhCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:config/shiro-ehcache.xml");
        return cacheManager;
    }

    /**
     * 配置SecurityManager
     *
     * @return SecurityManager
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(authRealm());
        securityManager.setCacheManager(getEhCacheManager());
        return securityManager;
    }

    /**
     * 設置由servlet容器管理filter生命周期
     *
     * @return LifecycleBeanPostProcessor
     */
    @Bean
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 開啟aop,對類代理
     *
     * @return Proxy
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 開啟shiro注解支持
     *
     * @return Advisor
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager());
        return advisor;
    }

    /**
     * 配置shiroFilter殿雪,beanName必須為shiroFilter
     *
     * @return ShiroFilter
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        // 配置SecurityManager
        filter.setSecurityManager(securityManager());
        // 配置登錄頁
        filter.setLoginUrl("/");
        // 登錄成功跳轉(zhuǎn)鏈接
        filter.setSuccessUrl("/sys");
        // 未授權(quán)界面
        filter.setUnauthorizedUrl("/403");
        // 攔截器鏈暇咆,由上到下順序執(zhí)行
        Map<String, String> filterChain = new LinkedHashMap<>();
        // 配置ajax登錄url匿名訪問
        filterChain.put("/login", "anon");
        // 配置登出路徑
        filterChain.put("/logout", "logout");
        // 靜態(tài)資源處理
        filterChain.put("/js/**", "anon");
        filterChain.put("/css/**", "anon");
        filterChain.put("/img/**", "anon");

        // 動態(tài)添加權(quán)限
        SecurityService securityService = null;
        while (securityService == null) {
            securityService = (SecurityService) context.getBean("securityServiceImpl");
        }
        List<Permission> permissions = securityService.getPermissions();
        for (Permission permission : permissions) {
            filterChain.put(permission.getUrl(), this.adaptPerms(permission.getPerm()));
        }

        // 認證后訪問
        filterChain.put("/**", "authc");
        filter.setFilterChainDefinitionMap(filterChain);
        return filter;
    }

    /**
     * 適配攔截器權(quán)限標識符
     *
     * @param perm perm
     * @return perms[]
     */
    private String adaptPerms(String perm) {
        StringBuilder sb = new StringBuilder();
        sb.append("perms[").append(perm).append("]");
        return sb.toString();
    }

    /**
     * 獲取ApplicationContext
     *
     * @param applicationContext applicationContext
     * @throws BeansException BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市丙曙,隨后出現(xiàn)的幾起案子爸业,更是在濱河造成了極大的恐慌,老刑警劉巖亏镰,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扯旷,死亡現(xiàn)場離奇詭異,居然都是意外死亡索抓,警方通過查閱死者的電腦和手機钧忽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纸兔,“玉大人惰瓜,你說我怎么就攤上這事『嚎螅” “怎么了崎坊?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長洲拇。 經(jīng)常有香客問我奈揍,道長曲尸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任男翰,我火速辦了婚禮另患,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蛾绎。我一直安慰自己昆箕,他們只是感情好,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布租冠。 她就那樣靜靜地躺著鹏倘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪顽爹。 梳的紋絲不亂的頭發(fā)上纤泵,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音镜粤,去河邊找鬼捏题。 笑死,一個胖子當著我的面吹牛肉渴,可吹牛的內(nèi)容都是我干的公荧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼黄虱,長吁一口氣:“原來是場噩夢啊……” “哼稚矿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起捻浦,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤晤揣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后朱灿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昧识,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年盗扒,在試婚紗的時候發(fā)現(xiàn)自己被綠了跪楞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡侣灶,死狀恐怖甸祭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情褥影,我是刑警寧澤池户,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響校焦,放射性物質(zhì)發(fā)生泄漏赊抖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一寨典、第九天 我趴在偏房一處隱蔽的房頂上張望氛雪。 院中可真熱鬧,春花似錦耸成、人聲如沸报亩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捆昏。三九已至赚楚,卻和暖如春毙沾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宠页。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工左胞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人举户。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓烤宙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親俭嘁。 傳聞我的和親對象是個殘疾皇子躺枕,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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

  • 構(gòu)建一個互聯(lián)網(wǎng)應用,權(quán)限校驗管理是很重要的安全措施供填,這其中主要包含: 認證 - 用戶身份識別拐云,即登錄 授權(quán) - 訪...
    zhuke閱讀 3,505評論 0 30
  • 1.簡介 Apache Shiro是Java的一個安全框架。功能強大近她,使用簡單的Java安全框架叉瘩,它為開發(fā)人員提供...
    H_Man閱讀 3,169評論 4 48
  • 前言 Spring boot 是什么,網(wǎng)上的很多介紹粘捎,這里博客就不多介紹了薇缅。如果不明白Spring boot是什么...
    xuezhijian閱讀 17,908評論 13 39
  • 柳哲 我研究家譜二十余年,收藏家譜不計其數(shù)攒磨。和圣柳下惠后裔世代傳承2700多年的《展氏族譜》泳桦,尤為罕見,極其珍貴娩缰。...
    柳志儒閱讀 245評論 0 0
  • 捆綁你的是愛嗎灸撰? 我一直覺得每個人都是欣欣向榮的,只要是內(nèi)心渴望光明,就不會被黑暗罩住梧奢,心里的渴望越大狱掂,眼神就會越...
    笑不二閱讀 479評論 3 1