SpringSecurity 源码解读(一)

来源:互联网 发布:php pc软件开发 编辑:程序博客网 时间:2024/06/06 13:19
由于读官方文档有些技术实在是不能用,所以我开始阅读源代码来进行学习,先从各种项目都会使用到认证拦截器开始
注意,由于笔者还没有对整个SpringSecurity完全掌握,所以只能对常用的流程所用到的代码进行解读,不过肯定是绝大部分的代码

前言:由于这个类太大了,所以这里先叙述常用的部分,内部类等其他不常用的后面会补上,建议读者读前要先对SpringSecurity的运行流程有认识.


小结:1.交给实现类进行认证    2.根据认证结果选择处理器
public abstract class AbstractAuthenticationProcessFilter extends GenericFilterBean implements
        ApplicationEventPublisherAware, MessageSourceAware{
public void doFilter(ServletRequest req,ServletResponse res,FilterChain chain) throws IOException,ServletException{
    HttpServletRequest request = (ServletRequest) req;
    HttpServletResponse response = (ServletResponse) res;
    //requeireAuthenetication是这里的一个内部类对象,代码在下面再进行分析,这里可以先理解为判断这个请求是否需要认证,不需要则执行下一个拦截器
    if(!requiresAuthentication(request,response){        
        chain.doFilter(request,response);
        return;
    }
    if(logger.isDebugEnabled()){
        logger.debug("Request is to process authentication");
    }

    //定义一个用于认证的对象,注意,Authentication只是一个接口,它的类型由这个类的实现类决定
    Authentication authResult;
    try{
        //这个方法是抽象方法,由实现类实现,默认实现类是UsenamePasswordAnthenticationFilter
        //这个方法完成之后,认证对象就已经完成了认证。
        authResult = attemptAuthentication(request,response);
        if(authResult == null){
            return;
        }
        //sessionStrategy是NullSessionStrategy的对象,实现类什么都不做,是一个空方法,可以通过setter对SessionStrategy对象进行修改
        sessionStrategy.onAuthentication(authResult,request,response);
    }catch(InternalAuthenticationServiceException failed){
        logger.error("An internal error occurred while trying to anthenticate the user.",failed);
        //具体方法在下面,但是不推荐看,涉及的东西太多了
        unsuccessfulAnthentication(request,response,failed);
        return;
    }catch(AuthenticationException failed){
        //这是用户自己输入错误,不用写日志
        unsuccessfulAnthentication(request,response,failed);
        return;
    }
    //continueChainBeforeSuccessfulAnthentication是谁控制的还不清楚,后面来填坑,默认是false
    if(continueChainBeforeSuccessfulAnthentication){
        chain.doFilter(request,response);
    }
    //这里莫名其妙,successfulAuthentication有个方法,四个参数和三个参数的,四个参数就是调用没有chain参数的三个参数的方法..子类可以覆盖
    successfulAuthentication(request,response,chain,authResult)
}


//四个参数的省略,就是调用只有三个参数的
protected void successfulAuthentication(HttpServletRequest request,HttpServletResponse response,Authentication authResult) throws IOException,ServletException{
        if (logger.isDebugEnabled()) {
            logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
        }
        //哈哈
        SecurityContextHolder.getContext().setAuthentication(authResult);
        //这个还要根据remeberMeService中的策略来进行,后面再说
        rememberMeServices.loginSuccess(request, response, authResult);
        //实现类太多了,不知道是哪个,挖个坑吧,反正不涉及核心
        if (this.eventPublisher != null) {
            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }
        successHandler.onAuthenticationSuccess(request,response,authResult);
}

attemptAuthentication和successHandler的流程过长了,下次在说吧。如果本文有什么错误,一定要指出来,我喜欢被人打脸。再见!

番外篇:认证失败时的处理流程
protected void unsuccessfulAnthentication(HttpServletRequest request,HttpServletResponse response,AuthenticationException failed){
    //涉及到其他的拦截器,先不说
    SecurityContextHolder.clearContext();
    if(logger.isDebugEnabled()){
        //日志blablabla
        logger.debug("Authentication request failed: " + failed.toString());
        logger.debug("Updated SecurityContextHolder to contain null Authentication");
        logger.debug("Delegating to authentication failure handler " + failureHandler);
    }
    //这个涉及到rememberMe拦截器,大体就是认证失败删去浏览器中的rememberMeCookie
    rememberMeServices.loginFail(request,response);
    //failureHandler就是在配置里配置的认证失败处理器对象,默认实现类是SimpleUrlAutheneicationFailureHandler
    failureHandler.onAuthenticationFailure(request,response,failed);
}
}

==============================================================================================================================================================
小结:两个判断(1.是否有认证失败跳转的url。2.请求转发还是重定向)
public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler{
    protected final Log logger = LogFactory.getLog(getClass());
    //认证失败时跳转的页面
    private String defaultFailureUrl;
    //是否允许创建session
    private boolean allowSessionCreation = true;
    //这里顺序我微调了一下,方便阅读
    //是否直接到目的地址(true即为请求转发,false即为重定向)
    private boolean forwardToDestination = false;
    //重定向策略(这个类不展开,就是俩方法,计算url和重定向)
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    ....构造器略去,这个涉及太多了,后面再说
    public void onAuthenticationFailure(HttpServletRequest request,HttpServletResponse response,AuthenticationException exception) throws IOException,ServletException{
        if(defaultFailureUrl == null){
            //没有设置认证失败跳转的URL,那么就直接报401
            logger.debug("No failure URL set,sending 401 Unauthorized error");
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Authentication Failed:" + exception.getMessage());    
        }else{
            //这个挺好理解,方法在下面
            saveException(request,exception);
            if(forwardToDestination){
                //请求转发
                logger.debug("Forwarding to" + defaultFailureUrl);
                request.getRequestDispatcher(defaultFailureUrl).forward(request,response);
            }else{
                //重定向
                logger.debug("Redirecting to " + defaultFailureUrl);
                redirectStrategy.sendRedirect(request,response,defaultFailureUrl);
            }
        }
    }
    protected final void saveException(HttpServletRequest request,AuthenticationException exception){
        if(forwardToDestination){
            //如果是请求转发,那么把错误放进request中
            request.setAttribute(WebAttribute.AUTHENTICATION_EXCEPTION,exception);
        }else{
            //不是,则把错误存进session中
            HttpSession session = request.getSession(false);
            if(session != null || allowSessionCreation){
                request.getSession.setAttribute(WebAttribute.AUTHENTICATION_EXCEPTION,exception);
            }
        }
    }
}