spring-security

一 web.xml配置文件

  • 配置2个filter

        springSecurityFilterChain
        org.springframework.web.filter.DelegatingFilterProxy
    

    
        encodingFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            UTF-8
        
        
            forceEncoding
            true
        
    

    
        encodingFilter
        /*
    
    
        springSecurityFilterChain
        /*
    
  • spring security是在DelegatingFilterProxy中进行处理
  • tomcat初始化DelegatingFilterProxy类对象
  • 从spring管理的bean中获取FilterChainProxy,赋值为Filter delegate
  • FilterChainProxy类型bean通过xml标签定义,解析后交给spring容器管理。

二 spring xml配置文件




    

    
        

        
        

        
        

    

    

  • org.springframework.security.config.SecurityNamespaceHandler 注册xml标签解析函数
  • HttpSecurityBeanDefinitionParser http标签解析函数
  • 一个http标签解析成一个DefaultSecurityFilterChain类型过滤串,按匹配规则RequestMatcher requestMatcher对目标url执行一组List filters过滤操作。
  • 所有的DefaultSecurityFilterChain用于初始化filterChainProxy属性List filterChains。filterChainProxy对象交给spring容器管理
  • 第二个http标签初始化了一组filter,当有请求时依次调用各filter的doFilter()函数处理请求。

三 filter

3.1 SecurityContextPersistenceFilter

  • 实例化
public SecurityContextPersistenceFilter(SecurityContextRepository repo) {
        this.forceEagerSessionCreation = false;
        this.repo = repo;
    }
  • http标签配置security-context-repository-ref="myCookieSecurityContextRepository"为构造函数参数repo
  • 过滤处理函数
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if(request.getAttribute("__spring_security_scpf_applied") != null) {//只处理一次
            chain.doFilter(request, response);
        } else {
            boolean debug = this.logger.isDebugEnabled();
            request.setAttribute("__spring_security_scpf_applied", Boolean.TRUE);
            if(this.forceEagerSessionCreation) {
                HttpSession session = request.getSession();
                if(debug && session.isNew()) {
                    this.logger.debug("Eagerly created session: " + session.getId());
                }
            }

            HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
//从请求中获取用户认证信息,cookie或session中保存的认证信息
            SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
            boolean var13 = false;

            try {
                var13 = true;
//默认threadlocal方式保存变量,保存filter处理前的认证信息                SecurityContextHolder.setContext(contextBeforeChainExecution);
//执行后续filter处理
                chain.doFilter(holder.getRequest(), holder.getResponse());
                var13 = false;
            } finally {
                if(var13) {//异常发生
                    SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
                    SecurityContextHolder.clearContext();
                    this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
                    request.removeAttribute("__spring_security_scpf_applied");
                    if(debug) {
                        this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
                    }

                }
            }

            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
            //清理缓存数据
            SecurityContextHolder.clearContext();
            //保存认证信息
            this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
//删除处理标记            request.removeAttribute("__spring_security_scpf_applied");
            if(debug) {
                this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
            }

        }
    }

3.2 WebAsyncManagerIntegrationFilter

  • 支持异步处理
  • 只调用一次处理
  • 过滤处理
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);//初始化并注册到request中
        SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor)asyncManager.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
        if(securityProcessingInterceptor == null) {//初始化注册到asyncManager中
            asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, new SecurityContextCallableProcessingInterceptor());
        }

        filterChain.doFilter(request, response);
    }

3.3 LogoutFilter

  • 实例化
public LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {
        Assert.notEmpty(handlers, "LogoutHandlers are required");
       //退出处理函数,清理session及cookie及缓存中的认证信息
        this.handlers = Arrays.asList(handlers);
        Assert.isTrue(!StringUtils.hasLength(logoutSuccessUrl) || UrlUtils.isValidRedirectUrl(logoutSuccessUrl), logoutSuccessUrl + " isn't a valid redirect URL");
        SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
        if(StringUtils.hasText(logoutSuccessUrl)) {
//指定退出后的跳转url            urlLogoutSuccessHandler.setDefaultTargetUrl(logoutSuccessUrl);
        }
        this.logoutSuccessHandler = urlLogoutSuccessHandler;
        this.setFilterProcessesUrl("/j_spring_security_logout");
    }
  • 过滤处理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if(!this.requiresLogout(request, response)) {//匹配函数未匹配
            chain.doFilter(request, response);
        } else {
            //获取认证信息
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Logging out user '" + auth + "' and transferring to logout destination");
            }

            Iterator i$ = this.handlers.iterator();

            while(i$.hasNext()) {//遍历退出处理函数,进行处理
                LogoutHandler handler = (LogoutHandler)i$.next();
                handler.logout(request, response, auth);
            }
            //使用sendRedirect(),跳转到退出url
            this.logoutSuccessHandler.onLogoutSuccess(request, response, auth);
        }
    }
  • sendRedirect()与forward()

sendRedirect就是服务端根据逻辑,发送一个状态码(Location ,状态码320),告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。

forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。

3.4 SecurityContextHolderAwareRequestFilter

  • 获取servlet版本信息
private boolean isServlet3() {
        return ClassUtils.hasMethod(ServletRequest.class, "startAsync", new Class[0]);
    }
  • 根据servlet版本信息初始化请求工厂
    this.requestFactory = (HttpServletRequestFactory)(this.isServlet3()?this.createServlet3Factory(this.rolePrefix):new HttpServlet25RequestFactory(this.trustResolver, this.rolePrefix));
  • servlet3工厂
private HttpServletRequestFactory createServlet3Factory(String rolePrefix) {
        HttpServlet3RequestFactory factory = new HttpServlet3RequestFactory(rolePrefix);
        factory.setTrustResolver(this.trustResolver);
        factory.setAuthenticationEntryPoint(this.authenticationEntryPoint);
        factory.setAuthenticationManager(this.authenticationManager);
        factory.setLogoutHandlers(this.logoutHandlers);
        return factory;
    }
  • 注入认证管理器AuthenticationManager
  • 注入认证入口函数AuthenticationEntryPoint
  • 注入logout处理函数
  • 过滤处理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

//请求工厂创建一个请求        chain.doFilter(this.requestFactory.create((HttpServletRequest)req, (HttpServletResponse)res), res);
    }
  • 请求的类关系图


    image.png

3.5 AnonymousAuthenticationFilter

  • 支持匿名用户认证,用户名 anonymousUser,角色ROLE_ANONYMOUS
  • 过滤处理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        if(this.applyAnonymousForThisRequest((HttpServletRequest)req)) {
            if(SecurityContextHolder.getContext().getAuthentication() == null) {//无认证信息
                SecurityContextHolder.getContext().setAuthentication(this.createAuthentication((HttpServletRequest)req));//创建匿名用户认证
                if(this.logger.isDebugEnabled()) {
                    this.logger.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
                }
            } else if(this.logger.isDebugEnabled()) {
                this.logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
            }
        }
//使用匿名用户进行后续处理
        chain.doFilter(req, res);
    }

3.6 ExceptionTranslationFilter

  • 异常处理
  • 实例化
public ExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint, RequestCache requestCache) {
        this.accessDeniedHandler = new AccessDeniedHandlerImpl();//禁止访问处理
        this.authenticationTrustResolver = new AuthenticationTrustResolverImpl();
        this.throwableAnalyzer = new ExceptionTranslationFilter.DefaultThrowableAnalyzer();//异常解析
        this.requestCache = new HttpSessionRequestCache();
        Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
        Assert.notNull(requestCache, "requestCache cannot be null");
//认证入口,异常发生时,调用认证入口函数
        this.authenticationEntryPoint = authenticationEntryPoint;
        this.requestCache = requestCache;
    }
  • 过滤处理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;

        try {
            chain.doFilter(request, response);
            this.logger.debug("Chain processed normally");
        } catch (IOException var9) {//io异常不处理
            throw var9;
        } catch (Exception var10) {
//解析异常
            Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
            RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
            if(ase == null) {
                ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
            }

            if(ase == null) {//解析失败,抛出异常
                if(var10 instanceof ServletException) {
                    throw (ServletException)var10;
                }

                if(var10 instanceof RuntimeException) {
                    throw (RuntimeException)var10;
                }

                throw new RuntimeException(var10);
            }
//解析成功,进行处理
            this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
        }

    }
  • 异常解析结果处理
private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {
        if(exception instanceof AuthenticationException) {
            this.logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
//认证异常,重新认证
            this.sendStartAuthentication(request, response, chain, (AuthenticationException)exception);
        } else if(exception instanceof AccessDeniedException) {//权限异常
            if(this.authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {//匿名用户,重新认证
                this.logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point", exception);
                this.sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException("Full authentication is required to access this resource"));
            } else {//调用认证失败处理函数处理
                this.logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);
                this.accessDeniedHandler.handle(request, response, (AccessDeniedException)exception);
            }
        }

    }

3.7 FilterSecurityInterceptor

  • doFilter处理
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);//初始化
        this.invoke(fi);
    }
  • invoke()
public void invoke(FilterInvocation fi) throws IOException, ServletException {
        if(fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } else {
            if(fi.getRequest() != null) {//只处理一次
                fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
            }
            //调用各模版函数
            InterceptorStatusToken token = super.beforeInvocation(fi);

            try {
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
                super.finallyInvocation(token);
            }

            super.afterInvocation(token, (Object)null);
        }

    }
  • beforeInvocation()
protected InterceptorStatusToken beforeInvocation(Object object) {
        Assert.notNull(object, "Object was null");
        boolean debug = this.logger.isDebugEnabled();
        if(!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {
            throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + this.getSecureObjectClass());
        } else {
            //获取url匹配pattern规则的`intercept-url`配置
            Collection attributes = this.obtainSecurityMetadataSource().getAttributes(object);
            if(attributes != null && !attributes.isEmpty()) {
                if(debug) {
                    this.logger.debug("Secure object: " + object + "; Attributes: " + attributes);
                }

 //未认证会创建匿名认证信息               if(SecurityContextHolder.getContext().getAuthentication() == null) {
                    this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
                }
//检查是否需要进行认证,一种是匿名认证,一种是配置了重复认证。
                Authentication authenticated = this.authenticateIfRequired();

                try {//授权校验处理,失败则抛AccessDeniedException
                    this.accessDecisionManager.decide(authenticated, object, attributes);
                } catch (AccessDeniedException var7) {
                    this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var7));//发送通知事件
                    throw var7;//异常处理函数中处理
                }

                if(debug) {
                    this.logger.debug("Authorization successful");
                }
                //通知授权结果
                if(this.publishAuthorizationSuccess) {
                    this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));
                }

                Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
                if(runAs == null) {
                    if(debug) {
                        this.logger.debug("RunAsManager did not change Authentication object");
                    }

                    return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
                } else {
                    if(debug) {
                        this.logger.debug("Switching to RunAs Authentication: " + runAs);
                    }

                    SecurityContext origCtx = SecurityContextHolder.getContext();
                    SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
                    SecurityContextHolder.getContext().setAuthentication(runAs);
                    return new InterceptorStatusToken(origCtx, true, attributes, object);
                }
            } else if(this.rejectPublicInvocations) {
                throw new IllegalArgumentException("Secure object invocation " + object + " was denied as public invocations are not allowed via this interceptor. " + "This indicates a configuration error because the " + "rejectPublicInvocations property is set to 'true'");
            } else {
                if(debug) {
                    this.logger.debug("Public object - authentication not attempted");
                }

                this.publishEvent(new PublicInvocationEvent(object));
                return null;
            }
        }
    }
  • finallyInvocation
protected void finallyInvocation(InterceptorStatusToken token) {
        if(token != null && token.isContextHolderRefreshRequired()) {//配置每次都刷新缓存的认证信息,默认配置为否
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Reverting to original Authentication: " + token.getSecurityContext().getAuthentication());
            }

            SecurityContextHolder.setContext(token.getSecurityContext());
        }

    }
  • afterInvocation
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
        if(token == null) {
            return returnedObject;
        } else {
            this.finallyInvocation(token);
            if(this.afterInvocationManager != null) {
                try {
//处理后校验
                    returnedObject = this.afterInvocationManager.decide(token.getSecurityContext().getAuthentication(), token.getSecureObject(), token.getAttributes(), returnedObject);
                } catch (AccessDeniedException var5) {
                    AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token.getAttributes(), token.getSecurityContext().getAuthentication(), var5);
                    this.publishEvent(event);
                    throw var5;
                }
            }

            return returnedObject;
        }
    }

你可能感兴趣的:(spring-security)