shiro(11)-shiroFilter(動(dòng)態(tài)配置過濾器鏈)

shiro安全控制目錄

shiro容器在啟動(dòng)的時(shí)候會(huì)默認(rèn)加載一個(gè)ini文件该押,里面有核心的默認(rèn)配置裙犹,當(dāng)然也有對(duì)應(yīng)的類,當(dāng)然在實(shí)際運(yùn)用中象对,我們可以通過查詢數(shù)據(jù)庫黑忱,將數(shù)據(jù)放入到InI對(duì)象中,動(dòng)態(tài)的進(jìn)行shiro的配置勒魔。

1. 源碼引來的思考

一般我們配置shiro主過濾鏈?zhǔn)侨缦屡渲玫母ι罚瑢tring類型注入到filterChainDefinitions屬性中。

<!-- Shiro主過濾器本身功能十分強(qiáng)大,其強(qiáng)大之處就在于它支持任何基于URL路徑表達(dá)式的冠绢、自定義的過濾器的執(zhí)行 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--shiro的核心安全接口抚吠,這是屬性是必須的-->
        <property name="securityManager" ref="securityManager"/>
        <!-- 要求登錄時(shí)的鏈接,非必須的屬性,默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁面 -->
        <property name="loginUrl" value="/login/init"></property>
        <!-- 用戶訪問未對(duì)其授權(quán)的資源時(shí),所顯示的連接 -->
        <property name="unauthorizedUrl" value="/pages/error/403.jsp"/>
        <!--定義過濾器鏈-->
        <property name="filterChainDefinitions">
            <value>
                <!--Shiro過濾器鏈的配置-->
                /login/init/** = anon <!-- 對(duì)于登錄相關(guān)不進(jìn)行鑒權(quán) -->
                /login/login/** = anon <!-- 對(duì)于登錄相關(guān)不進(jìn)行鑒權(quán) -->
                /static/** = anon     <!-- 靜態(tài)資源不進(jìn)行鑒權(quán) -->
                /** = user
            </value>
        </property>
        <property name="filters">
            <map>
                <entry key="user" value-ref="userFilter"></entry>
            </map>
        </property>
    </bean>
    <!--自定義攔截器-->
    <bean id="userFilter" class="com.springmvc.common.filter.SystemUserFilter"/>

而在源碼中,實(shí)際上是創(chuàng)建了Ini.section(實(shí)際上是一個(gè)Map<String,String>) [組件]對(duì)象弟胀,然后調(diào)用了 setFilterChainDefinitionMap(section);方法楷力。那么我們可以自定義一個(gè)Ini.section對(duì)象(無需在XML中進(jìn)行配置,可以動(dòng)態(tài)配置過濾器鏈)孵户,然后set注入到filterChainDefinitionMap中呀弥雹。

shiro的filterChainDefinitions屬性源碼配置:

    public void setFilterChainDefinitions(String definitions) {
        Ini ini = new Ini();
        ini.load(definitions);
       
        Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
        if (CollectionUtils.isEmpty(section)) {
            section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
        }
        //將設(shè)置的Ini.Section保存到FilterChainDefinitionMap中。
        setFilterChainDefinitionMap(section);
    }

傳入的definitions值:

                /login/init/** = anon 
                /login/login/** = user 
                /static/** = anon     
                /** = user

2. 動(dòng)態(tài)配置過濾器鏈

Spring-FactoryBean學(xué)習(xí)(更靈活的創(chuàng)建bean的方式)

Spring的XML配置文件將對(duì)象交由Spring管理的原理是:通過反射機(jī)制創(chuàng)建出對(duì)象延届,再交由Spring容器管理剪勿。那么我們在代碼中若想將Bean注入到容器中,我們可以new出對(duì)象來方庭。在調(diào)用對(duì)象的set方法或者構(gòu)造方法完成屬性注入厕吉。

為什么動(dòng)態(tài)配置過濾器鏈呢?

一般shiro的權(quán)限控制原理就是

  1. 實(shí)現(xiàn)AuthorizingRealm類械念,實(shí)現(xiàn)里面的鉤子方法doGetAuthenticationInfo() 認(rèn)證回調(diào)方法头朱,每次登陸時(shí)調(diào)用;doGetAuthorizationInfo()授權(quán)查詢方法龄减,當(dāng)使用注解 @RequiresPermissions進(jìn)行權(quán)限控制時(shí)回調(diào)项钮,判斷用戶是否含有該權(quán)限。本質(zhì)上就是AOP的體現(xiàn)希停。

  2. 但是通過給每個(gè)權(quán)限方法加注解烁巫,代碼比較死板。所以若是可以將權(quán)限信息在項(xiàng)目啟動(dòng)的時(shí)候宠能,配置在過濾器鏈中(自定義過濾器鏈可參考:shiro(8)-shiro過濾器+自定義過濾器)亚隙。方式就像授權(quán)攔截器一樣,/user/**=perms["user:create"]违崇,那么就可以舍棄權(quán)限標(biāo)簽的用法阿弃。

  3. 關(guān)鍵點(diǎn)就是如何動(dòng)態(tài)的獲取user:create權(quán)限信息诊霹。并且默認(rèn)的perms授權(quán)器要求滿足所有的權(quán)限,才可放行渣淳。故我們需要自定義一個(gè)過濾器脾还,滿足任一權(quán)限,便可放行入愧。

1. 自定義授權(quán)攔截器myPermissionFilter:滿足任一權(quán)限便可放行
代碼可參考:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

2. 自定義授權(quán)攔截器myAuthcFilter:針對(duì)上送的請(qǐng)求鄙漏,進(jìn)行用戶認(rèn)證(可以在代碼中使用getSubject(request, response).login(token);進(jìn)行用戶登錄)

如何動(dòng)態(tài)配置過濾器鏈呢?

1. 方式一:使用@Bean注解

    //讀取配置文件中的(自定義的) ShiroFilter鏈配置
    @Value("${shiro.filterChainDefinitions}")
    private String filterChainDefinitions;

    @Bean
    public Ini.Section section(){
        //自定義過濾器鏈名稱
        String restPermissionString = "myAuthcFilter,myPermissionFilter[{0}]";
        //加載默認(rèn)的url過濾定義
        Ini ini = new Ini();
        //加載XML配置中的filterChainDefinitions配置
        ini.load(this.filterChainDefinitions);
        //配置Map<String,String>
        Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
        
        List<MyResource> resList =  MyResourceService.queryResourcList(query);
        // 將自定義url過濾添加到section中
        for (MyResourceres : resList) {
            if(StringUtils.isNotBlank(res.getResKey()) && StringUtils.isNotBlank(res.getResUrl())) {
                if(!section.containsKey(res.getResKey())) {
                    section.put(res.getResUrl(), MessageFormat.format(restPermissionString, res.getResKey()));
                }
            }
        }
        section.put("/**", "myAuthcFilter");
        return section;
    }

2. 方式二:使用FactoryBean<Ini.Section>創(chuàng)建對(duì)象

public class ChainDefinitionSectionMetaSource implements FactoryBean<Section> {
    @Autowired
    private SysRescService sysRescService;
    /**
     * 默認(rèn)url過濾定義(shiro過濾器的filterChainDefinitions屬性)
     */
    @Value("${shiro.filterChainDefinitions}")
    private String filterChainDefinitions;

    /**
     * 設(shè)置默認(rèn)url過濾定義
     *
     * @param filterChainDefinitions
     */
    public void setFilterChainDefinitions(String filterChainDefinitions) {
        this.filterChainDefinitions = filterChainDefinitions;
    }

    @Override
    public Section getObject() throws Exception {
         //自定義過濾器鏈名稱
        String restPermissionString = "myAuthcFilter,myPermissionFilter[{0}]";
        // 加載默認(rèn)的url過濾定義
        Ini ini = new Ini();
        ini.load(this.filterChainDefinitions);
        Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
        
        List<MyResource> resList =  MyResourceService.queryResourcList(query);
        // 將自定義url過濾添加到section中
        for (MyResourceres : resList) {
            if(StringUtils.isNotBlank(res.getResKey()) && StringUtils.isNotBlank(res.getResUrl())) {
                if(!section.containsKey(res.getResKey())) {
                    //   /order/query   myAuthcFilter,myPermissionFilter[sysuser_list]砂客。/order/query路徑訪問的情況下泥张,必須經(jīng)過認(rèn)證,并且有sysuser_list的權(quán)限
                    section.put(res.getResUrl(), MessageFormat.format(restPermissionString, res.getResKey()));
                }
            }
        }
        section.put("/**", "myAuthcFilter");
        return section;
    }
    @Override
    public Class<?> getObjectType() {
        return Section.class;
    }
    @Override
    public boolean isSingleton() {
        return false;
    }
}

靈活的創(chuàng)建出復(fù)雜的bean對(duì)象后鞠值,通過XML將其交由Spring管理媚创。(需要注意的是:即使class參數(shù)后面是FactoryBean<T>對(duì)象,但是這個(gè)Bean彤恶,卻是InI.section類型钞钙。)

  <!--自定義ChainDefinitionSectionMetaSource-->
  <bean id="section" class="com.system.shiro.ChainDefinitionSectionMetaSource">
        <property name="filterChainDefinitions">
            <value>
                <!-- Shiro 過濾鏈的定義 -->
                /login/** = anon <!-- 對(duì)于登錄相關(guān)不進(jìn)行鑒權(quán) -->
                /register/** = anon <!-- 對(duì)于注冊相關(guān)不進(jìn)行鑒權(quán) -->
                /static/** = anon <!-- 靜態(tài)資源不進(jìn)行鑒權(quán) -->
            </value>
        </property>
    </bean>

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- Shiro的核心安全接口,這個(gè)屬性是必須的 -->
        <property name="securityManager" ref="securityManager"/>
        <property name="unauthorizedUrl" value="/error/403"/>
        <!--由于filterChainDefinitionMap創(chuàng)建比較復(fù)雜,而且需要和數(shù)據(jù)庫交互(靈活)声离,故若是在配置文件中創(chuàng)建芒炼,那么繁雜而又死板。故采用FactoryBean在代碼中創(chuàng)建Bean术徊。-->
        <property name="filterChainDefinitionMap" ref="section" />
        <!--自定義攔截器-->
        <property name="filters">
            <map>
                <entry key="myAuthcFilter" value-ref="myAuthcFilter"/>
                <entry key="myPermissionFilter" value-ref="myPermissionFilter"/>
            </map>
        </property>
    </bean>

文章參考

apache shiro-動(dòng)態(tài)創(chuàng)建filterChainDefinitions

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末本刽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赠涮,更是在濱河造成了極大的恐慌子寓,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笋除,死亡現(xiàn)場離奇詭異斜友,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)垃它,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門鲜屏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人国拇,你說我怎么就攤上這事洛史。” “怎么了贝奇?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵虹菲,是天一觀的道長。 經(jīng)常有香客問我掉瞳,道長毕源,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任陕习,我火速辦了婚禮霎褐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘该镣。我一直安慰自己冻璃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布损合。 她就那樣靜靜地躺著省艳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嫁审。 梳的紋絲不亂的頭發(fā)上跋炕,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音律适,去河邊找鬼辐烂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛捂贿,可吹牛的內(nèi)容都是我干的纠修。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼厂僧,長吁一口氣:“原來是場噩夢啊……” “哼扣草!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起颜屠,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤辰妙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后汽纤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體上岗,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蕴坪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肴掷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡背传,死狀恐怖呆瞻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情径玖,我是刑警寧澤痴脾,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站梳星,受9級(jí)特大地震影響赞赖,放射性物質(zhì)發(fā)生泄漏滚朵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一前域、第九天 我趴在偏房一處隱蔽的房頂上張望辕近。 院中可真熱鬧,春花似錦匿垄、人聲如沸移宅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漏峰。三九已至,卻和暖如春届榄,著一層夾襖步出監(jiān)牢的瞬間浅乔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工痒蓬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留童擎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓攻晒,卻偏偏與公主長得像顾复,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鲁捏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350