Apache Shiro学习笔记(六)FilterChain
鲁春利的工作笔记,好记性不如烂笔头Apache Shiro学习笔记(七)IniWebEnvironment
Shiro FilterChain
Shiro 对Servlet 容器的FilterChain 进行了代理,即ShiroFilter 在继续Servlet 容器的Filter链的执行之前,通过ProxiedFilterChain 对Servlet 容器的FilterChain 进行了代理;即先走Shiro 自己的Filter 体系,然后才会委托给Servlet 容器的FilterChain 进行Servlet 容器级别的Filter链执行;Shiro的ProxiedFilterChain执行流程:
1、先执行Shiro自己的Filter链;
2、再执行Servlet容器的Filter链(即原始的Filter)。
ProxiedFilterChain 是通过FilterChainResolver 根据配置文件中部分是否与请求的URL是否匹配解析得到的。
[*] FilterChainResolver
package org.apache.shiro.web.filter.mgt;
public interface FilterChainResolver {
FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);
} Shiro 内部提供了一个路径匹配的FilterChainResolver 实现:PathMatchingFilterChainResolver:
其根据中配置的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 filters; //pool of filters available for creating chains
private Map filterChains; //key: chain name, value: chain
public DefaultFilterChainManager() {
this.filters = new LinkedHashMap();
this.filterChains = new LinkedHashMap();
addDefaultFilters(false);
}
public DefaultFilterChainManager(FilterConfig filterConfig) {
this.filters = new LinkedHashMap();
this.filterChains = new LinkedHashMap();
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 配置文件中的/部分并注册这些拦截器到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来完成,比如:
//1、创建FilterChainResolver
PathMatchingFilterChainResolver filterChainResolver = new PathMatchingFilterChainResolver();
//2、创建FilterChainManager
DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager();
//3、注册Filter
for(DefaultFilter filter : DefaultFilter.values()) {
filterChainManager.addFilter(filter.name(), (Filter) ClassUtils.newInstance(filter.getFilterClass()));
}
//4、注册URL-Filter的映射关系
filterChainManager.addToChain("/login.jsp", "authc");
filterChainManager.addToChain("/unauthorized.jsp", "anon");
filterChainManager.addToChain("/**", "authc");
filterChainManager.addToChain("/**", "roles", "admin");
//5、设置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:
shiroEnvironmentClass
com.github.zhangkaitao.shiro.chapter8.web.env.MyIniWebEnvironment
自定义拦截器
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配置
formLogin=com.invicme.apps.shiro.filter.FormLoginFilter
/test.jsp=formLogin
/login.jsp=formLogin
默认拦截器
Shiro 内置了很多默认的拦截器,比如身份验证、授权等相关的。默认拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举拦截器:
页:
[1]