魯春利的工做筆記,好記性不如爛筆頭java
Apache Shiro學習筆記(七)IniWebEnvironment git
Shiro FilterChain
github
Shiro 對Servlet 容器的FilterChain 進行了代理,即ShiroFilter 在繼續Servlet 容器的Filter鏈的執行以前,經過ProxiedFilterChain 對Servlet 容器的FilterChain 進行了代理;即先走Shiro 本身的Filter 體系,而後纔會委託給Servlet 容器的FilterChain 進行Servlet 容器級別的Filter鏈執行;Shiro的ProxiedFilterChain執行流程:web
一、先執行Shiro本身的Filter鏈;apache
二、再執行Servlet容器的Filter鏈(即原始的Filter)。app
ProxiedFilterChain 是經過FilterChainResolver 根據配置文件中[urls]部分是否與請求的URL是否匹配解析獲得的。jsp
FilterChainResolveride
package org.apache.shiro.web.filter.mgt; public interface FilterChainResolver { FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain); }
Shiro 內部提供了一個路徑匹配的FilterChainResolver 實現:PathMatchingFilterChainResolver:post
其根據[urls]中配置的url 模式(默認Ant 風格)= 攔截器鏈和請求的url是否匹配來解析獲得配置的攔截器鏈的;而PathMatchingFilterChainResolver內部經過FilterChainManager維護着攔截器鏈,好比DefaultFilterChainManager實現維護着url 模式與攔截器鏈的關係。所以咱們能夠經過FilterChainManager 進行動態動態增長url模式與攔截器鏈的關係。學習
PathMatchingFilterChainResolver
package org.apache.shiro.web.filter.mgt; public class PathMatchingFilterChainResolver implements FilterChainResolver { private FilterChainManager filterChainManager; private PatternMatcher pathMatcher; public PathMatchingFilterChainResolver() { this.pathMatcher = new AntPathMatcher(); this.filterChainManager = new DefaultFilterChainManager(); } public PathMatchingFilterChainResolver(FilterConfig filterConfig) { this.pathMatcher = new AntPathMatcher(); this.filterChainManager = new DefaultFilterChainManager(filterConfig); } public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) { FilterChainManager filterChainManager = getFilterChainManager(); if (!filterChainManager.hasChains()) { return null; } String requestURI = getPathWithinApplication(request); //the 'chain names' in this implementation are actually path patterns defined by the user. We just use them //as the chain name for the FilterChainManager's requirements for (String pathPattern : filterChainManager.getChainNames()) { // If the path does match, then pass on to the subclass implementation for specific checks: if (pathMatches(pathPattern, requestURI)) { if (log.isTraceEnabled()) { log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "]. " + "Utilizing corresponding filter chain..."); } return filterChainManager.proxy(originalChain, pathPattern); } } return null; } }
DefaultFilterChainManager
package org.apache.shiro.web.filter.mgt; public class DefaultFilterChainManager implements FilterChainManager { private FilterConfig filterConfig; private Map<String, Filter> filters; //pool of filters available for creating chains private Map<String, NamedFilterList> filterChains; //key: chain name, value: chain public DefaultFilterChainManager() { this.filters = new LinkedHashMap<String, Filter>(); this.filterChains = new LinkedHashMap<String, NamedFilterList>(); addDefaultFilters(false); } public DefaultFilterChainManager(FilterConfig filterConfig) { this.filters = new LinkedHashMap<String, Filter>(); this.filterChains = new LinkedHashMap<String, NamedFilterList>(); setFilterConfig(filterConfig); addDefaultFilters(true); } protected void addDefaultFilters(boolean init) { for (DefaultFilter defaultFilter : DefaultFilter.values()) { addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false); } } }
DefaultFilterChainManager 會默認添加org.apache.shiro.web.filter.mgt.DefaultFilter 中聲明的
攔截器。
DefaultFilter
public enum DefaultFilter { anon(AnonymousFilter.class), authc(FormAuthenticationFilter.class), authcBasic(BasicHttpAuthenticationFilter.class), logout(LogoutFilter.class), noSessionCreation(NoSessionCreationFilter.class), perms(PermissionsAuthorizationFilter.class), port(PortFilter.class), rest(HttpMethodPermissionFilter.class), roles(RolesAuthorizationFilter.class), ssl(SslFilter.class), user(UserFilter.class); // 代碼略 }
若是要註冊自定義攔截器,IniSecurityManagerFactory/WebIniSecurityManagerFactory在啓動時會自動掃描ini 配置文件中的[filters]/[main]部分並註冊這些攔截器到DefaultFilterChainManager;且建立相應的url 模式與其攔截器關係鏈。
若是想自定義FilterChainResolver,能夠經過實現WebEnvironment接口完成:
package org.apache.shiro.web.env; public class IniWebEnvironment extends ResourceBasedWebEnvironment implements Initializable, Destroyable { protected FilterChainResolver createFilterChainResolver() { FilterChainResolver resolver = null; Ini ini = getIni(); if (!CollectionUtils.isEmpty(ini)) { //only create a resolver if the 'filters' or 'urls' sections are defined: Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS); Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS); if (!CollectionUtils.isEmpty(urls) || !CollectionUtils.isEmpty(filters)) { //either the urls section or the filters section was defined. Go ahead and create the resolver: IniFilterChainResolverFactory factory = new IniFilterChainResolverFactory(ini, this.objects); resolver = factory.getInstance(); } } return resolver; } }
MyIniWebEnvironment
public class MyIniWebEnvironment extends IniWebEnvironment { @Override protected FilterChainResolver createFilterChainResolver() { //在此處擴展本身的FilterChainResolver return super.createFilterChainResolver(); } }
若是覆蓋了IniWebEnvironment 默認的FilterChainResolver,須要本身來解析請求與FilterChain 之間的關係。若是想動態實現url-攔截器的註冊,就能夠經過實現此處的FilterChainResolver來完成,好比:
//一、建立FilterChainResolver PathMatchingFilterChainResolver filterChainResolver = new PathMatchingFilterChainResolver(); //二、建立FilterChainManager DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager(); //三、註冊Filter for(DefaultFilter filter : DefaultFilter.values()) { filterChainManager.addFilter(filter.name(), (Filter) ClassUtils.newInstance(filter.getFilterClass())); } //四、註冊URL-Filter的映射關係 filterChainManager.addToChain("/login.jsp", "authc"); filterChainManager.addToChain("/unauthorized.jsp", "anon"); filterChainManager.addToChain("/**", "authc"); filterChainManager.addToChain("/**", "roles", "admin"); //五、設置Filter的屬性 FormAuthenticationFilter authcFilter = (FormAuthenticationFilter)filterChainManager.getFilter("authc"); authcFilter.setLoginUrl("/login.jsp"); RolesAuthorizationFilter rolesFilter = (RolesAuthorizationFilter)filterChainManager.getFilter("roles"); rolesFilter.setUnauthorizedUrl("/unauthorized.jsp"); filterChainResolver.setFilterChainManager(filterChainManager); return filterChainResolver;
此處本身去實現註冊filter,及url 模式與filter 之間的映射關係。能夠經過定製FilterChainResolver或FilterChainManager來完成諸如動態URL匹配的實現。
而後再web.xml中進行以下配置Environment:
<context-param> <param-name>shiroEnvironmentClass</param-name> <param-value>com.github.zhangkaitao.shiro.chapter8.web.env.MyIniWebEnvironment</param-value> </context-param>
自定義攔截器
package com.invicme.apps.shiro.filter; import java.io.IOException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.web.filter.PathMatchingFilter; import org.apache.shiro.web.util.WebUtils; /** * * @author lucl * 自定義表單攔截器 * */ public class FormLoginFilter extends PathMatchingFilter { private String loginUrl = "/login.jsp"; private String successUrl = "/"; @Override protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { if (SecurityUtils.getSubject().isAuthenticated()) { return true;// 已經登陸過 } HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if (isLoginRequest(req)) { if ("post".equalsIgnoreCase(req.getMethod())) {// form表單提交 boolean loginSuccess = login(req); // 登陸 if (loginSuccess) { return redirectToSuccessUrl(req, resp); } } return true;// 繼續過濾器鏈 } else {// 保存當前地址並重定向到登陸界面 saveRequestAndRedirectToLogin(req, resp); return false; } } private boolean redirectToSuccessUrl(HttpServletRequest req, HttpServletResponse resp) throws IOException { WebUtils.redirectToSavedRequest(req, resp, successUrl); return false; } private void saveRequestAndRedirectToLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException { WebUtils.saveRequest(req); WebUtils.issueRedirect(req, resp, loginUrl); } private boolean login(HttpServletRequest req) { String username = req.getParameter("username"); String password = req.getParameter("password"); try { SecurityUtils.getSubject().login( new UsernamePasswordToken(username, password)); } catch (Exception e) { req.setAttribute("shiroLoginFailure", e.getClass()); return false; } return true; } private boolean isLoginRequest(HttpServletRequest req) { return pathsMatch(loginUrl, WebUtils.getPathWithinApplication(req)); } }
onPreHandle主要流程:
shiro.ini配置
[filters] formLogin=com.invicme.apps.shiro.filter.FormLoginFilter [urls] /test.jsp=formLogin /login.jsp=formLogin
默認攔截器
Shiro 內置了不少默認的攔截器,好比身份驗證、受權等相關的。默認攔截器能夠參考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚舉攔截器: