鲁春利的工做笔记,好记性不如烂笔头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中的枚举拦截器: