<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                用過 Spring Security 的朋友應該比較熟悉對 URL 進行全局的權限控制,即訪問 URL 時進行權限匹配;如果沒有權限直接跳到相應的錯誤頁面。Shiro 也支持類似的機制,不過需要稍微改造下來滿足實際需求。不過在 Shiro 中,更多的是通過 AOP 進行分散的權限控制,即方法級別的;而通過 URL 進行權限控制是一種集中的權限控制。本章將介紹如何在 Shiro 中完成動態 URL 權限控制。 本章代碼基于《第十六章 綜合實例》,請先了解相關數據模型及基本流程后再學習本章。 **表及數據 SQL** 請運行 shiro-example-chapter19/sql/ shiro-schema.sql 表結構 請運行 shiro-example-chapter19/sql/ shiro-schema.sql 數據 **實體** 具體請參考 com.github.zhangkaitao.shiro.chapter19 包下的實體。 ``` java public class UrlFilter implements Serializable { private Long id; private String name; //url名稱/描述 private String url; //地址 private String roles; //所需要的角色,可省略 private String permissions; //所需要的權限,可省略 } ``` 表示攔截的 URL 和角色 / 權限之間的關系,多個角色 / 權限之間通過逗號分隔,此處還可以擴展其他的關系,另外可以加如 available 屬性表示是否開啟該攔截。 **DAO** 具體請參考 com.github.zhangkaitao.shiro.chapter19.dao 包下的 DAO 接口及實現。 **Service** 具體請參考 com.github.zhangkaitao.shiro.chapter19.service 包下的 Service 接口及實現。 ``` java 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 攔截的增刪改查實現。 ``` java @Service public class UrlFilterServiceImpl implements UrlFilterService { @Autowired private ShiroFilerChainManager shiroFilerChainManager; @Override public UrlFilter createUrlFilter(UrlFilter urlFilter) { urlFilterDao.createUrlFilter(urlFilter); initFilterChain(); return urlFilter; } //其他方法請參考源碼 @PostConstruct public void initFilterChain() { shiroFilerChainManager.initFilterChains(findAll()); } } ``` UrlFilterServiceImpl 在進行新增、修改、刪除時會調用 initFilterChain 來重新初始化 Shiro 的 URL 攔截器鏈,即同步數據庫中的 URL 攔截器定義到 Shiro 中。此處也要注意如果直接修改數據庫是不會起作用的,因為只要調用這幾個 Service 方法時才同步。另外當容器啟動時會自動回調 initFilterChain 來完成容器啟動后的 URL 攔截器的注冊。 **ShiroFilerChainManager** ``` java @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并注冊默認的 filterChainManager.getFilterChains().clear(); if(defaultFilterChains != null) { filterChainManager.getFilterChains().putAll(defaultFilterChains); } //2、循環URL Filter 注冊filter chain for (UrlFilter urlFilter : urlFilters) { String url = urlFilter.getUrl(); //注冊roles filter if (!StringUtils.isEmpty(urlFilter.getRoles())) { filterChainManager.addToChain(url, "roles", urlFilter.getRoles()); } //注冊perms filter if (!StringUtils.isEmpty(urlFilter.getPermissions())) { filterChainManager.addToChain(url, "perms", urlFilter.getPermissions()); } } } } ``` 1、init:Spring 容器啟動時會調用 init 方法把在 spring 配置文件中配置的默認攔截器保存下來,之后會自動與數據庫中的配置進行合并。 2、initFilterChains:UrlFilterServiceImpl 會在 Spring 容器啟動或進行增刪改 UrlFilter 時進行注冊 URL 攔截器到 Shiro。 攔截器及攔截器鏈知識請參考《第八章 攔截器機制》,此處再介紹下 Shiro 攔截器的流程: ``` java AbstractShiroFilter //如ShiroFilter/ SpringShiroFilter都繼承該Filter doFilter //Filter的doFilter doFilterInternal //轉調doFilterInternal executeChain(request, response, chain) //執行攔截器鏈 FilterChain chain = getExecutionChain(request, response, origChain) //使用原始攔截器鏈獲取新的攔截器鏈 chain.doFilter(request, response) //執行新組裝的攔截器鏈 getExecutionChain(request, response, origChain) //獲取攔截器鏈流程 FilterChainResolver resolver = getFilterChainResolver(); //獲取相應的FilterChainResolver FilterChain resolved = resolver.getChain(request, response, origChain); //通過FilterChainResolver根據當前請求解析到新的FilterChain攔截器鏈 ``` 默認情況下如使用 ShiroFilterFactoryBean 創建 shiroFilter 時,默認使用 PathMatchingFilterChainResolver 進行解析,而它默認是根據當前請求的 URL 獲取相應的攔截器鏈,使用 Ant 模式進行 URL 匹配;默認使用 DefaultFilterChainManager 進行攔截器鏈的管理。 PathMatchingFilterChainResolver 默認流程: ``` java public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) { //1、首先獲取攔截器鏈管理器 FilterChainManager filterChainManager = getFilterChainManager(); if (!filterChainManager.hasChains()) { return null; } //2、接著獲取當前請求的URL(不帶上下文) String requestURI = getPathWithinApplication(request); //3、循環攔截器管理器中的攔截器定義(攔截器鏈的名字就是URL模式) for (String pathPattern : filterChainManager.getChainNames()) { //4、如當前URL匹配攔截器名字(URL模式) if (pathMatches(pathPattern, requestURI)) { //5、返回該URL模式定義的攔截器鏈 return filterChainManager.proxy(originalChain, pathPattern); } } return null; } ``` 默認實現有點小問題: 如果多個攔截器鏈都匹配了當前請求 URL,那么只返回第一個找到的攔截器鏈;后續我們可以修改此處的代碼,將多個匹配的攔截器鏈合并返回。 DefaultFilterChainManager 內部使用 Map 來管理 URL 模式 - 攔截器鏈的關系;也就是說相同的 URL 模式只能定義一個攔截器鏈,不能重復定義;而且如果多個攔截器鏈都匹配時是無序的(因為使用 map.keySet() 獲取攔截器鏈的名字,即 URL 模式)。 FilterChainManager 接口: ``` java public interface FilterChainManager { Map<String, Filter> getFilters(); //得到注冊的攔截器 void addFilter(String name, Filter filter); //注冊攔截器 void addFilter(String name, Filter filter, boolean init); //注冊攔截器 void createChain(String chainName, String chainDefinition); //根據攔截器鏈定義創建攔截器鏈 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); //使用指定的攔截器鏈代理原始攔截器鏈 } ``` 此接口主要三個功能:注冊攔截器,注冊攔截器鏈,對原始攔截器鏈生成代理之后的攔截器鏈,比如 ``` xml <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 就是攔截器名字列表。 之前說過默認的 PathMatchingFilterChainResolver 和 DefaultFilterChainManager 不能滿足我們的需求,我們稍微擴展了一下: **CustomPathMatchingFilterChainResolver ** ``` java 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); } } ``` 和默認的 PathMatchingFilterChainResolver 區別是,此處得到所有匹配的攔截器鏈,然后通過調用 CustomDefaultFilterChainManager.proxy(originalChain, chainNames) 進行合并后代理。 **CustomDefaultFilterChainManager** ``` java 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) { //請參考源碼 } private void applySuccessUrlIfNecessary(Filter filter) { //請參考源碼 } private void applyUnauthorizedUrlIfNecessary(Filter filter) { //請參考源碼 } } ``` 1. CustomDefaultFilterChainManager:調用其構造器時,會自動注冊默認的攔截器; 2. loginUrl、successUrl、unauthorizedUrl:分別對應登錄地址、登錄成功后默認跳轉地址、未授權跳轉地址,用于給相應攔截器的; 3. filterChainDefinitionMap:用于存儲如 ShiroFilterFactoryBean 在配置文件中配置的攔截器鏈定義,即可以認為是默認的靜態攔截器鏈;會自動與數據庫中加載的合并; 4. setDefaultFilterChainDefinitions:解析配置文件中傳入的字符串攔截器鏈配置,解析為相應的攔截器鏈; 5. setCustomFilters:注冊我們自定義的攔截器;如 ShiroFilterFactoryBean 的 filters 屬性; 6. init:初始化方法,Spring 容器啟動時會調用,首先其會自動給相應的攔截器設置如 loginUrl、successUrl、unauthorizedUrl;其次根據 filterChainDefinitionMap 構建默認的攔截器鏈; 7. initFilter:此處我們忽略實現 initFilter,因為交給 spring 管理了,所以 Filter 的相關配置會在 Spring 配置中完成; 8. proxy:組合多個攔截器鏈為一個生成一個新的 FilterChain 代理。 **Web 層控制器** 請參考 com.github.zhangkaitao.shiro.chapter19.web.controller 包,相對于第十六章添加了 UrlFilterController 用于 UrlFilter 的維護。另外,移除了控制器方法上的權限注解,而是使用動態 URL 攔截進行控制。 **Spring 配置——spring-config-shiro.xml** ``` 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,注冊相應的攔截器及默認的攔截器鏈。 ``` xml <bean id="filterChainResolver" class="com.github.zhangkaitao.shiro.spring.CustomPathMatchingFilterChainResolver"> <property name="customDefaultFilterChainManager" ref="filterChainManager"/> </bean> ``` filterChainResolver 是自定義的 CustomPathMatchingFilterChainResolver,使用上邊的 filterChainManager 進行攔截器鏈的管理。 ``` xml <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> </bean> ``` shiroFilter 不再定義 filters 及 filterChainDefinitions,而是交給了 filterChainManager 進行完成。 ``` xml <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 注冊給 shiroFilter,其使用它進行動態 URL 權限控制。 其他配置和第十六章一樣,請參考第十六章。 **測試** 1、首先執行 shiro-data.sql 初始化數據。 2、然后再 URL 管理中新增如下數據: ![](https://box.kancloud.cn/b95fd180291911932e7ff4696919fe7a_336x202.png) 3、訪問 `http://localhost:8080/chapter19/user` 時要求用戶擁有 aa 角色,此時是沒有的所以會跳轉到未授權頁面; 4、添加 aa 角色然后授權給用戶,此時就有權限訪問 `http://localhost:8080/chapter19/user`。 實際項目可以在此基礎上進行擴展。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看