shiro在項(xiàng)目中的使用(一)

【前言】
Apache Shiro是Java的一個(gè)安全框架。主要用于權(quán)限控制,簡(jiǎn)單易用。之前單看shiro始終理會(huì)不了它的思想,后來(lái)在網(wǎng)上發(fā)現(xiàn)某智的一個(gè)bos項(xiàng)目中運(yùn)用到了shiro,果斷學(xué)習(xí),看完后略有心得,以記之。

【項(xiàng)目簡(jiǎn)介】
該bos項(xiàng)目主要是一個(gè)后臺(tái)管理項(xiàng)目,采用傳統(tǒng)ssh技術(shù)架構(gòu),使用spring與shiro進(jìn)行整合做權(quán)限攔截,以下是項(xiàng)目中權(quán)限部分的筆記。

首先,我們從外部來(lái)看Shiro吧,即從應(yīng)用程序角度的來(lái)觀察如何使用Shiro完成工作

shiro工作流程.png
Subject:主體,代表了當(dāng)前“用戶(hù)”

SecurityManager:安全管理器;即所有與安全有關(guān)的操作都會(huì)與SecurityManager交互

Realm:域,Shiro從從Realm獲取安全數(shù)據(jù)(如用戶(hù)、角色、權(quán)限)

用戶(hù)主體訪(fǎng)問(wèn)系統(tǒng),由安全管理器進(jìn)行權(quán)限驗(yàn)證,安全管理器從Realm域中獲取到該用戶(hù)的角色權(quán)限,并作出相應(yīng)的權(quán)限反饋,Realm域就是一個(gè)Dao,主要獲取用戶(hù)的角色權(quán)限數(shù)據(jù)并交給安全管理器。所以整個(gè)流程中,安全管理器是核心角色。

【1】web.xml中配置shiro的過(guò)濾器
shiro的過(guò)濾器就類(lèi)似于struts2的核心過(guò)濾器一般

<!-- shiro過(guò)濾器 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

注意:這里filterName的值可以隨便起,無(wú)要求。但是在spring注入時(shí)要保持一致

【2】將shiro過(guò)濾器注入spring

<!-- 配置工廠(chǎng)bean,用于創(chuàng)建shiro框架用到過(guò)濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 注入安全管理 -->
        <property name="securityManager" ref="securityManager"></property>
        <!-- 注入當(dāng)前系統(tǒng)的登錄頁(yè)面 -->
        <property name="loginUrl" value="/login.jsp"></property>
        <!-- 注入成功頁(yè)面 -->
        <property name="successUrl" value="/index.jsp"></property>
        <!-- 注入權(quán)限不足頁(yè)面 -->
        <property name="unauthorizedUrl" value="/unauthorizedUrl.jsp"/>
        <!-- 注入url攔截規(guī)則 -->
        <property name="filterChainDefinitions">
            <value>
                /css/** = anon
                /images/** = anon
                /js/** = anon
                /login.jsp* = anon
                /validatecode.jsp* = anon
                /userAction_login.action = anon
                /page_base_staff.action = perms["staff"]<!-- roles角色集,perms權(quán)限集 -->
                /* = authc
            </value>
            <!-- 
                /page_base_staff.action = roles["staff"]//要訪(fǎng)問(wèn)此action必須有staff這個(gè)角色
                /page_base_staff.action = perms["staff"]//要訪(fǎng)問(wèn)此action必須有staff這個(gè)權(quán)限
             -->
        </property>
    </bean>

注意:以上屬性均以set注入,查看ShiroFilterFactoryBean源碼便知,業(yè)務(wù)需要配哪個(gè)屬性就配哪個(gè),非必要配置,這里頁(yè)面以/開(kāi)頭均在webroot目錄下。

這里bean 的id屬性要與web.xml中shiro的過(guò)濾器名稱(chēng)一致

url攔截規(guī)則:一般圖片、JS、CSS樣式不攔截,直接設(shè)置anon角色權(quán)限即可(/css/** = anon)
具體url攔截:
    /page_base_staff.action = perms["staff"]//訪(fǎng)問(wèn)此action需要staff權(quán)限
    /page_base_staff.action =roles["staff"]//訪(fǎng)問(wèn)此action需要staff角色(角色是權(quán)限的集合)

要攔截的路徑:/* = authc

【3】編寫(xiě)自定義Realm域并注入到spring中
自定義Realm需要繼承AuthorizingRealm,實(shí)現(xiàn)其認(rèn)證 授權(quán)方法

/**
 * ClassName: BOSRealm
 * @author lvfang
 * @Desc: 自定義realm
 * @date 2017-8-23
 */
public class BOSRealm extends AuthorizingRealm {
    
    @Resource
    private IUserDao userDao;
    
    /**
     * 認(rèn)證方法(是否有這個(gè)用戶(hù))
     */
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        System.out.println("進(jìn)入認(rèn)證方法... ...");
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;//token令牌強(qiáng)轉(zhuǎn)
        String username = upToken.getUsername();//從令牌中得到用戶(hù)名
        
        //查詢(xún)用戶(hù)
        User user = userDao.findUserByUsername(username);
        if(user == null){
            //用戶(hù)名不存在
            return null;
        }else{
            //用戶(hù)名存在
            String password = user.getPassword();
            
            // 創(chuàng)建簡(jiǎn)單認(rèn)證信息對(duì)象
            /***
             * 參數(shù)一:簽名,程序可以在任意位置獲取當(dāng)前放入的對(duì)象
             * 參數(shù)二:從數(shù)據(jù)庫(kù)中查詢(xún)出的密碼
             * 參數(shù)三:當(dāng)前realm的名稱(chēng)
             */
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,password,this.getClass().getSimpleName());
            //返回給安全管理器,由安全管理器負(fù)責(zé)比對(duì)數(shù)據(jù)庫(kù)中查詢(xún)出的密碼和頁(yè)面提交的密碼
            return info;
        }   
    }

    /**
     * 授權(quán)方法(這個(gè)用戶(hù)有什么權(quán)限)
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("進(jìn)入授權(quán)方法... ...");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("staff");//為當(dāng)前用戶(hù)授予staff權(quán)限
        info.addRole("staff");//為當(dāng)前用戶(hù)授予staff角色(角色是權(quán)限的集合)    
        //TODO 根據(jù)當(dāng)前登錄用戶(hù)查詢(xún)數(shù)據(jù)庫(kù),獲取其對(duì)應(yīng)的權(quán)限數(shù)據(jù)
        
        return info;
    }
}

注入spring

<!-- 注冊(cè)自定義realm -->
    <bean id="bosRealm" class="com.itheima.bos.shiro.BOSRealm"></bean>

【4】注入安全管理器,并將realm注入給安全管理器

<!-- 注入 securityManager管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 將自定義realm注入給securityManager管理 -->
        <property name="realm" ref="bosRealm"></property>
    </bean>

【5】在login方法中做處理

/**
     * 登陸方法(shiro版)
     * @return
     */
    public String login(){  
        //判斷驗(yàn)證碼
        String code = (String) this.setSession("key");
        //判斷驗(yàn)證碼是否正確
        if(StringUtils.isNotBlank(checkcode)&& checkcode.equals(code)){
            
            //獲取當(dāng)前用戶(hù)對(duì)象
            Subject subject = SecurityUtils.getSubject();//目前為"未認(rèn)證狀態(tài)"
            String password = model.getPassword();
            password = MD5Utils.md5(password);
            //構(gòu)造一個(gè)用戶(hù)名密碼令牌
            AuthenticationToken token = new UsernamePasswordToken(model.getUsername(),password);
            
            try {
                subject.login(token);
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                //登陸失敗  設(shè)置提示信息,跳轉(zhuǎn)登陸頁(yè)面
                this.addActionError("用戶(hù)名不存在!");
                return "login";
            } catch (Exception e) {
                e.printStackTrace();
                //登陸失敗  設(shè)置提示信息,跳轉(zhuǎn)登陸頁(yè)面
                this.addActionError("用戶(hù)名或密碼錯(cuò)誤!");
                return "login";
            }
            
            //獲取認(rèn)證信息對(duì)象中存儲(chǔ)的User對(duì)象
            User user = (User) subject.getPrincipal();
            this.getSession().setAttribute("loginUser", user);//用戶(hù)存入session
            return "home";
        }else{
            //登陸失敗,驗(yàn)證碼失敗 跳轉(zhuǎn)至登陸頁(yè)面
            this.addActionError("驗(yàn)證碼錯(cuò)誤!");
            return "login";
        }   
    }

這里根據(jù)username和password構(gòu)造一個(gè)用戶(hù)名令牌,當(dāng)subject主體去調(diào)用login()方法登陸時(shí),會(huì)執(zhí)行Realm域中的認(rèn)證和授權(quán)方法。

【附加】

1:
shiro過(guò)濾器屬性含義

securityManager:這個(gè)屬性是必須的。

loginUrl :沒(méi)有登錄的用戶(hù)請(qǐng)求需要登錄的頁(yè)面時(shí)自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面,不是必須的屬性,不輸入地址的話(huà)會(huì)自動(dòng)尋找項(xiàng)目web項(xiàng)目的根目錄下的”/login.jsp”頁(yè)面。

successUrl :登錄成功默認(rèn)跳轉(zhuǎn)頁(yè)面,不配置則跳轉(zhuǎn)至”/”。如果登陸前點(diǎn)擊的一個(gè)需要登錄的頁(yè)面,則在登錄自動(dòng)跳轉(zhuǎn)到那個(gè)需要登錄的頁(yè)面。不跳轉(zhuǎn)到此。

unauthorizedUrl :沒(méi)有權(quán)限默認(rèn)跳轉(zhuǎn)的頁(yè)面


2:
其權(quán)限過(guò)濾器及配置釋義

anon:例子/admins/**=anon 沒(méi)有參數(shù),表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要認(rèn)證(登錄)才能使用,沒(méi)有參數(shù)

roles(角色):例子/admins/user/**=roles[admin],參數(shù)可以寫(xiě)多個(gè),多個(gè)時(shí)必須加上引號(hào),并且參數(shù)之間用逗號(hào)分割,當(dāng)有多個(gè)參數(shù)時(shí),例如admins/user/**=roles["admin,guest"],每個(gè)參數(shù)通過(guò)才算通過(guò),相當(dāng)于hasAllRoles()方法。

perms(權(quán)限):例子/admins/user/**=perms[user:add:*],參數(shù)可以寫(xiě)多個(gè),多個(gè)時(shí)必須加上引號(hào),并且參數(shù)之間用逗號(hào)分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],當(dāng)有多個(gè)參數(shù)時(shí)必須每個(gè)參數(shù)都通過(guò)才通過(guò),想當(dāng)于isPermitedAll()方法。

rest:例子/admins/user/**=rest[user],根據(jù)請(qǐng)求的方法,相當(dāng)于/admins/user/**=perms[user:method] ,其中method為post,get,delete等。

port:例子/admins/user/**=port[8081],當(dāng)請(qǐng)求的url的端口不是8081是跳轉(zhuǎn)到schemal://serverName:8081?queryString,其中schmal是協(xié)議http或https等,serverName是你訪(fǎng)問(wèn)的host,8081是url配置里port的端口,queryString

是你訪(fǎng)問(wèn)的url里的?后面的參數(shù)。

authcBasic:例如/admins/user/**=authcBasic沒(méi)有參數(shù)表示httpBasic認(rèn)證

ssl:例子/admins/user/**=ssl沒(méi)有參數(shù),表示安全的url請(qǐng)求,協(xié)議為https

user:例如/admins/user/**=user沒(méi)有參數(shù)表示必須存在用戶(hù),當(dāng)?shù)侨氩僮鲿r(shí)不做檢查

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139
  • 前言 Spring boot 是什么,網(wǎng)上的很多介紹,這里博客就不多介紹了。如果不明白Spring boot是什么...
    xuezhijian閱讀 18,015評(píng)論 13 39
  • 1.簡(jiǎn)介 Apache Shiro是Java的一個(gè)安全框架。功能強(qiáng)大,使用簡(jiǎn)單的Java安全框架,它為開(kāi)發(fā)人員提供...
    H_Man閱讀 3,247評(píng)論 4 47
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,261評(píng)論 6 342
  • 雨突然 下得好猛 他沒(méi)帶傘 躲在路邊屋檐 發(fā)呆 那傘下的姑娘們 行色勿勿 他問(wèn) 白娘子呀 你眼淚漣漣找啥呢 許仙又...
    雪莉詩(shī)話(huà)閱讀 175評(píng)論 1 12

友情鏈接更多精彩內(nèi)容