上篇分析了加载的过程
这次我们来看下认证流程,上篇我们知道最终返回的对象是SpringShiroFilter,因此所有的请求拦截都是走它。
private static final class SpringShiroFilter extends AbstractShiroFilter {
protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
super();
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
}
setSecurityManager(webSecurityManager);
if (resolver != null) {
setFilterChainResolver(resolver);
}
}
}
按照常识,我们去找doFilter方法。
SpringShiroFilter extends AbstractShiroFilter extends OncePerRequestFilter
public abstract class OncePerRequestFilter extends NameableFilter {
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName());
filterChain.doFilter(request, response);
} else //noinspection deprecation
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);
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);
}
}
}
protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException;
}
在OncePerRequestFilter内找到了入口,真正的功能代码是在doFilterInternal内。而doFilterInternal方法定义在子类
public abstract class AbstractShiroFilter extends OncePerRequestFilter {
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {
Throwable t = null;
try {
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
//获取shiro中的用户,从这里可以看出,每一次拦截,都会创建出一个subject
final Subject subject = createSubject(request, response);
//subject调用过滤器链
subject.execute(new Callable() {
public Object call() throws Exception {
// 更新会话最后访问时间,用于计算会话超时
updateSessionLastAccessTime(request, response);
// 执行过滤器链
executeChain(request, response, chain);
return null;
}
});
} catch (ExecutionException ex) {
t = ex.getCause();
}
//...
}
}
doFilterInternal方法内有个重要的部分,就是subject的创建,稍微追踪下发现是在DefaultWebSubjectFactory创建出来的。
public class DefaultWebSubjectFactory extends DefaultSubjectFactory {
public DefaultWebSubjectFactory() {
super();
}
public Subject createSubject(SubjectContext context) {
if (!(context instanceof WebSubjectContext)) {
return super.createSubject(context);
}
WebSubjectContext wsc = (WebSubjectContext) context;
SecurityManager securityManager = wsc.resolveSecurityManager();
Session session = wsc.resolveSession();
boolean sessionEnabled = wsc.isSessionCreationEnabled();
//principals信息
PrincipalCollection principals = wsc.resolvePrincipals();
//是否认证过,第一次这边明细是false
boolean authenticated = wsc.resolveAuthenticated();
String host = wsc.resolveHost();
ServletRequest request = wsc.resolveServletRequest();
ServletResponse response = wsc.resolveServletResponse();
return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,
request, response, securityManager);
}
}
调用subject的executeChain方法执行过滤器链。
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
throws IOException, ServletException {
FilterChain chain = getExecutionChain(request, response, origChain);
//chain = authc --> FormAuthenticationFilter
chain.doFilter(request, response);
}
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
FilterChain chain = origChain;
//加载过程中第3步里组装的PathMatchingFilterChainResolver
FilterChainResolver resolver = getFilterChainResolver();
if (resolver == null) {
log.debug("No FilterChainResolver configured. Returning original FilterChain.");
return origChain;
}
//resolved = authc --> FormAuthenticationFilter
FilterChain resolved = resolver.getChain(request, response, origChain);
if (resolved != null) {
log.trace("Resolved a configured FilterChain for the current request.");
chain = resolved;
} else {
log.trace("No FilterChain configured for the current request. Using the default.");
}
return chain;
}
现在我们目标明确了,我们需要找的就是 authc 对应的 FormAuthenticationFilter的执行流程,这个就是认证流程。
翻一下它的继承体系,我们需要找到doFilter在哪儿,找了一下发现和加载那边是同一个顶级父类。
FormAuthenticationFilter
extends AuthenticatingFilter
extends AuthenticationFilter
extends AccessControlFilter
extends PathMatchingFilter
extends AdviceFilter
extends OncePerRequestFilter
OncePerRequestFilter内的doFilter,这边代码不贴了,但是这个时候的子类和加载那边不同了,变成了AdviceFilter。
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
Exception exception=null;
// 1. 执行preHandle
boolean continueChain=preHandle(request,response);
//...
if(continueChain){
executeChain(request,response,chain);
}
// 2. 执行postHandle
postHandle(request,response);
//...
}
preHandle 方法是留给子类实现的。我们去找了下在子类PathMatchingFilter里,最终调用onPreHandle方法,也是留给子类实现的。
// 执行preHandle
public abstract class PathMatchingFilter extends AdviceFilter implements PathConfigProcessor {
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
//....
for (String path : this.appliedPaths.keySet()) {
if (pathsMatch(path, request)) {
// 根据配置,访问URL:"/login.do"时,会匹配上FormAuthenticationFilter,
// 而FormAuthenticationFilter继承自PathMatchingFilter,所以返回true
//....
return isFilterChainContinued(request, response, path, config);
}
}
//如果没有任何满足要求,直接放行
return true;
}
private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
String path, Object pathConfig) throws Exception {
if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
//...
return onPreHandle(request, response, pathConfig);
}
//...
}
}
onPreHandle方法在AccessControlFilter内,执行了两个判断,一个是判断当前请求是否被允许访问,一个是是否有权限。
//onPreHandle 方法也是留给子类实现的,在AccessControlFilter内
public abstract class AccessControlFilter extends PathMatchingFilter {
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}
}
第一个判断:
//isAccessAllowed的实现类
public abstract class AuthenticationFilter extends AccessControlFilter {
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = getSubject(request, response);
//显然这边是false,因为第一次肯定是没有认证过的
return subject.isAuthenticated();
}
}
第二个判断:
//onAccessDenied的实现类就是我们一开始关注的FormAuthenticationFilter
public class FormAuthenticationFilter extends AuthenticatingFilter {
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response)) {
//完全全新的第一次请求肯定不是login请求,因为这个时候还没有输入用户名密码
} else {
//...
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
}
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
saveRequest(request);
//重定向到login.jsp 页面
redirectToLogin(request, response);
}
这个时候应该整个认证的过程就结束了,接下去就是登陆流程了,在上篇XML配置的基础上,所有的请求都要走认证流程,只有第一次会执行两次判断,一次是isAccessAllowed,一次是onAccessDenied。如果你成功登陆进去且在有效期内,那么第二个判断不会再次执行。