Shiro源码----认证

上篇分析了加载的过程

shiro加载

这次我们来看下认证流程,上篇我们知道最终返回的对象是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。如果你成功登陆进去且在有效期内,那么第二个判断不会再次执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值