上篇回顾

上篇介绍了HttpSecurity如何建造过滤器链,本文主要介绍几个主要的过滤器。

HttpSecurity的过滤器链工作流程是,FilterChainProxy 类遍历过滤器链中的过滤器,执行每个过滤器的doFilter方法

VirtualFilterChain

public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException {
            if (currentPosition == size) {
                this.firewalledRequest.reset();

                originalChain.doFilter(request, response);
            }
            else {
                currentPosition++;

                Filter nextFilter = additionalFilters.get(currentPosition - 1);
                nextFilter.doFilter(request, response, this);
            }
        }

认证过滤器 UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter 父类AbstractAuthenticationProcessingFilter中的doFilter()方法,会判断每个请求是否需要认证。

不需要认证的请求直接放行,需要的认证的会被拦下。

doFilter

requiresAuthentication

判断是否需要认证是怎么做的呢?

其实是我们在调用httpSecurity.formLogin().permitAll()时设置的。

formLogin

FormLoginConfigurer

UsernamePasswordAuthenticationFilter

AbstractAuthenticationProcessingFilter

UsernamePasswordAuthenticationFilter 参数有username,password的,走UsernamePasswordAuthenticationFilter,提取参数构造UsernamePasswordAuthenticationToken进行认证,成功则填充SecurityContextHolder的Authentication

UsernamePasswordAuthenticationFilter实现了其父类AbstractAuthenticationProcessingFilter中的attemptAuthentication方法。这个方法会调用认证管理器AuthenticationManager去认证。

attemptAuthentication

ProviderManager

ProviderManager是认证管理器AuthenticationManager的默认实现(通过遍历来查找支持当前AuthenticationTokenAuthenticationProvider

通过提供不同的AuthenticationProvider实现类,可以通过多种方式进行认证

其内部会调用authenticate(Authentication authentication)遍历providers,调用provider.authenticate()来尝试认证

我们可以实现AuthenticationProvider接口,重写authenticate()方法,来查询数据库对用户名密码做认证

ProviderManager

PS: 上面的parent其实就是存放的我们自定义编写的provider的认证管理器。这里就不贴出来了

认证过滤器 BasicAuthenticationFilter

header里头有Authorization,而且value是以Basic开头的,则走BasicAuthenticationFilter,提取参数构造UsernamePasswordAuthenticationToken进行认证,成功则填充SecurityContextHolder的Authentication

BasicAuthenticationFilter

认证过滤器 AnonymousAuthenticationFilter

给没有登陆的用户,填充AnonymousAuthenticationTokenSecurityContextHolderAuthentication

AnonymousAuthenticationFilter

授权过滤器 AbstractSecurityInterceptor

默认的过滤器是FilterSecurityInterceptor,继承了AbstractSecurityInterceptor实现了Filter接口

我们一般直接继承这个过滤器或者继承他的父类,自定义一个AuthorizeSecurityInterceptor

目的是为了注入自定义的授权管理器AccessDecisionManager、和权限元数据FilterInvocationSecurityMetadataSource

FilterSecurityInterceptor是在WebSecurityConfigurerAdapterinit()里配置的

img

FilterSecurityInterceptor中的doFilter()会调用super.beforeInvocation(fi)方法,内部调用授权管理器做授权

AuthorizeSecurityInterceptor

doFilter

invoke

beforeInvocation

自定义的AuthorizeSecurityMetadataSource实现了FilterInvocationSecurityMetadataSourcegetAttributes()方法,可以根据url获取对应的角色列表

AuthorizeSecurityMetadataSource

自定义的AuthorizeAccessDecisionManager实现了AccessDecisionManager,实现了decide()方法来判断当前用户是否有此url的权限

AuthorizeAccessDecisionManager

框架默认的AccessDecisionManager通过投票决策的方式来授权

  • AffirmativeBased(spring security默认使用)

    只要有投通过(ACCESS_GRANTED=1)票,则直接判为通过。如果没有投通过票且反对(ACCESS_DENIED=-1)票在1个及其以上的,则直接判为不通过。

    AffirmativeBased
    vote

  • ConsensusBased(少数服从多数)

    通过的票数大于反对的票数则判为通过;通过的票数小于反对的票数则判为不通过;通过的票数和反对的票数相等,则可根据配置allowIfEqualGrantedDeniedDecisions(默认为true)进行判断是否通过。

  • UnanimousBased(反对票优先)

    无论多少投票者投了多少通过(ACCESS_GRANTED)票,只要有反对票(ACCESS_DENIED),那都判为不通过;如果没有反对票且有投票者投了通过票,那么就判为通过.

其他过滤器

ExceptionTranslationFilter

该过滤器主要用来捕获处理spring security抛出的异常,异常主要来源于FilterSecurityInterceptor