AccessControlFilter是shiro-web模块当中比较重要的类,所有的拦截器都继承此类,分析此类源码对应使用其它的filter有很大的帮助。

下图是shiro-web 提供的filter,每种filter都对应了不同的权限拦截规则,本文主要分析AccessControlFilter。

Shiro系列教程 AccessControlFilter源码分析_shiro


AccessControlFilter的继承关系

Shiro系列教程 AccessControlFilter源码分析_shiro_02


ServletContextSupport 源码比较简单不做分析。


AbstractFilter源码分析


public abstract class AbstractFilter extends ServletContextSupport implements Filter {

private static transient final Logger log = LoggerFactory.getLogger(AbstractFilter.class);


protected FilterConfig filterConfig;


public FilterConfig getFilterConfig() {
return filterConfig;
}


public void setFilterConfig(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
setServletContext(filterConfig.getServletContext());
}

//获取filter的配置参数
protected String getInitParam(String paramName) {
FilterConfig config = getFilterConfig();
if (config != null) {
return StringUtils.clean(config.getInitParameter(paramName));
}
return null;
}

//filter初始化方法
public final void init(FilterConfig filterConfig) throws ServletException {
//设置配置信息对象FilterConfig
setFilterConfig(filterConfig);
try {
//初始化完毕后回调
onFilterConfigSet();
} catch (Exception e) {
if (e instanceof ServletException) {
throw (ServletException) e;
} else {
if (log.isErrorEnabled()) {
log.error("Unable to start Filter: [" + e.getMessage() + "].", e);
}
throw new ServletException(e);
}
}
}


protected void onFilterConfigSet() throws Exception {
}

}

NameableFilter源码分析





public abstract class NameableFilter extends AbstractFilter implements Nameable {


//给filter加了名字
private String name;

//获取名字
protected String getName() {
//如果为空则返回web.xml filter-name的值
if (this.name == null) {
FilterConfig config = getFilterConfig();
if (config != null) {
this.name = config.getFilterName();
}
}

return this.name;
}

public void setName(String name) {
this.name = name;
}

protected StringBuilder toStringBuilder() {
String name = getName();
if (name == null) {
return super.toStringBuilder();
} else {
StringBuilder sb = new StringBuilder();
sb.append(name);
return sb;
}
}

}






OncePerRequestFilter类源码分析


public abstract class OncePerRequestFilter extends NameableFilter {

//后缀
public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";

//是允许此filter处理
private boolean enabled = true; //most filters wish to execute when configured, so default to true

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}


public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//获得一个key用来标识当前request(请求)已经执行过一次doFilterInternal
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();

//判断是否已经执行过doFilterInternal
if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName());
//跳过当前filter的处理,直接走后续逻辑(说白了就是脱离shiro的处理)
filterChain.doFilter(request, response);
} else //noinspection deprecation
//判断是否允许执行doFilterInternal 默认是允许的,不走下面的逻辑
if (/* added in 1.2: */ !isEnabled(request, response) ||
/* retain backwards compatibility: */ shouldNotFilter(request) ) {
log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.",
getName());
filterChain.doFilter(request, response);
} else {
// Do invoke this filter...

log.trace("Filter '{}' not yet executed. Executing now.", getName());
//设置标识
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

//执行doFilterInternal逻辑,该方法由子类重写来执行具体的shiro逻辑
try {
doFilterInternal(request, response, filterChain);
} finally {
// Once the request has finished, we're done and we don't
// need to mark as 'already filtered' any more.
request.removeAttribute(alreadyFilteredAttributeName);
}
}
}


@SuppressWarnings({"UnusedParameters"})
protected boolean isEnabled(ServletRequest request, ServletResponse response) throws ServletException, IOException {
return isEnabled();
}

protected String getAlreadyFilteredAttributeName() {
String name = getName();
if (name == null) {
name = getClass().getName();
}
//名称加上后缀
return name + ALREADY_FILTERED_SUFFIX;
}


@Deprecated
@SuppressWarnings({"UnusedDeclaration"})
protected boolean shouldNotFilter(ServletRequest request) throws ServletException {
return false;
}

//由子类重写,这个方法最多被执行一次
protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException;
}






AdviceFilter类源码fe


//一个AOP的类,在执行chain.doFilter(request, response); 添加了前置 后置 最终三个环绕方法.
public abstract class AdviceFilter extends OncePerRequestFilter {


//AOP方法
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
return true;
}


//AOP方法
@SuppressWarnings({"UnusedDeclaration"})
protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
}


AOP方法
@SuppressWarnings({"UnusedDeclaration"})
public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
}


protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception {
chain.doFilter(request, response);
}

public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {

Exception exception = null;

try {

//执行前置AOP方法 根据返回值continueChain觉得是否继续执行chain.doFilter(request, response);
boolean continueChain = preHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]");
}

//如果preHandle返回true则执行
if (continueChain) {
executeChain(request, response, chain);
}

//如果不出异常则执行postHandle
postHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Successfully invoked postHandle method");
}

} catch (Exception e) {
exception = e;
} finally {
//异常与否都在最后执行
cleanup(request, response, exception);
}
}

protected void cleanup(ServletRequest request, ServletResponse response, Exception existing)
throws ServletException, IOException {
Exception exception = existing;
try {
//AOP方法
afterCompletion(request, response, exception);
if (log.isTraceEnabled()) {
log.trace("Successfully invoked afterCompletion method.");
}
} catch (Exception e) {
if (exception == null) {
exception = e;
} else {
log.debug("afterCompletion implementation threw an exception. This will be ignored to " +
"allow the original source exception to be propagated.", e);
}
}
//如果executeChain方法出现异常则在这里抛出
if (exception != null) {
if (exception instanceof ServletException) {
throw (ServletException) exception;
} else if (exception instanceof IOException) {
throw (IOException) exception;
} else {
if (log.isDebugEnabled()) {
String msg = "Filter execution resulted in an unexpected Exception " +
"(not IOException or ServletException as the Filter API recommends). " +
"Wrapping in ServletException and propagating.";
log.debug(msg);
}
throw new ServletException(exception);
}
}
}
}


PathMatchingFilter类源码分析



public abstract class PathMatchingFilter extends AdviceFilter implements PathConfigProcessor {

//路径匹配器
//匹配规则http://blog.csdn.net/nimasike/article/details/70739982
protected PatternMatcher pathMatcher = new AntPathMatcher();

//这里存的内容是 例如:
// /login.jsp [anon]
// /index.jsp [bar, baz]
protected Map<String, Object> appliedPaths = new LinkedHashMap<String, Object>();

//假设你的配置是 /user/** = user, roles[admin, foo]
//如果这个类是roles path为/user/** config为admin, foo
//如果这个类是user path为/user/** config为null
public Filter processPathConfig(String path, String config) {
String[] values = null;
if (config != null) {
values = split(config);
}

this.appliedPaths.put(path, values);
return this;
}


//获得请求路径
//假设请求http://localhost/index.jsp?id=18
//则返回值为/index.jsp
protected String getPathWithinApplication(ServletRequest request) {
return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
}

//请求路径与path匹配
protected boolean pathsMatch(String path, ServletRequest request) {
String requestURI = getPathWithinApplication(request);
log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, requestURI);
return pathsMatch(path, requestURI);
}


protected boolean pathsMatch(String pattern, String path) {
return pathMatcher.matches(pattern, path);
}


//这个方法返回false则请求会被中断
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
if (log.isTraceEnabled()) {
log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately.");
}
return true;
}

//首先进行路径匹配
for (String path : this.appliedPaths.keySet()) {
// If the path does match, then pass on to the subclass implementation for specific checks
//(first match 'wins'):
if (pathsMatch(path, request)) {
//匹配到路径执行isFilterChainContinued
log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path);
Object config = this.appliedPaths.get(path);
return isFilterChainContinued(request, response, path, config);
}
}

//如果没有匹配允许执行
return true;
}


@SuppressWarnings({"JavaDoc"})
private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
String path, Object pathConfig) throws Exception {

//这里判断是否允许shiro执行 默认允许
if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
if (log.isTraceEnabled()) {
log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}]. " +
"Delegating to subclass implementation for 'onPreHandle' check.",
new Object[]{getName(), path, pathConfig});
}
//则执行onPreHandle,根据返回值来决定是否继续允许执行后续的filter
//所有shiro-fiter都会重写此方法,如果返回false 则请求会被中断
return onPreHandle(request, response, pathConfig);
}

if (log.isTraceEnabled()) {
log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}]. " +
"The next element in the FilterChain will be called immediately.",
new Object[]{getName(), path, pathConfig});
}

return true;
}


protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return true;
}


@SuppressWarnings({"UnusedParameters"})
protected boolean isEnabled(ServletRequest request, ServletResponse response, String path, Object mappedValue)
throws Exception {
return isEnabled(request, response);
}
}



AccessControlFilter类源码

public abstract class AccessControlFilter extends PathMatchingFilter {

//默认登录页
public static final String DEFAULT_LOGIN_URL = "/login.jsp";

public static final String GET_METHOD = "GET";

public static final String POST_METHOD = "POST";

private String loginUrl = DEFAULT_LOGIN_URL;

public String getLoginUrl() {
return loginUrl;
}

public void setLoginUrl(String loginUrl) {
this.loginUrl = loginUrl;
}

protected Subject getSubject(ServletRequest request, ServletResponse response) {
return SecurityUtils.getSubject();
}

//子类根据业务规则觉得是否中断请求
protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;

protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return onAccessDenied(request, response);
}

protected abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;

//这里调用的isAccessAllowed
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}

protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
return pathsMatch(getLoginUrl(), request);
}

//保存请求路径调转到登录页面
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
saveRequest(request);
redirectToLogin(request, response);
}

//保存请求路径
protected void saveRequest(ServletRequest request) {
WebUtils.saveRequest(request);
}

//跳转到登录页
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
String loginUrl = getLoginUrl();
WebUtils.issueRedirect(request, response, loginUrl);
}

}



技术交流群:212320390