一步一步教你用shiro——2配置并自定義realm

shiro中realm的是進(jìn)行認(rèn)證和授權(quán)的組件堵腹,自帶了幾種實現(xiàn)答憔,比如jdbcRealm和iniRealm,實際項目中肯定都是自己實現(xiàn)realm

  • 首先需要建立用戶表霉晕,存放用戶名忌堂、密碼雁比、權(quán)限信息
CREATE TABLE IF NOT EXISTS `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `user_name` varchar(10) NOT NULL DEFAULT '' COMMENT '用戶名',
  `password` varchar(50) NOT NULL DEFAULT '' COMMENT '用戶密碼盈魁,用戶名為鹽愿汰,五次md5',
  `roles` varchar(20) NOT NULL DEFAULT '' COMMENT '角色名,逗號分隔',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_idx_role_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶表';
  • 實現(xiàn)dao增刪改查(使用的mybatis代理dao)枕屉,封一個service實際操作user表
@Service
public class UserService {
    //mybatis代理實現(xiàn)的dao
    @Resource
    private UserDao userDao;

    //根據(jù)用戶名獲得user對象
    public User queryUserByName(String name) {
        try {
            if (StringUtils.isBlank(name)) {
                return null;
            }
            return userDao.queryUserByName(name);
        } catch (Exception e) {
            log.error("db error when query user:{}", name, e);
        }
        return null;
    }
    
    //根據(jù)用戶名獲得用戶的所有角色
    public Set<String> queryUserRole(String userName) {
        User user = queryUserByName(userName);
        if (user == null) {
            return Collections.emptySet();
        }
        List<String> roleList = StringAssist.splitComma(user.getRoles());
        return Sets.newHashSet(roleList);
    }
}
  • 自定義MyRealm繼承AuthorizingRealm常柄,分別實現(xiàn)認(rèn)證和授權(quán)的方法
  • doGetAuthenticationInfo是認(rèn)證的方法,當(dāng)用戶登陸的時候會調(diào)用搀庶,例如下面
     @PostMapping("login")
    public String login(String username, String password) {
        try {
            //shiro通過SecurityUtils.getSubject()獲得主體拐纱,主體可以理解為客戶端實例,原理在后面講
            Subject subject = SecurityUtils.getSubject();
            //已經(jīng)認(rèn)證過哥倔,也就是該客戶端已經(jīng)登陸過
            if (subject.isAuthenticated()) {
                return "redirect:/static/html/indexLogin.html";
            }
            //一般都使用UsernamePasswordToken,shiro的token中有Principal和Credentials的概念
            //Principal代表當(dāng)前客戶端要登錄的用戶揍庄,Credentials代表證明該用戶身份的憑證
            //UsernamePasswordToken將username作為Principal咆蒿,password作為Credentials
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            //rememberMe功能后面講
            token.setRememberMe(true);
            subject.login(token);
        } catch (AuthenticationException e) {
            //登錄失敗則跳轉(zhuǎn)到登錄失敗頁面,可能是用戶名或密碼錯誤
            return "redirect:/static/html/loginError.html";
        }
        return "redirect:/static/html/indexLogin.html";
    }
  • doGetAuthorizationInfo是授權(quán)的方法,在攔截器中進(jìn)行權(quán)限校驗的時候會調(diào)用
public class MyRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    //用戶的權(quán)限信息包含roles角色和permission權(quán)限兩部分沃测,我這里只使用了角色進(jìn)行進(jìn)行權(quán)限控制
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //principals.getPrimaryPrincipal()獲得的就是當(dāng)前用戶名
        if (principals == null || StringUtils.isBlank((String) principals.getPrimaryPrincipal())) {
            return null;
        }
        //將用戶角色信息傳入SimpleAuthorizationInfo
        return new SimpleAuthorizationInfo(userService.queryUserRole((String) principals.getPrimaryPrincipal()));
    }

    //token實際就是在login時傳入的UsernamePasswordToken
    //getPrincipal()中只執(zhí)行了getUsername(),getCredentials()只執(zhí)行了getPassword()
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        if (token == null||StringUtils.isBlank((String) token.getPrincipal())) {
            return null;
        }
        //根據(jù)token中的用戶名查庫缭黔,獲得user對象
        User user = userService.queryUserByName((String) token.getPrincipal());
        if (user == null) {
            return null;
        }
        //SimpleAuthenticationInfo代表該用戶的認(rèn)證信息,其實就是數(shù)據(jù)庫中的用戶名蒂破、密碼馏谨、加密密碼使用的鹽
        //存在數(shù)據(jù)庫中的密碼是對用戶真是密碼通過md5加鹽加密得到的,保證安全附迷,及時數(shù)據(jù)泄露惧互,也得不到真正的用戶密碼
        //getName()返回該realm的名字,代表該認(rèn)證信息的來源是該realm喇伯,作用不大喊儡,一般都是單realm
        //該方法返回后,上層會對token和SimpleAuthenticationInfo進(jìn)行比較稻据,首先比較Principal()艾猜,然后將token的Credentials
        //進(jìn)行md5加上SimpleAuthenticationInfo中的鹽加密,加密結(jié)果和SimpleAuthenticationInfo的Credentials比較
        return new SimpleAuthenticationInfo(
                user.getUserName(), user.getPassword(), ByteSource.Util.bytes(user.getUserName()), getName());
    }
  • 在securityManager中注入realm捻悯,其中authenticator必須先于realms注入匆赃,這一點非常關(guān)鍵,我之前無論如何都無法授權(quán)今缚,debug發(fā)現(xiàn)authenticator中的realms為空
    <!--非web環(huán)境使用DefaultSecurityManager-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--多realm的授權(quán)策略設(shè)置算柳,配置為必須滿足全部realm才算成功,不在realms前配置的話authenticator中的realms集合為空-->
        <!--securityManager注入realms的時候荚斯,會把realm也放一份到authenticator中埠居,所以必須寫在realms上面!!!-->
        <property name="authenticator">
            <bean class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
                <property name="authenticationStrategy">
                    <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"/>
                </property>
            </bean>
        </property>
        <!--如果只有一個realm的話,可以直接注入realm屬性事期,不需要注入realms屬性-->
        <!--為了以后的擴(kuò)展滥壕,即使只有一個realm還是注入了realms屬性(雖然以后估計也都是單realm)-->
        <property name="realms">
            <list>
                <bean class="com.qunar.lfz.shiro.MyRealm">
                    <property name="credentialsMatcher">
                        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                            <!--指定加密算法-->
                            <property name="hashAlgorithmName" value="MD5"/>
                            <!--指定對密碼連續(xù)進(jìn)行5輪md5加密-->
                            <property name="hashIterations" value="5"/>
                        </bean>
                    </property>
                </bean>
            </list>
        </property>
    </bean>
  • PS:因為我們指定了用戶的原密碼通過5次md5加鹽加密進(jìn)行校驗,這也就要求用戶注冊的時候存入數(shù)據(jù)庫的密碼也是經(jīng)過5次md5加鹽加密的兽泣。shiro提供了Md5Hash工具類绎橘,通過new Md5Hash("原密碼", "鹽值", 5).toString()查看加密后的密碼。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唠倦,一起剝皮案震驚了整個濱河市称鳞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稠鼻,老刑警劉巖冈止,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異候齿,居然都是意外死亡熙暴,警方通過查閱死者的電腦和手機(jī)闺属,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來周霉,“玉大人掂器,你說我怎么就攤上這事【阆洌” “怎么了国瓮?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狞谱。 經(jīng)常有香客問我乃摹,道長,這世上最難降的妖魔是什么芋簿? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任峡懈,我火速辦了婚禮,結(jié)果婚禮上与斤,老公的妹妹穿的比我還像新娘肪康。我一直安慰自己,他們只是感情好撩穿,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布磷支。 她就那樣靜靜地躺著,像睡著了一般食寡。 火紅的嫁衣襯著肌膚如雪雾狈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天抵皱,我揣著相機(jī)與錄音善榛,去河邊找鬼。 笑死呻畸,一個胖子當(dāng)著我的面吹牛移盆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伤为,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼咒循,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了绞愚?” 一聲冷哼從身側(cè)響起叙甸,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎位衩,沒想到半個月后裆蒸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡糖驴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年光戈,在試婚紗的時候發(fā)現(xiàn)自己被綠了哪痰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遂赠。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡久妆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跷睦,到底是詐尸還是另有隱情筷弦,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布抑诸,位于F島的核電站烂琴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蜕乡。R本人自食惡果不足惜奸绷,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望层玲。 院中可真熱鬧号醉,春花似錦、人聲如沸辛块。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽润绵。三九已至线椰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尘盼,已是汗流浹背憨愉。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留卿捎,地道東北人配紫。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像娇澎,于是被迫代替她去往敵國和親笨蚁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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