日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

第十九章 動(dòng)態(tài)URL權(quán)限控制——《跟我學(xué)Shiro》

系統(tǒng) 1840 0

?

目錄貼: ?跟我學(xué)Shiro目錄貼

?

用過(guò)Spring Security的朋友應(yīng)該比較熟悉對(duì)URL進(jìn)行全局的權(quán)限控制,即訪問(wèn)URL時(shí)進(jìn)行權(quán)限匹配;如果沒(méi)有權(quán)限直接跳到相應(yīng)的錯(cuò)誤頁(yè)面。Shiro也支持類似的機(jī)制,不過(guò)需要稍微改造下來(lái)滿足實(shí)際需求。不過(guò)在Shiro中,更多的是通過(guò)AOP進(jìn)行分散的權(quán)限控制,即方法級(jí)別的;而通過(guò)URL進(jìn)行權(quán)限控制是一種集中的權(quán)限控制。本章將介紹如何在Shiro中完成動(dòng)態(tài)URL權(quán)限控制。

?

本章代碼基于《第十六章?綜合實(shí)例》,請(qǐng)先了解相關(guān)數(shù)據(jù)模型及基本流程后再學(xué)習(xí)本章。

?

表及數(shù)據(jù) SQL

請(qǐng)運(yùn)行shiro-example-chapter19/sql/ shiro-schema.sql?表結(jié)構(gòu)

請(qǐng)運(yùn)行shiro-example-chapter19/sql/ shiro-schema.sql?數(shù)據(jù)

?

實(shí)體

具體請(qǐng)參考com.github.zhangkaitao.shiro.chapter19包下的實(shí)體。?

      public class UrlFilter implements Serializable {
    private Long id;
    private String name; //url名稱/描述
    private String url; //地址
    private String roles; //所需要的角色,可省略
    private String permissions; //所需要的權(quán)限,可省略
}?
    

表示攔截的URL和角色/權(quán)限之間的關(guān)系,多個(gè)角色/權(quán)限之間通過(guò)逗號(hào)分隔,此處還可以擴(kuò)展其他的關(guān)系,另外可以加如available屬性表示是否開(kāi)啟該攔截。

?

DAO

具體請(qǐng)參考com.github.zhangkaitao.shiro.chapter19.dao包下的DAO接口及實(shí)現(xiàn)。

?

Service

具體請(qǐng)參考com.github.zhangkaitao.shiro.chapter19.service包下的Service接口及實(shí)現(xiàn)。??

      public interface UrlFilterService {
    public UrlFilter createUrlFilter(UrlFilter urlFilter);
    public UrlFilter updateUrlFilter(UrlFilter urlFilter);
    public void deleteUrlFilter(Long urlFilterId);
    public UrlFilter findOne(Long urlFilterId);
    public List<UrlFilter> findAll();
}
    

基本的URL攔截的增刪改查實(shí)現(xiàn)。?

?

      @Service
public class UrlFilterServiceImpl implements UrlFilterService {
    @Autowired
private ShiroFilerChainManager shiroFilerChainManager;

    @Override
    public UrlFilter createUrlFilter(UrlFilter urlFilter) {
        urlFilterDao.createUrlFilter(urlFilter);
        initFilterChain();
        return urlFilter;
    }
    //其他方法請(qǐng)參考源碼
    @PostConstruct
    public void initFilterChain() {
        shiroFilerChainManager.initFilterChains(findAll());
    }
}?
    

UrlFilterServiceImpl在進(jìn)行新增、修改、刪除時(shí)會(huì)調(diào)用initFilterChain來(lái)重新初始化Shiro的URL攔截器鏈,即同步數(shù)據(jù)庫(kù)中的URL攔截器定義到Shiro中。此處也要注意如果直接修改數(shù)據(jù)庫(kù)是不會(huì)起作用的,因?yàn)橹灰{(diào)用這幾個(gè)Service方法時(shí)才同步。另外當(dāng)容器啟動(dòng)時(shí)會(huì)自動(dòng)回調(diào)initFilterChain來(lái)完成容器啟動(dòng)后的URL攔截器的注冊(cè)。

??

ShiroFilerChainManager ?

      @Service
public class ShiroFilerChainManager {
    @Autowired private DefaultFilterChainManager filterChainManager;
    private Map<String, NamedFilterList> defaultFilterChains;
    @PostConstruct
    public void init() {
        defaultFilterChains = 
          new HashMap<String, NamedFilterList>(filterChainManager.getFilterChains());
    }
    public void initFilterChains(List<UrlFilter> urlFilters) {
        //1、首先刪除以前老的filter chain并注冊(cè)默認(rèn)的
        filterChainManager.getFilterChains().clear();
        if(defaultFilterChains != null) {
            filterChainManager.getFilterChains().putAll(defaultFilterChains);
        }
        //2、循環(huán)URL Filter 注冊(cè)filter chain
        for (UrlFilter urlFilter : urlFilters) {
            String url = urlFilter.getUrl();
            //注冊(cè)roles filter
            if (!StringUtils.isEmpty(urlFilter.getRoles())) {
                filterChainManager.addToChain(url, "roles", urlFilter.getRoles());
            }
            //注冊(cè)perms filter
            if (!StringUtils.isEmpty(urlFilter.getPermissions())) {
                filterChainManager.addToChain(url, "perms", urlFilter.getPermissions());
            }
        }
    }
}?
    

1、init:Spring容器啟動(dòng)時(shí)會(huì)調(diào)用init方法把在spring配置文件中配置的默認(rèn)攔截器保存下來(lái),之后會(huì)自動(dòng)與數(shù)據(jù)庫(kù)中的配置進(jìn)行合并。

2、initFilterChains:UrlFilterServiceImpl會(huì)在Spring容器啟動(dòng)或進(jìn)行增刪改UrlFilter時(shí)進(jìn)行注冊(cè)URL攔截器到Shiro。

?

攔截器及攔截器鏈知識(shí)請(qǐng)參考《第八章?攔截器機(jī)制》,此處再介紹下Shiro攔截器的流程:

AbstractShiroFilter //如ShiroFilter/ SpringShiroFilter都繼承該Filter

?? doFilter //Filter的doFilter

?? ??doFilterInternal //轉(zhuǎn)調(diào)doFilterInternal

????? ?executeChain(request, response, chain) //執(zhí)行攔截器鏈

? ???????FilterChain chain = getExecutionChain(request, response, origChain) //使用原始攔截器鏈獲取新的攔截器鏈

????? ?????chain.doFilter(request, response) //執(zhí)行新組裝的攔截器鏈

?

getExecutionChain(request, response, origChain) //獲取攔截器鏈流程

?????? FilterChainResolver resolver = getFilterChainResolver(); //獲取相應(yīng)的FilterChainResolver

?????? FilterChain resolved = resolver.getChain(request, response, origChain); //通過(guò)FilterChainResolver根據(jù)當(dāng)前請(qǐng)求解析到新的FilterChain攔截器鏈

?

默認(rèn)情況下如使用ShiroFilterFactoryBean創(chuàng)建shiroFilter時(shí),默認(rèn)使用PathMatchingFilterChainResolver進(jìn)行解析,而它默認(rèn)是根據(jù)當(dāng)前請(qǐng)求的URL獲取相應(yīng)的攔截器鏈,使用Ant模式進(jìn)行URL匹配;默認(rèn)使用DefaultFilterChainManager進(jìn)行攔截器鏈的管理。

?

PathMatchingFilterChainResolver默認(rèn)流程:

      public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
    //1、首先獲取攔截器鏈管理器
    FilterChainManager filterChainManager = getFilterChainManager();
    if (!filterChainManager.hasChains()) {
        return null;
    }
    //2、接著獲取當(dāng)前請(qǐng)求的URL(不帶上下文)
    String requestURI = getPathWithinApplication(request);
    //3、循環(huán)攔截器管理器中的攔截器定義(攔截器鏈的名字就是URL模式)
    for (String pathPattern : filterChainManager.getChainNames()) {
        //4、如當(dāng)前URL匹配攔截器名字(URL模式)
        if (pathMatches(pathPattern, requestURI)) {
            //5、返回該URL模式定義的攔截器鏈
            return filterChainManager.proxy(originalChain, pathPattern);
        }
    }
    return null;
}?
    

默認(rèn)實(shí)現(xiàn)有點(diǎn)小問(wèn)題:

如果多個(gè)攔截器鏈都匹配了當(dāng)前請(qǐng)求URL,那么只返回第一個(gè)找到的攔截器鏈;后續(xù)我們可以修改此處的代碼,將多個(gè)匹配的攔截器鏈合并返回。

?

DefaultFilterChainManager內(nèi)部使用Map來(lái)管理URL模式-攔截器鏈的關(guān)系;也就是說(shuō)相同的URL模式只能定義一個(gè)攔截器鏈,不能重復(fù)定義;而且如果多個(gè)攔截器鏈都匹配時(shí)是無(wú)序的(因?yàn)槭褂胢ap.keySet()獲取攔截器鏈的名字,即URL模式)。

?

FilterChainManager接口:?

      public interface FilterChainManager {
    Map<String, Filter> getFilters(); //得到注冊(cè)的攔截器
    void addFilter(String name, Filter filter); //注冊(cè)攔截器
    void addFilter(String name, Filter filter, boolean init); //注冊(cè)攔截器
    void createChain(String chainName, String chainDefinition); //根據(jù)攔截器鏈定義創(chuàng)建攔截器鏈
    void addToChain(String chainName, String filterName); //添加攔截器到指定的攔截器鏈
    void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) throws ConfigurationException; //添加攔截器(帶有配置的)到指定的攔截器鏈
    NamedFilterList getChain(String chainName); //獲取攔截器鏈
    boolean hasChains(); //是否有攔截器鏈
    Set<String> getChainNames(); //得到所有攔截器鏈的名字
    FilterChain proxy(FilterChain original, String chainName); //使用指定的攔截器鏈代理原始攔截器鏈
}?
    

此接口主要三個(gè)功能:注冊(cè)攔截器,注冊(cè)攔截器鏈,對(duì)原始攔截器鏈生成代理之后的攔截器鏈,比如??

      <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
……
    <property name="filters">
        <util:map>
            <entry key="authc" value-ref="formAuthenticationFilter"/>
            <entry key="sysUser" value-ref="sysUserFilter"/>
        </util:map>
    </property>
    <property name="filterChainDefinitions">
        <value>
            /login = authc
            /logout = logout
            /authenticated = authc
            /** = user,sysUser
        </value>
    </property>
</bean>?
    

filters屬性定義了攔截器;filterChainDefinitions定義了攔截器鏈;如/**就是攔截器鏈的名字;而user,sysUser就是攔截器名字列表。

?

之前說(shuō)過(guò)默認(rèn)的PathMatchingFilterChainResolver和DefaultFilterChainManager不能滿足我們的需求,我們稍微擴(kuò)展了一下:

??

CustomPathMatchingFilterChainResolver ?

      public class CustomPathMatchingFilterChainResolver
             extends PathMatchingFilterChainResolver {
  private CustomDefaultFilterChainManager customDefaultFilterChainManager;
  public void setCustomDefaultFilterChainManager(
        CustomDefaultFilterChainManager customDefaultFilterChainManager) {
      this.customDefaultFilterChainManager = customDefaultFilterChainManager;
      setFilterChainManager(customDefaultFilterChainManager);
  }

  public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
      FilterChainManager filterChainManager = getFilterChainManager();
      if (!filterChainManager.hasChains()) {
          return null;
      }
      String requestURI = getPathWithinApplication(request);
      List<String> chainNames = new ArrayList<String>();
      for (String pathPattern : filterChainManager.getChainNames()) {
        if (pathMatches(pathPattern, requestURI)) {
        chainNames.add(pathPattern);
        }
      }
      if(chainNames.size() == 0) {
        return null;
      }
      return customDefaultFilterChainManager.proxy(originalChain, chainNames);
  }
}?
    

和默認(rèn)的PathMatchingFilterChainResolver區(qū)別是,此處得到所有匹配的攔截器鏈,然后通過(guò)調(diào)用CustomDefaultFilterChainManager.proxy(originalChain, chainNames)進(jìn)行合并后代理。

?

CustomDefaultFilterChainManager ? ??

      public class CustomDefaultFilterChainManager extends DefaultFilterChainManager {
    private Map<String, String> filterChainDefinitionMap = null;
    private String loginUrl;
    private String successUrl;
    private String unauthorizedUrl;
    public CustomDefaultFilterChainManager() {
        setFilters(new LinkedHashMap<String, Filter>());
        setFilterChains(new LinkedHashMap<String, NamedFilterList>());
        addDefaultFilters(true);
    }
    public Map<String, String> getFilterChainDefinitionMap() {
        return filterChainDefinitionMap;
    }
    public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {
        this.filterChainDefinitionMap = filterChainDefinitionMap;
    }
    public void setCustomFilters(Map<String, Filter> customFilters) {
        for(Map.Entry<String, Filter> entry : customFilters.entrySet()) {
            addFilter(entry.getKey(), entry.getValue(), false);
        }
}
    public void setDefaultFilterChainDefinitions(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);
        }
        setFilterChainDefinitionMap(section);
    }
    public String getLoginUrl() {
        return loginUrl;
    }
    public void setLoginUrl(String loginUrl) {
        this.loginUrl = loginUrl;
    }
    public String getSuccessUrl() {
        return successUrl;
    }
    public void setSuccessUrl(String successUrl) {
        this.successUrl = successUrl;
    }
    public String getUnauthorizedUrl() {
        return unauthorizedUrl;
    }
    public void setUnauthorizedUrl(String unauthorizedUrl) {
        this.unauthorizedUrl = unauthorizedUrl;
    }
    @PostConstruct
    public void init() {
        Map<String, Filter> filters = getFilters();
        if (!CollectionUtils.isEmpty(filters)) {
            for (Map.Entry<String, Filter> entry : filters.entrySet()) {
                String name = entry.getKey();
                Filter filter = entry.getValue();
                applyGlobalPropertiesIfNecessary(filter);
                if (filter instanceof Nameable) {
                    ((Nameable) filter).setName(name);
                }
                addFilter(name, filter, false);
            }
        }
        Map<String, String> chains = getFilterChainDefinitionMap();
        if (!CollectionUtils.isEmpty(chains)) {
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue();
                createChain(url, chainDefinition);
            }
        }
    }
    protected void initFilter(Filter filter) {
        //ignore 
    }

    public FilterChain proxy(FilterChain original, List<String> chainNames) {
        NamedFilterList configured = new SimpleNamedFilterList(chainNames.toString());
        for(String chainName : chainNames) {
            configured.addAll(getChain(chainName));
        }
        return configured.proxy(original);
    }
    private void applyGlobalPropertiesIfNecessary(Filter filter) {
        applyLoginUrlIfNecessary(filter);
        applySuccessUrlIfNecessary(filter);
        applyUnauthorizedUrlIfNecessary(filter);
    }
    private void applyLoginUrlIfNecessary(Filter filter) {
        //請(qǐng)參考源碼
    }
    private void applySuccessUrlIfNecessary(Filter filter) {
        //請(qǐng)參考源碼
    }
    private void applyUnauthorizedUrlIfNecessary(Filter filter) {
        //請(qǐng)參考源碼
    }
}?
    

1、CustomDefaultFilterChainManager:調(diào)用其構(gòu)造器時(shí),會(huì)自動(dòng)注冊(cè)默認(rèn)的攔截器;

2、loginUrl、successUrl、unauthorizedUrl:分別對(duì)應(yīng)登錄地址、登錄成功后默認(rèn)跳轉(zhuǎn)地址、未授權(quán)跳轉(zhuǎn)地址,用于給相應(yīng)攔截器的;

3、filterChainDefinitionMap:用于存儲(chǔ)如ShiroFilterFactoryBean在配置文件中配置的攔截器鏈定義,即可以認(rèn)為是默認(rèn)的靜態(tài)攔截器鏈;會(huì)自動(dòng)與數(shù)據(jù)庫(kù)中加載的合并;

4、setDefaultFilterChainDefinitions:解析配置文件中傳入的字符串?dāng)r截器鏈配置,解析為相應(yīng)的攔截器鏈;

5、setCustomFilters:注冊(cè)我們自定義的攔截器;如ShiroFilterFactoryBean的filters屬性;

6、init:初始化方法,Spring容器啟動(dòng)時(shí)會(huì)調(diào)用,首先其會(huì)自動(dòng)給相應(yīng)的攔截器設(shè)置如loginUrl、successUrl、unauthorizedUrl;其次根據(jù)filterChainDefinitionMap構(gòu)建默認(rèn)的攔截器鏈;

7、initFilter:此處我們忽略實(shí)現(xiàn)initFilter,因?yàn)榻唤ospring管理了,所以Filter的相關(guān)配置會(huì)在Spring配置中完成;

8、proxy:組合多個(gè)攔截器鏈為一個(gè)生成一個(gè)新的FilterChain代理。

?

Web 層控制器?

請(qǐng)參考com.github.zhangkaitao.shiro.chapter19.web.controller包,相對(duì)于第十六章添加了UrlFilterController用于UrlFilter的維護(hù)。另外,移除了控制器方法上的權(quán)限注解,而是使用動(dòng)態(tài)URL攔截進(jìn)行控制。

?

Spring 配置—— spring-config-shiro.xml ???

      <bean id="filterChainManager" 
    class="com.github.zhangkaitao.shiro.spring.CustomDefaultFilterChainManager">
    <property name="loginUrl" value="/login"/>
    <property name="successUrl" value="/"/>
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
    <property name="customFilters">
        <util:map>
            <entry key="authc" value-ref="formAuthenticationFilter"/>
            <entry key="sysUser" value-ref="sysUserFilter"/>
        </util:map>
    </property>
    <property name="defaultFilterChainDefinitions">
        <value>
            /login = authc
            /logout = logout
            /unauthorized.jsp = authc
            /** = user,sysUser
        </value>
    </property>
</bean>?
    

filterChainManager是我們自定義的CustomDefaultFilterChainManager,注冊(cè)相應(yīng)的攔截器及默認(rèn)的攔截器鏈。?

      <bean id="filterChainResolver" 
    class="com.github.zhangkaitao.shiro.spring.CustomPathMatchingFilterChainResolver">
    <property name="customDefaultFilterChainManager" ref="filterChainManager"/>
</bean>?
    

filterChainResolver是自定義的CustomPathMatchingFilterChainResolver,使用上邊的filterChainManager進(jìn)行攔截器鏈的管理。?

      <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
</bean>?
    

shiroFilter不再定義filters及filterChainDefinitions,而是交給了filterChainManager進(jìn)行完成。?

      <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="shiroFilter"/>
    <property name="targetMethod" value="setFilterChainResolver"/>
    <property name="arguments" ref="filterChainResolver"/>
</bean>?
    

最后把filterChainResolver注冊(cè)給shiroFilter,其使用它進(jìn)行動(dòng)態(tài)URL權(quán)限控制。

?

其他配置和第十六章一樣,請(qǐng)參考第十六章。

?

測(cè)試

1、首先執(zhí)行shiro-data.sql初始化數(shù)據(jù)。

2、然后再URL管理中新增如下數(shù)據(jù):?

第十九章 動(dòng)態(tài)URL權(quán)限控制——《跟我學(xué)Shiro》

3、訪問(wèn)http://localhost:8080/chapter19/user時(shí)要求用戶擁有aa角色,此時(shí)是沒(méi)有的所以會(huì)跳轉(zhuǎn)到未授權(quán)頁(yè)面;

4、添加aa角色然后授權(quán)給用戶,此時(shí)就有權(quán)限訪問(wèn)http://localhost:8080/chapter19/user。

?

實(shí)際項(xiàng)目可以在此基礎(chǔ)上進(jìn)行擴(kuò)展。

?

? ? ?

?

?

示例源代碼: https://github.com/zhangkaitao/shiro-example ;可加群 231889722?探討Spring/Shiro技術(shù)。

? ? ? ??

??

第十九章 動(dòng)態(tài)URL權(quán)限控制——《跟我學(xué)Shiro》


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 江北区| 五河县| 高清| 佛山市| 兴化市| 汝阳县| 平舆县| 武汉市| 喀喇| 东光县| 上犹县| 江山市| 秦皇岛市| 诸城市| 东宁县| 赫章县| 大埔县| 呼和浩特市| 镇平县| 阿克陶县| 彭州市| 孝义市| 冕宁县| 奉贤区| 台江县| 长白| 磐石市| 志丹县| 瑞安市| 莱阳市| 兴安县| 宝兴县| 布尔津县| 民勤县| 任丘市| 新平| 江津市| 神木县| 彭州市| 广安市| 乐业县|