# 概述

  • SpringSecurity是一个有效、高度定制化的认证与访问控制框架;
  • SpringSecurity是一个致力于为java应用提供认证与授权能力的框架;

# 主体框架构成

  • cas: 基于cas的客户端认证能力
  • crypto: 加解密能力
  • jwt: Json Web Tokens,客户端存储能力
  • oauth2: oauth2三方授权及单点的能力
  • web: 与web应用集成的能力
  • ldap: Light Directory Access Protocol,轻量级目录访问协议,LDAP统一认证服务能力
  • openid: 去中心化的数字身份识别系统认证能力

# 运行时序

springSecurity时序 springSecurity时序 springSecurity时序 springSecurity时序

# 主要官方过滤器说明

# 无条件过滤器

名称 作用 能否阻断 后置处理
ChannelProcessingFilter 主要用于确保请求通过特定的安全通道进行传输,比如 HTTP 或 HTTPS。 能 无
ConcurrentSessionFilter 专注于处理并发会话的具体细节,如限制同一用户可以拥有的活跃会话数量、处理超出限制的登录尝试以及支持会话信息的显示和管理等 能 无
WebAsyncManagerIntegrationFilter 主要作用是确保 Spring Security 与 Spring MVC 的异步请求处理机制(如 Servlet 3.0 引入的异步处理功能)能够无缝协作 不能 无
SecurityContextPersistenceFilter 在请求之前,从配置的SecurityContextRepository中获取相应信息填充SecurityHolder;
请求完成后,清除holder并将其存储;
不能 有
HeaderWriterFilter 向当前响应添加响应头。可用于添加启用浏览器保护的某些响应头。像X-Frame-Options,X-XSS-Protection和X-Content-Type-Options。 不能 有
CorsFilter 处理跨域的预检请求 能 无
CsrfFilter 进行跨域请求伪造攻击防护 能 无
X509AuthenticationFilter X509证书认证 能 无
RequestCacheAwareFilter 它的主要职责是与 RequestCache 交互来缓存和恢复请求。这个过滤器确保当用户尝试访问一个受保护的资源但未经过认证或授权时,他们被重定向到登录页面或其他形式的身份验证过程之前,原始请求的信息能够被保存下来。一旦用户成功完成身份验证,原始请求就会从缓存中恢复,并且用户会被重定向回到最初请求的资源。 不能 无
SecurityContextHolderAwareRequestFilter 作用是将标准的 HttpServletRequest 包装成一个能够感知 Spring Security 安全上下文的请求对象。通过这种包装,可以让 Servlet API 的安全相关方法(如 getRemoteUser()、isUserInRole(String role) 和 getUserPrincipal() 等)与 Spring Security 提供的安全功能集成起来。 不能 无
JaasApiIntegrationFilter jaasApi集成过滤器 能 无
RememberMeAuthenticationFilter 记住我过滤器 不能 无
AnonymousAuthenticationFilter 用于在安全上下文中为未经过认证的用户创建匿名身份验证对象。其主要目的是为了简化安全逻辑处理,使得代码无需频繁检查用户是否为 null 来判断用户是否已登录,使得 Spring Security 能够以一种更简洁且一致的方式处理未经认证的用户请求,同时提供了对这些请求的细粒度控制能力 不能 无
OAuth2AuthorizationCodeGrantFilter 主要作用是在应用中处理 OAuth 2.0 授权码许可流程,这是一种安全且常见的授权机制,通常用于第三方应用获取对资源的有限访问权限而不必暴露用户的凭据。 不能 无
SessionManagementFilter 主要用于管理用户会话的安全性。它的主要职责包括控制并发会话的数量、检测会话超时情况以及处理已认证用户的会话固定攻击防护等 能 无
ExceptionTranslationFilter 主要用于处理在请求处理过程中抛出的安全相关异常,并将这些异常转换为适当的 HTTP 响应。它不直接处理认证或授权逻辑,而是作为一道桥梁,确保当发生安全相关的异常时,能够以用户友好的方式响应客户端。 不能 有
FilterSecurityInterceptor 核心组件,主要用于保护 Web 资源的安全访问。它位于过滤器链的末端,负责执行最终的授权决策,决定是否允许当前用户访问特定的 HTTP 请求资源。 能 无
SwitchUserFilter 允许已经认证的用户(通常是一个管理员或支持人员)在会话中切换到另一个用户的账户,而无需知道该用户的密码。这种功能特别适用于需要帮助用户解决问题或者查看特定用户视图的情况,例如客服代表为了协助解决客户问题而临时切换到客户的账户。 能 无

# 条件过滤器

名称 作用 条件 阻断 后置
LogoutFilter 登出处理过滤器 路径匹配 是 无
OAuth2AuthorizationRequestRedirectFilter 引导oauth2的授权码以及隐式授权码模式重定向到授权服务器的授权端点 路径匹配 是 有
CasAuthenticationFilter Cas(服务端)登录的过滤器 路径匹配 是 是
OAuth2LoginAuthenticationFilter 用于支持 OAuth 2.0 登录(也称为“社交登录”)的过滤器。它的主要作用是在用户尝试通过 OAuth 2.0 提供者(如 Google、Facebook 等)进行登录时,处理认证过程。具体来说,这个过滤器负责拦截来自 OAuth 2.0 提供者的回调请求,并使用这些信息来创建或更新用户的认证状态 路径匹配 是 是
UsernamePasswordAuthenticationFilter 表单登录认证过滤器 路径匹配 是 是
OpenIDAuthenticationFilter openID认证过滤器 路径匹配 是 是
DefaultLoginPageGeneratingFilter 默认登陆页生成过滤器 路径匹配 是 是
DefaultLogoutPageGeneratingFilter 默认登出页生成过滤器 路径匹配 是 是
DigestAuthenticationFilter http摘要认证过滤器 请求头匹配 是 是
BearerTokenAuthenticationFilter 用于处理 OAuth 2.0 资源服务器中 Bearer Token 验证的过滤器。它主要用于保护 RESTful API 或者其他类型的资源服务器,确保只有携带了有效访问令牌(Access Token)的请求才能访问受保护的资源。 请求头匹配 是 是
BasicAuthenticationFilter http基本令牌认证过滤器 请求头匹配 是 是

# 默认过滤链

优先级 过滤器
1 WebAsyncManagerIntegrationFilter
2 SecurityContextPersistenceFilter
3 HeaderWriterFilter
4 CsrfFilter
5 LogoutFilter
6 UsernamePasswordAuthenticationFilter
7 DefaultLoginPageGeneratingFilter
8 DefaultLogoutPageGeneratingFilter
9 BasicAuthenticationFilter
10 RequestCacheAwareFilter
11 SecurityContextHolderAwareRequestFilter
12 AnonymousAuthenticationFilter
13 SessionManagementFilter
14 ExceptionTranslationFilter
15 FilterSecurityInterceptor

# oauth2场景涉及的过滤器(老版,后被逐渐废弃)

  • 代表应用程序本身 (而不是某个特定用户) 向授权服务器获取访问令牌 (Access Token),通常用于访问受保护的资源 API。
  • 在新的 Spring Security OAuth2 客户端库 (spring-security-oauth2-client) 中,它已被 DefaultOAuth2AuthorizedClientManager 和相关机制取代,用于管理客户端凭证等流程
  • 需要注意的是,基于该流程也能实现oauth2登录流程;
优先级 名称 作用
1 OAuth2ClientContextFilter 导向oauth2授权服务器进行授权,通过特定的异常进行识别,即 UserRedirectRequiredException
2 OAuth2ClientAuthenticationProcessingFilter 1.第一轮,抛出特定异常,结合上方的上下文过滤器,自动引导到授权服务器获取授权码
2.第二轮,自动根据授权码获取accessToken码
3.根据accessToken码获取AccessToken实体,然后从实体中获取认证对象。 这一步默认是通过jwt实现;--本质上是通过accessToken获取用户信息
3 / 通过监听事件,实现本地认证对象的构建(包括用户同步等机制自行拓展)

# oauth2场景涉及的过滤器(新版,推荐用于用户登录)

  • OAuth2AuthorizationRequestRedirectFilter, 用于引导oauth2授权服务器进行授权,是oauth2流程的起点,通过 ClientAuthorizationRequiredException进行标识
  • 异常,默认由 OAuth2AuthorizedClientArgumentResolver抛出,当参数类型为 OAuth2AuthorizedClient并且含有 @RegisteredOAuth2AuthorizedClient时生效
  • 根据授权码获取accessToken,以及根据accessToken获取用户,核心逻辑基于 OAuth2LoginAuthenticationFilter进行实现,采用了 凭authentication过滤器+authenticationProvider的标准模式;
  • 获取的用户为标准的oauth2User, 包括了关键的 社交id, 社交昵称, 社交头像等信息,可能涉及到与本系统的用户体系的绑定关系等;
  • authenticationProvider涉及到两层,分别为: OAuth2LoginAuthenticationToken 以及 OAuth2AuthorizationCodeAuthenticationToken, 对OAuth2AuthorizationCodeAuthenticationToken的授权完成后,获取AccessToken, 通过accessToken获取用户信息,则在OAuth2LoginAuthenticationToken的过程中进行实现。

# springSession场景涉及的过滤器

优先级 名称 作用
1 SessionRepositoryFilter 覆写获取session的逻辑

# 固有过滤器的优先级规定

springSecurity过滤器固有顺序

# SpringSecurityWeb全解析

# 与Spring环境的结合方式

/*xxx: EnableWebSecurity也是sping3开始便有的特性*/
public @interface EnableWebSecurity {
    /*xxx: webSecurity可以打开配置说明,默认关闭*/
    boolean debug() default false;
}
@Configuration
/*xxx: spring-security的默认配置项,可以自动配置*/
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    private WebSecurity webSecurity;
    
    /*xxx: 默认情况下,采用该配置*/
    private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;

    @Autowired(required = false)
    public void setFilterChainProxySecurityConfigurer(
            ObjectPostProcessor<Object> objectPostProcessor,
           /*xxx: 收集SecurityConfigurer,一般来说,单个SecurityConfigurer就代表一条过滤链*/
           List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
            throws Exception {
        webSecurity = objectPostProcessor
                .postProcess(new WebSecurity(objectPostProcessor));
        /*xxx: 省略其它抽象...*/
        for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
            webSecurity.apply(webSecurityConfigurer);
        }
        this.webSecurityConfigurers = webSecurityConfigurers;
    }

    /*xxx: 这个bean的名称,也是有门道的,不能轻易变更*/
    @Bean("springSecurityFilterChain")
    public Filter springSecurityFilterChain() throws Exception {
        boolean hasConfigurers = webSecurityConfigurers != null
                && !webSecurityConfigurers.isEmpty();
        /*xxx: 在没有配置的情况下,采用默认配置*/
        if (!hasConfigurers) {
            WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
                    .postProcess(new WebSecurityConfigurerAdapter() {
                    });
            webSecurity.apply(adapter);
        }
        return webSecurity.build();
    }
}
/*xxx: 该类位由spring-web项目提供实现*/
/*xxx: springSecurity与web集成时候的入口类*/
public class DelegatingFilterProxy extends GenericFilterBean {
    /*xxx: 代理的filter类*/
    private volatile Filter delegate;

    public DelegatingFilterProxy(Filter delegate) {
        Assert.notNull(delegate, "Delegate Filter must not be null");
        this.delegate = delegate;
    }

    @Override
    /*xxx: 作为一个过滤器 在 容器的 applicationFilterChain 进行过滤*/
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        /*xxx: 懒实例化过滤器代理对象 */
        Filter delegateToUse = this.delegate;

        /*xxx: 执行过滤器代理*/
        invokeDelegate(delegateToUse, request, response, filterChain);
    }

    protected void invokeDelegate(
            Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        /*xxx: 通过过滤器代理进行过滤操作*/
        delegate.doFilter(request, response, filterChain);
    }
}
<web-app>
<filter>
        <!--xxx: 需要注意,这个名字是固定的(在不更改默认源码架构的情况下)-->
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
/*xxx: web上下文初始化器,正常情况下,应该采用这个配置*/
public abstract class AbstractSecurityWebApplicationInitializer
      implements WebApplicationInitializer {
    
    public final void onStartup(ServletContext servletContext) throws ServletException {
        insertSpringSecurityFilterChain(servletContext);
    }

    private void insertSpringSecurityFilterChain(ServletContext servletContext) {
        String filterName = "springSecurityFilterChain";
        DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(
                filterName);

        registerFilter(servletContext, true, filterName, springSecurityFilterChain);
    }

    private final void registerFilter(ServletContext servletContext,
                                      boolean insertBeforeOtherFilters, String filterName, Filter filter) {
        Dynamic registration = servletContext.addFilter(filterName, filter);
        EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
        /*xxx: 配置拦截模式*/
        registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters,
                "/*");
    }
}

更多相关知识可参考: springMVC学习笔记-三个初始化器章节中WebApplicationInitializer部分内容

# 与SpringBoot环境的结合方式

@ConditionalOnClass({ 
      AbstractSecurityWebApplicationInitializer.class,
      SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
    @Bean
    /*xxx: 这个名字也至关重要 */
    @ConditionalOnBean("springSecurityFilterChain")
    public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
            SecurityProperties securityProperties) {
        DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
                "springSecurityFilterChain");
        registration.setOrder(securityProperties.getFilter().getOrder());
        registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
        return registration;
    }
}
public class DelegatingFilterProxyRegistrationBean extends AbstractFilterRegistrationBean<DelegatingFilterProxy>
      implements ApplicationContextAware {
    @Override
    public DelegatingFilterProxy getFilter() {
        return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {
            @Override
            protected void initFilterBean() throws ServletException {
            }
        };
    }
}

更多相关知识可参考:springMVC学习笔记-三个初始化器章节中ServletContextInitializer部分内容

# 过滤链组装--逻辑结构设计

public interface SecurityBuilder<O> {
    /*xxx: 过滤链构造*/
    O build() throws Exception;
}

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
    private AtomicBoolean building = new AtomicBoolean();

    /*xxx: 过滤链*/
    private O object;

    public final O build() throws Exception {
        if (this.building.compareAndSet(false, true)) {
            this.object = doBuild();
            return this.object;
        }
    }

    /*xxx: 执行构建*/
    protected abstract O doBuild() throws Exception;
}
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
      extends AbstractSecurityBuilder<O> {
    /*xxx: 配置项注册表*/
    private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();

    @Override
    protected final O doBuild() throws Exception {
        synchronized (configurers) {
            init();
            configure();
           return performBuild();
        }
    }

    /*xxx: 初始化*/
    private void init() throws Exception {
        Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
        for (SecurityConfigurer<O, B> configurer : configurers) {
            configurer.init((B) this);
        }
        
    }

    /*xxx: 配置*/
    private void configure() throws Exception {
        Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

        for (SecurityConfigurer<O, B> configurer : configurers) {
            configurer.configure((B) this);
        }
    }
    
    /*xxx: 执行构建*/
    protected abstract O performBuild() throws Exception;

    /*xxx: 收集配置*/
    /*xxx: 注意,这里收集的 SecurityConfigurerAdapter ,可以是针对单个过滤链的配置,也可以增加过滤器链*/
    /*xxx: 配置是添加过滤器链,还是配置过滤器,取决于配置项的 init行为*/
    public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
            throws Exception {
        configurer.setBuilder((B) this);
        add(configurer);
        return configurer;
    }

    private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
        /*xxx: 配置收集*/
        synchronized (configurers) {
            Class clazz = configurer.getClass();
            List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
                    .get(clazz) : null;
            if (configs == null) {
                configs = new ArrayList<SecurityConfigurer<O, B>>(1);
            }
            configs.add(configurer);
            this.configurers.put(clazz, configs);
        }
    }
}
public final class WebSecurity extends
      AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
      SecurityBuilder<Filter>, ApplicationContextAware {
    
    private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();

    private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
    
    @Override
    protected Filter performBuild() throws Exception {
        int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
        List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
                chainSize);

        /*xxx: 构建过滤链*/
        for (RequestMatcher ignoredRequest : ignoredRequests) {
            securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
        }
        /*xxx: 构建过滤链*/
        for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
            securityFilterChains.add(securityFilterChainBuilder.build());
        }

        /*xxx: 构建过滤链代理*/
        FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

        Filter result = filterChainProxy;
        
        return result;
    }
}

# 过滤配置器新建过滤链的情况

public abstract class WebSecurityConfigurerAdapter implements
      WebSecurityConfigurer<WebSecurity> {
    public void init(final WebSecurity web) throws Exception {
        final HttpSecurity http = getHttp();
        web.addSecurityFilterChainBuilder(http);
    }
}

# 过滤配置器编辑过滤链的情况

public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
      AbstractHttpConfigurer<AnonymousConfigurer<H>, H> {
    @Override
    /*xxx: 注意参数类型的变化*/
    public void init(H http) throws Exception {
        if (authenticationProvider == null) {
            authenticationProvider = new AnonymousAuthenticationProvider(getKey());
        }
        if (authenticationFilter == null) {
            authenticationFilter = new AnonymousAuthenticationFilter(getKey(), principal,
                    authorities);
        }
        authenticationProvider = postProcess(authenticationProvider);
        http.authenticationProvider(authenticationProvider);
    }
}

# 过滤链编辑--逻辑结构设计

public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
      SecurityBuilder<DefaultSecurityFilterChain> {
    /*xxx: 在过滤器之后加入过滤器*/
    H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);

    H addFilter(Filter filter);

    /*xxx: 移除过滤器构造器*/
    <C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(
            Class<C> clazz);

    /*xxx: 添加认证管理器 */
    H authenticationProvider(AuthenticationProvider authenticationProvider);

    /*xxx: 添加用户信息服务*/
    H userDetailsService(UserDetailsService userDetailsService) throws Exception;
}

public final class HttpSecurity extends
        AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
        implements SecurityBuilder<DefaultSecurityFilterChain>,
        HttpSecurityBuilder<HttpSecurity> {
    /*xxx: 提供了多个快捷的链式配置项,包括sessionManage,formLogin,rememberMe,cors等等*/

    @Override
    /*xxx: http默认为一条过滤链*/
    protected DefaultSecurityFilterChain performBuild() throws Exception {
        Collections.sort(filters, comparator);
        return new DefaultSecurityFilterChain(requestMatcher, filters);
    }
}

# 过滤链构造器--逻辑结构设计

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
    /*xxx: 各个配置器的初始化方法*/
    void init(B builder) throws Exception;

    /*xxx: 各个配置器统一调用的配置方法*/
    void configure(B builder) throws Exception;
}

public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
        SecurityConfigurer<Filter, T> {
}

public abstract class WebSecurityConfigurerAdapter implements
        WebSecurityConfigurer<WebSecurity> {
    /*xxx: webSecurity的初始化钩子*/
    public void init(final WebSecurity web) throws Exception {
        final HttpSecurity http = getHttp();
        web.addSecurityFilterChainBuilder(http);
    }

    protected final HttpSecurity getHttp() throws Exception {
        if (http != null) {
            return http;
        }

        http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
                sharedObjects);

        if (!disableDefaults) {
            http.csrf().and()
                    .addFilter(new WebAsyncManagerIntegrationFilter())
                    .exceptionHandling().and()
                    .headers().and()
                    /*xxx: 会话管理*/
                    .sessionManagement().and()
                    .securityContext().and()
                    .requestCache().and()
                    .anonymous().and()
                    .servletApi().and()
                    .apply(new DefaultLoginPageConfigurer<>()).and()
                    .logout();
        }
    }

    /*xxx: 授权配置*/
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().and()
                .httpBasic();
    }

    /*xxx: 注意这个configure 与上面的configure的区别 */
    /*xxx: 该函数是 过滤链构造时的一个钩子函数,详见: AbstractConfiguredSecurityBuilder的configure方法 */
    /*xxx: 可用于添加 过滤器 */
    @Override
    public void configure(WebSecurity web) throws Exception {
    }
}

# 过滤链流程设计

/*xxx: springSecuirty 用来负责 过滤器链的选择 ,springSecurity的实际入口 */
public class FilterChainProxy extends GenericFilterBean {
    private List<SecurityFilterChain> filterChains;

    public FilterChainProxy(List<SecurityFilterChain> filterChains) {
        this.filterChains = filterChains;
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        doFilterInternal(request, response, chain);
    }

    private void doFilterInternal(ServletRequest request, ServletResponse response,
                                  FilterChain chain) throws IOException, ServletException {
        FirewalledRequest fwRequest = firewall
                .getFirewalledRequest((HttpServletRequest) request);

        /*xxx: 从匹配的过滤链条中,获取过滤器列表 */
        List<Filter> filters = getFilters(fwRequest);

        /*xxx: 如果过滤器列表为空,则跳过 */
        if (filters == null || filters.size() == 0) {
            chain.doFilter(fwRequest, fwResponse);
            return;
        }
        /*xxx: 否则,根据过滤器列表,构造虚拟过滤链*/
        /*xxx: 则根据过滤器列表,创建一个虚拟过滤器链,并进行过滤 */
        VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
        vfc.doFilter(fwRequest, fwResponse);
    }

    /*xxx: springSecurity中,最重要的方法,获取过滤链中的过滤器列表 */
    private List<Filter> getFilters(HttpServletRequest request) {
        /*xxx: 从所有的过滤链中,进行筛选,采用首次匹配原则*/
        for (SecurityFilterChain chain : filterChains) {
            if (chain.matches(request)) {
                return chain.getFilters();
            }
        }

        return null;
    }
}
/*xxx: springSecuirty 用来负责 过滤器链的选择 ,springSecurity的实际入口 */
public class FilterChainProxy extends GenericFilterBean {
    private static class VirtualFilterChain implements FilterChain {
        private int currentPosition = 0;

        /*xxx: 原生过滤链*/
        private final FilterChain originalChain;
        
        @Override
        public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException {
            if (currentPosition == size) {
                /*xxx: 当springSecurity的虚拟过滤链执行完毕后,需要再次执行 应用层面的过滤链*/
                originalChain.doFilter(request, response);
            }
            else {
                currentPosition++;
                Filter nextFilter = additionalFilters.get(currentPosition - 1);
                nextFilter.doFilter(request, response, this);
            }
        }
    }
}

# 默认过滤链

@Configuration
/*xxx: spring-security的默认配置项,可以自动配置*/
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    @Bean(name = "springSecurityFilterChain")
    public Filter springSecurityFilterChain() throws Exception {
        boolean hasConfigurers = webSecurityConfigurers != null
                && !webSecurityConfigurers.isEmpty();

        /*xxx: 在没有配置的情况下,采用默认配置*/
        if (!hasConfigurers) {
            WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
                    .postProcess(new WebSecurityConfigurerAdapter() {
                    });
            webSecurity.apply(adapter);
        }
        return webSecurity.build();
    }
}
public abstract class WebSecurityConfigurerAdapter implements
      WebSecurityConfigurer<WebSecurity> {
    protected final HttpSecurity getHttp() throws Exception {
        http
                .csrf().and()
                .addFilter(new WebAsyncManagerIntegrationFilter())
                .exceptionHandling().and()
                .headers().and()
                /*xxx: 会话管理*/
                .sessionManagement().and()
                .securityContext().and()
                .requestCache().and()
                .anonymous().and()
                .servletApi().and()
                .apply(new DefaultLoginPageConfigurer<>()).and()
                .logout();

        configure(http);
        return http;
    }

    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().and()
                .httpBasic();
    }
}

public final class HttpSecurity extends
        AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
        implements SecurityBuilder<DefaultSecurityFilterChain>,
        HttpSecurityBuilder<HttpSecurity> {
    @Override
    protected DefaultSecurityFilterChain performBuild() throws Exception {
        Collections.sort(filters, comparator);
        return new DefaultSecurityFilterChain(requestMatcher, filters);
    }
}
/*xxx: 过滤链*/
public interface SecurityFilterChain {

   boolean matches(HttpServletRequest request);

   List<Filter> getFilters();
}

public final class DefaultSecurityFilterChain implements SecurityFilterChain {
    /*xxx: 匹配器*/
    private final RequestMatcher requestMatcher;
    /*xxx: 过滤链中的过滤器集合*/
    private final List<Filter> filters;

    public List<Filter> getFilters() {
        return filters;
    }

    public boolean matches(HttpServletRequest request) {
        return requestMatcher.matches(request);
    }
}

# 认证--应用设计

public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>
      extends AbstractHttpConfigurer<T, B> {
    private F authFilter;

    protected AbstractAuthenticationFilterConfigurer(F authenticationFilter,
                                                     String defaultLoginProcessingUrl) {
        this.authFilter = authenticationFilter;

        if (defaultLoginProcessingUrl != null) {
            loginProcessingUrl(defaultLoginProcessingUrl);
        }
    }

    public T loginProcessingUrl(String loginProcessingUrl) {
        this.loginProcessingUrl = loginProcessingUrl;
        authFilter
                .setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
        return getSelf();
    }

    protected abstract RequestMatcher createLoginProcessingUrlMatcher(
            String loginProcessingUrl);

    @Override
    public void configure(B http) throws Exception {
        authFilter.setAuthenticationManager(http
                .getSharedObject(AuthenticationManager.class));
        authFilter.setAuthenticationSuccessHandler(successHandler);
        authFilter.setAuthenticationFailureHandler(failureHandler);

        if (authenticationDetailsSource != null) {
            authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
        }

        SessionAuthenticationStrategy sessionAuthenticationStrategy = http
                .getSharedObject(SessionAuthenticationStrategy.class);
        if (sessionAuthenticationStrategy != null) {
            authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
        }

        RememberMeServices rememberMeServices = http
                .getSharedObject(RememberMeServices.class);
        if (rememberMeServices != null) {
            authFilter.setRememberMeServices(rememberMeServices);
        }

        F filter = postProcess(authFilter);
        http.addFilter(filter);
    }
}
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
      AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
    @Override
    protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
        return new AntPathRequestMatcher(loginProcessingUrl, "POST");
    }

    @Override
    public void init(H http) throws Exception {
        super.init(http);
        initDefaultLoginFilter(http);
    }

    private void initDefaultLoginFilter(H http) {
        DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
                .getSharedObject(DefaultLoginPageGeneratingFilter.class);
        if (loginPageGeneratingFilter != null && !isCustomLoginPage()) {
            /*xxx: 登录页地址*/
            loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());
            /*xxx: 认证地址*/
            loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());
        }
    }
}

# 认证过滤器--逻辑设计

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
      implements ApplicationEventPublisherAware, MessageSourceAware {
    /*xxx: 认证管理器*/
    private AuthenticationManager authenticationManager;

    /*xxx:记住我的服务*/
    private RememberMeServices rememberMeServices = new NullRememberMeServices();

    /*xxx: session认证策略*/
    private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();

    /*xxx: 结果处理器*/
    private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();

    /*xxx: 进行过滤的方法 */
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        /*xxx: 首先判断,是否符合当前的认证规则,不符合则放行*/
        if (!requiresAuthentication(request, response)) {
            chain.doFilter(request, response);

            return;
        }

        /*xxx: 获取认证信息 */
        Authentication  authResult = attemptAuthentication(request, response);

        /*xxx: 进行session策略的认证 */
        sessionStrategy.onAuthentication(authResult, request, response);

        /*xxx: 调用认证成功处理器对结果进行处理*/
        successfulAuthentication(request, response, chain, authResult);
    }

    /*xxx: 尝试进行认证*/
    public abstract Authentication attemptAuthentication(HttpServletRequest request,
                                                         HttpServletResponse response) throws AuthenticationException, IOException,
            ServletException;
}
/*xxx: 用户名密码过滤器,spring3.0开始设计*/
public class UsernamePasswordAuthenticationFilter extends
      AbstractAuthenticationProcessingFilter {
    /*xxx: 尝试进行认证*/
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        /*xxx: 生成一个基本的 authentication 令牌*/
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);

        /*xxx: 为该 authentication 设置附加信息*/
        setDetails(request, authRequest);

        /*xxx: 调用 认证管理器,对令牌进行 认证*/
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

# 认证架构--逻辑设计

/*xxx: 一次完整的认证 可以包含多个 provider,由  providerManager进行管理*/
   /*xxx: 认证管理器 */
public interface AuthenticationManager {
    /*xxx: 迭代验证,直到有一个验证通过,则跳出*/
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
}

/*xxx: springSecurity 默认使用它来完成 迭代认证流程*/
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
        InitializingBean {
    /*xxx: 认证提供器集合*/
    private List<AuthenticationProvider> providers = Collections.emptyList();

    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Authentication result;
        /*xxx: 获取所有的认证提供器,迭代对其进行认证 */
        for (AuthenticationProvider provider : getProviders()) {
            if (!provider.supports(toTest)) {
                continue;
            }

            /*xxx: 如果当前的 认证提供器,支持该令牌的认证,则对其进行认证*/
             result = provider.authenticate(authentication);

            /*xxx: 认证成功后,需要将源Token 的详细信息 拷贝到 目标Token 中 */
            /*xxx: 拷贝成功后,不再尝试后续的认证*/
            if (result != null) {
                copyDetails(authentication, result);
                break;
            }
        }

        return result;
    }
}
public interface AuthenticationProvider {
    /*xxx: 验证过程,成功则返回一个验证完成的 Authentication*/
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

    /*xxx: 是否支持当前的Authentication类型*/
    boolean supports(Class<?> authentication);
}

由此可以知道,在典型的http认证场景中,真正提供认证的不是AuthenticationManager,而是:AuthenticationProvider,它们是没有关系的两个类
与http认证不同的是,oauth2协议的资源服务器访问过程,则没有采用这一套体系,而是使用的原生OAuth2AuthenticationManager,继承自AuthenticationManager

# 认证主体流程--流程设计

public abstract class AbstractUserDetailsAuthenticationProvider implements
        AuthenticationProvider, InitializingBean, MessageSourceAware {
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        /*xxx: 从令牌中,获取用户名*/
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                : authentication.getName();

        /*xxx: 从缓存中,获取用户信息*/
        UserDetails user = this.userCache.getUserFromCache(username);

        if (user == null) {
            /*xxx: 首先先检索用户 */
            user = retrieveUser(username,
                    (UsernamePasswordAuthenticationToken) authentication);
        }
        /*xxx: 检测用户账号是否可用: 包括是否锁定,是否冻结,是否过期*/
        preAuthenticationChecks.check(user);
        /*xxx: 附加认证检查,默认情况下,该方法就是检查 是否与给定的密码匹配*/
        additionalAuthenticationChecks(user,
                (UsernamePasswordAuthenticationToken) authentication);

        /*xxx: 检查用户密码是否过期 */
        postAuthenticationChecks.check(user);

        /*xxx: 返回认证通过的 Authentication */
        return createSuccessAuthentication(principalToReturn, authentication, user);
    }

    /*xxx: 检索用户 */
    protected abstract UserDetails retrieveUser(String username,
                                                UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException;
}
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    /*xxx: 为认证提供数据来源*/
    private UserDetailsService userDetailsService;

    private UserDetailsPasswordService userDetailsPasswordService;
    
    /*xxx: 检索用户*/
    protected final UserDetails retrieveUser(String username,
                                             UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {

        /*xxx: 根据用户名,获取用户信息,需要遵循 springSecurity的模型规则。 通过 UserService*/
        UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        return loadedUser;
    }
}
public interface UserDetailsService {
    /*xxx: 根据用户名查询用户*/
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

public class JdbcDaoImpl extends JdbcDaoSupport
        implements UserDetailsService, MessageSourceAware {
    /*xxx: 通过jdbc实现的数据源服务,实现略*/    
}

# 认证令牌--逻辑设计

public abstract class AbstractAuthenticationToken implements Authentication,
        CredentialsContainer {
    private final Collection<GrantedAuthority> authorities;
    private Object details;
    private boolean authenticated = false;

    public boolean isAuthenticated() {
        return authenticated;
    }
}

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
    private final Object principal;
    private Object credentials;
}

# 认证前置流程--从session中提取认证信息

public class SecurityContextPersistenceFilter extends GenericFilterBean {
    static final String FILTER_APPLIED = "__spring_security_scpf_applied";
    
    private SecurityContextRepository repo;

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        if (request.getAttribute(FILTER_APPLIED) != null) {
            chain.doFilter(request, response);
            return;
        }

        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

        HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
                response);
        SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

        /*xxx: 将恢复的认证信息,保存至当前线程上下文中 */
        SecurityContextHolder.setContext(contextBeforeChainExecution);

        chain.doFilter(holder.getRequest(), holder.getResponse());
        
        
    }
}
public interface SecurityContextRepository {
    SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
}

public class HttpSessionSecurityContextRepository implements SecurityContextRepository {
    public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
    
    private String springSecurityContextKey = SPRING_SECURITY_CONTEXT_KEY;

    public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
        HttpServletRequest request = requestResponseHolder.getRequest();
        HttpServletResponse response = requestResponseHolder.getResponse();
        HttpSession httpSession = request.getSession(false);

        SecurityContext context = readSecurityContextFromSession(httpSession);
        
        return contex;
    }

    private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
        Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);
        return (SecurityContext) contextFromSession;
    }
}

# 认证后置流程--认证信息保存至session

public class SecurityContextPersistenceFilter extends GenericFilterBean {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        try {
            /*xxx: 省略其他抽象...*/
            chain.doFilter(holder.getRequest(), holder.getResponse());
        }finally {
            SecurityContext contextAfterChainExecution = SecurityContextHolder
                    .getContext();
            
            SecurityContextHolder.clearContext();

            repo.saveContext(contextAfterChainExecution, holder.getRequest(),
                    holder.getResponse());
            request.removeAttribute("__spring_security_scpf_applied");
        }
    }
}
public interface SecurityContextRepository {
    void saveContext(SecurityContext context, HttpServletRequest request,
                     HttpServletResponse response);
}

public class HttpSessionSecurityContextRepository implements SecurityContextRepository {
    public void saveContext(SecurityContext context, HttpServletRequest request,
                            HttpServletResponse response) {
        SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils
                .getNativeResponse(response,
                        SaveContextOnUpdateOrErrorResponseWrapper.class);
        if (!responseWrapper.isContextSaved()) {
            responseWrapper.saveContext(context);
        }
    }
}
public abstract class SaveContextOnUpdateOrErrorResponseWrapper
      extends OnCommittedResponseWrapper {
    protected abstract void saveContext(SecurityContext context);
}

final class SaveToSessionResponseWrapper extends
        SaveContextOnUpdateOrErrorResponseWrapper {
    @Override
    protected void saveContext(SecurityContext context) {
        final Authentication authentication = context.getAuthentication();
        HttpSession httpSession = request.getSession(false);

        /*xxx: 将上下文信息保存至session中*/
        if (httpSession != null) {
            httpSession.setAttribute("SPRING_SECURITY_CONTEXT", context);
        }
    }
}

# 认证后置流程--session控制

session与用户绑定

public interface SessionAuthenticationStrategy {
    void onAuthentication(Authentication authentication, HttpServletRequest request,
                          HttpServletResponse response) throws SessionAuthenticationException;
}

public class RegisterSessionAuthenticationStrategy implements
      SessionAuthenticationStrategy {
    private final SessionRegistry sessionRegistry;

    public void onAuthentication(Authentication authentication,
                                 HttpServletRequest request, HttpServletResponse response) {
        sessionRegistry.registerNewSession(request.getSession().getId(),
                authentication.getPrincipal());
    }
}

session并发控制

public interface SessionAuthenticationStrategy {
    void onAuthentication(Authentication authentication, HttpServletRequest request,
                          HttpServletResponse response) throws SessionAuthenticationException;
}

/*xxx: 会话并发控制策略  */
public class ConcurrentSessionControlAuthenticationStrategy implements
        MessageSourceAware, SessionAuthenticationStrategy {
    /*xxx: 默认允许的并发数为1, 新的会话会踢掉旧的会话*/
    private int maximumSessions = 1;

    private final SessionRegistry sessionRegistry;

    public ConcurrentSessionControlAuthenticationStrategy(SessionRegistry sessionRegistry) {
        this.sessionRegistry = sessionRegistry;
    }

    public void onAuthentication(Authentication authentication,
                                 HttpServletRequest request, HttpServletResponse response) {
        final List<SessionInformation> sessions = sessionRegistry.getAllSessions(
                authentication.getPrincipal(), false);

        int sessionCount = sessions.size();
        int allowedSessions = getMaximumSessionsForThisUser(authentication);
        /*xxx: 允许的并发数大于当前并发数时,不做处理*/
        if (sessionCount < allowedSessions) {
            return;
        }

        /*xxx: -1表示不做限制*/
        if (allowedSessions == -1) {
            return;
        }

        /*xxx: 进行策略判断 */
        allowableSessionsExceeded(sessions, allowedSessions, sessionRegistry);
    }

    protected void allowableSessionsExceeded(List<SessionInformation> sessions,
                                             int allowableSessions, SessionRegistry registry)
            throws SessionAuthenticationException {
        sessions.sort(Comparator.comparing(SessionInformation::getLastRequest));
        int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;
        List<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);
        /*xxx: 当新会话建立时,最早的会话过期*/
        /*xxx: 注意,这一步只是标志session过期,但是需要该标志被检测的时候,才会进行剔除*/
        for (SessionInformation session: sessionsToBeExpired) {
            session.expireNow();
        }
    }
}

session并发管理的逻辑为:

  • 用户的principal对应了多个 ids;
  • 每个ids保存了session的信息;
  • 以 UserDetails 为键,因此在覆写自定义的userDetails时,必须覆写 hashCode 和 equals方法

# session管理

# 授权--逻辑结构设计

# 投票管理器

/*xxx: 访问决策管理器 */
public interface AccessDecisionManager {
    /*xxx: 资源通行投票*/
    void decide(Authentication authentication, Object object,
                Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
            InsufficientAuthenticationException;

    /*xxx: 是否能够参与投票*/
    boolean supports(Class<?> clazz);
}

/*xxx: 抽象访问决策器*/
public abstract class AbstractAccessDecisionManager implements AccessDecisionManager,
        InitializingBean, MessageSourceAware {
    /*xxx: 投票器池*/
    private List<AccessDecisionVoter<? extends Object>> decisionVoters;

    public boolean supports(Class<?> clazz) {
        for (AccessDecisionVoter voter : this.decisionVoters) {
            if (!voter.supports(clazz)) {
                return false;
            }
        }

        return true;
    }
}
/*xxx: 乐观投票器,只要有一个通过就算通过*/
public class AffirmativeBased extends AbstractAccessDecisionManager {
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
        int deny = 0;

        for (AccessDecisionVoter voter : getDecisionVoters()) {
            int result = voter.vote(authentication, object, configAttributes);

            switch (result) {
                /*xxx: 有一个通过,就算通过*/
                case AccessDecisionVoter.ACCESS_GRANTED:
                    return;

                case AccessDecisionVoter.ACCESS_DENIED:
                    deny++;
                    break;

                default:
                    break;
            }
        }
    }
}
/*xxx: 少数服从多数的投票器,如果通过的票数,多于反对的票数,则整体通过,反之同理*/
public class ConsensusBased extends AbstractAccessDecisionManager {
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
        int grant = 0;
        int deny = 0;

        for (AccessDecisionVoter voter : getDecisionVoters()) {
            int result = voter.vote(authentication, object, configAttributes);
            /*xxx: 记录通过的票数,以及不通过的票数*/
            switch (result) {
                case AccessDecisionVoter.ACCESS_GRANTED:
                    grant++;
                    break;

                case AccessDecisionVoter.ACCESS_DENIED:
                    deny++;
                    break;

                default:
                    break;
            }
        }

        if (grant > deny) {
            return;
        }

        if (deny > grant) {
            /*xxx: 抛授权异常...*/
        }
    }
}
/*xxx: 悲观投票器,所有的投票器赞成,才算通过。有一个反对,则不通过*/
public class UnanimousBased extends AbstractAccessDecisionManager {
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> attributes) throws AccessDeniedException {
        int grant = 0;
        for (AccessDecisionVoter voter : getDecisionVoters()) {
            int result = voter.vote(authentication, object, singleAttributeList);
            
            switch (result) {
                case AccessDecisionVoter.ACCESS_GRANTED:
                    grant++;
                    break;

                case AccessDecisionVoter.ACCESS_DENIED:
                    /*xxx: 投反对票,直接抛出资源访问异常*/
                    throw new AccessDeniedException(messages.getMessage(
                            "AbstractAccessDecisionManager.accessDenied",
                            "Access is denied"));
                default:
                    break;
            }
        }
    }
}

# 投票器

/*xxx: 访问策略投票器*/
public interface AccessDecisionVoter<S> {
    int ACCESS_GRANTED = 1;
    int ACCESS_ABSTAIN = 0;
    int ACCESS_DENIED = -1;

    boolean supports(Class<?> clazz);

    int vote(Authentication authentication, S object,
             Collection<ConfigAttribute> attributes);
}
public class RoleVoter implements AccessDecisionVoter<Object> {
    private String rolePrefix = "ROLE_";

    public boolean supports(Class<?> clazz) {
        return true;
    }

    public boolean supports(ConfigAttribute attribute) {
        if ((attribute.getAttribute() != null)
                && attribute.getAttribute().startsWith(getRolePrefix())) {
            return true;
        }
        else {
            return false;
        }
    }

    public int vote(Authentication authentication, Object object,
                    Collection<ConfigAttribute> attributes) {
        int result = ACCESS_ABSTAIN;
        Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);

        for (ConfigAttribute attribute : attributes) {
            if (this.supports(attribute)) {
                result = ACCESS_DENIED;
                for (GrantedAuthority authority : authorities) {
                    /*xxx: 判断是否有对应的角色信息*/
                    if (attribute.getAttribute().equals(authority.getAuthority())) {
                        return ACCESS_GRANTED;
                    }
                }
            }
        }

        return result;
    }
}

# 授权--过程设计

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements
      Filter {
    private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";

    /*xxx: 默认过滤链的最后一个过滤器*/
    /*xxx: 抛出的异常,会被倒数第二个过滤器ExceptionTranslationFilter,进行处理*/
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        /*xxx: 将请求封装为 过滤器执行器*/
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        /*xxx: 调用执行器*/
        invoke(fi);
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        /*xxx: 进行资源投票,如果授权未通过,则会抛错 */
        /*xxx: 在这个阶段,也可能抛 认证异常,如果之前未进行认证*/
        InterceptorStatusToken token = super.beforeInvocation(fi);

        /*xxx: 对拦截器状态令牌,进行后置处理*/
        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        }
        finally {
            super.finallyInvocation(token);
        }
    }
}

public class FilterInvocation {
    private FilterChain chain;
    private HttpServletRequest request;
    private HttpServletResponse response;
}
public abstract class AbstractSecurityInterceptor implements InitializingBean,
      ApplicationEventPublisherAware, MessageSourceAware {
    /*xxx: 返回 拦截器状态令牌 */
    protected InterceptorStatusToken beforeInvocation(Object object) {
        /*xxx: 获取SecurityMedataSource元数据*/
        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
                .getAttributes(object);

        /*xxx: 通过授权管理器,进行授权 */
        try {
            this.accessDecisionManager.decide(authenticated, object, attributes);
        }catch (AccessDeniedException accessDeniedException) {
            throw accessDeniedException;
        }
    }
}
public interface SecurityMetadataSource extends AopInfrastructureBean {
    Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException;
}

public class DefaultFilterInvocationSecurityMetadataSource implements
        FilterInvocationSecurityMetadataSource {
    
    private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;

    public DefaultFilterInvocationSecurityMetadataSource(
            LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap) {

        this.requestMap = requestMap;
    }
    
    public Collection<ConfigAttribute> getAttributes(Object object) {
        final HttpServletRequest request = ((FilterInvocation) object).getRequest();
        for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
                .entrySet()) {
            if (entry.getKey().matches(request)) {
                return entry.getValue();
            }
        }
        return null;
    }
}

# 授权--元数据准备流程

public abstract class WebSecurityConfigurerAdapter implements
      WebSecurityConfigurer<WebSecurity> {
    protected void configure(HttpSecurity http) throws Exception {
        http
                /*xxx: 链式配置,根据配置拦截器的先后顺序,依次生效*/
                .authorizeRequests().antMatchers("/test/**").hasAuthority("ROLE_TEST")
                .anyRequest().authenticated()
                .and()
                /*xxx: 这个匹配拦截器,是针对过滤链的*/
                /*xxx: 上方的匹配拦截器,是针对授权过程的*/
                .antMatcher("/mySecurity/**");
    }
}
public final class HttpSecurity extends
        AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
        implements SecurityBuilder<DefaultSecurityFilterChain>,
        HttpSecurityBuilder<HttpSecurity> {
    /*xxx: 通过调用其 anyRequest(),antMatchers(),regexMatchers(),等方法来匹配系统的URL*/
    public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
            throws Exception {
        ApplicationContext context = getContext();
        return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context))
                .getRegistry();
    }

    private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
            C configurer) throws Exception {
        C existingConfig = (C) getConfigurer(configurer.getClass());
        if (existingConfig != null) {
            return existingConfig;
        }
        /*xxx: 收集配置,略*/
        return apply(configurer);
    }
}
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
      extends
      AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
    private final ExpressionInterceptUrlRegistry REGISTRY;

    public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
        this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
    }
}

public class ExpressionInterceptUrlRegistry
        extends
        AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {
}

abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
        extends AbstractConfigAttributeRequestMatcherRegistry<T> {
    
}

public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends
        AbstractRequestMatcherRegistry<C> {
    /*xxx: 通过url描述信息进行拦截*/
    private List<UrlMapping> urlMappings = new ArrayList<>();

    /*xxx: 未配置完成的映射匹配 */
    private List<RequestMatcher> unmappedMatchers;
    
    final void addMapping(UrlMapping urlMapping) {
        /*xxx: 每次添加映射的时候,都会将未完成的映射匹配器置空,这非常重要*/
        this.unmappedMatchers = null;
        this.urlMappings.add(urlMapping);
    }

    /*xxx: 可以指定优先级的方式指定拦截器*/
    /*xxx: PermitAllSupport.permitAll()方法有调用*/
    final void addMapping(int index, UrlMapping urlMapping) {
        this.urlMappings.add(index, urlMapping);
    }

    final LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> createRequestMap() {
        if (unmappedMatchers != null) {
            /*xxx: 抛错...*/
        }
        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
        for (UrlMapping mapping : getUrlMappings()) {
            RequestMatcher matcher = mapping.getRequestMatcher();
            Collection<ConfigAttribute> configAttrs = mapping.getConfigAttrs();
            requestMap.put(matcher, configAttrs);
        }
        return requestMap;
    }
}

static final class UrlMapping {
    private RequestMatcher requestMatcher;
    private Collection<ConfigAttribute> configAttrs;
}

public abstract class AbstractRequestMatcherRegistry<C> {
    private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
    
    public C anyRequest() {
        return requestMatchers(ANY_REQUEST);
    }

    public C requestMatchers(RequestMatcher... requestMatchers) {
        return chainRequestMatchers(Arrays.asList(requestMatchers));
    }

    public C antMatchers(String... antPatterns) {
        return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
    }

    protected abstract C chainRequestMatchers(List<RequestMatcher> requestMatchers);
}

# 授权元数据-逻辑设计

public interface ConfigAttribute extends Serializable {
    String getAttribute();
}

public class SecurityConfig implements ConfigAttribute {
    private final String attrib;

    @Override
    public String getAttribute() {
        return this.attrib;
    }
}

class WebExpressionConfigAttribute implements ConfigAttribute,
        EvaluationContextPostProcessor<FilterInvocation> {
    private final Expression authorizeExpression;

    @Override
    public String getAttribute() {
        return null;
    }

    @Override
    /*xxx: 通过这个方法进行辅助投票的*/
    public EvaluationContext postProcess(EvaluationContext context, FilterInvocation fi) {
        return this.postProcessor == null ? context
                : this.postProcessor.postProcess(context, fi);
    }
}

# 关键过滤器流程设计

# 异常转换器流程设计

public class ExceptionTranslationFilter extends GenericFilterBean {
    /*xxx: 默认情况下,这个异常转换的过滤器,位于默认过滤器链的倒数第二个位置,用于处理 授权失败的情况*/
    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);
        }catch (Exception ex) {
            /*xxx: 提取异常类型*/
            Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
            RuntimeException ase = (AuthenticationException) throwableAnalyzer
                    .getFirstThrowableOfType(AuthenticationException.class, causeChain);

            if (ase == null) {
                ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
                        AccessDeniedException.class, causeChain);
            }

            if (ase != null) {
                /*xxx: 对springSecurity的异常进行处理 */
                handleSpringSecurityException(request, response, chain, ase);
            }else{
                /*xxx: 抛出其它原生错误... 略*/
            }
        }
    }

    /*xxx: 对于出现的错误,有两种处理方式,分别对应了 认证错误(认证错误包含了 匿名认证 的情况),以及 授权错误*/
    private void handleSpringSecurityException(HttpServletRequest request,
                                               HttpServletResponse response, FilterChain chain, RuntimeException exception)
            throws IOException, ServletException {
        if (exception instanceof AuthenticationException) {
            /*xxx: 第一种,通过认证端口,处理异常 */
            sendStartAuthentication(request, response, chain,
                    (AuthenticationException) exception);
        }else if (exception instanceof AccessDeniedException) {
            if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {
                /*xxx: 第一种,通过认证端口,处理异常(当前未匿名认证时,采用该方式) */
                sendStartAuthentication(request, response, chain,
                        new InsufficientAuthenticationException("..."));
            }else{
                /*xxx: 第二种,通过 AccessDeniedHandler进行处理(内部)*/
                accessDeniedHandler.handle(request, response,
                        (AccessDeniedException) exception);
            }
        }
    }

    protected void sendStartAuthentication(HttpServletRequest request,
                                           HttpServletResponse response, FilterChain chain,
                                           AuthenticationException reason) throws ServletException, IOException {
        /*xxx: 该方法,如果之前没有会话信息(即之前的会话请求中,没有sessionId),则会维持会话信息 */
        /*xxx: 通俗说,就是会创建session*/
        requestCache.saveRequest(request, response);

        /*xxx: cas客户端登录,貌似就是用的这种方式*/
        authenticationEntryPoint.commence(request, response, reason);
    }
}
/*xxx: ExceptionTranslationFilter 用于启动身份验证方案*/
   /*xxx: 认证入口点 */
public interface AuthenticationEntryPoint {
    /*xxx: 开始认证*/
    void commence(HttpServletRequest request, HttpServletResponse response,
                  AuthenticationException authException) throws IOException, ServletException;
}
/*xxx: 进行登录,支持两种策略: forward与redirect*/
public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoint,
        InitializingBean {
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {
        String redirectUrl = null;
        if (useForward) {
            redirectUrl = buildHttpsRedirectUrlForRequest(request);
            RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);
            dispatcher.forward(request, response);
            return;
        }else {
            redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
            redirectStrategy.sendRedirect(request, response, redirectUrl);
        }
    }
}
/*xxx: 内部处理 accessDenied的策略*/
public interface AccessDeniedHandler {
    void handle(HttpServletRequest request, HttpServletResponse response,
                AccessDeniedException accessDeniedException) throws IOException,
            ServletException;
}

public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       AccessDeniedException accessDeniedException) throws IOException,
            ServletException {
        if (!response.isCommitted()) {
            /*xxx: 有错误页的情况下,转发到错误页面*/
            if (errorPage != null) {
                response.setStatus(HttpStatus.FORBIDDEN.value());
                RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
                dispatcher.forward(request, response);
            }else {
                response.sendError(HttpStatus.FORBIDDEN.value(),
                        HttpStatus.FORBIDDEN.getReasonPhrase());
            }
        }
    }
}

# session控制过滤器

public final class HttpSecurity extends
      AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
      implements SecurityBuilder<DefaultSecurityFilterChain>,
      HttpSecurityBuilder<HttpSecurity> {
    public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
        return getOrApply(new SessionManagementConfigurer<>());
    }
}

public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
        extends AbstractHttpConfigurer<SessionManagementConfigurer<H>, H> {
    @Override
    public void configure(H http) throws Exception {
        SecurityContextRepository securityContextRepository = http
                .getSharedObject(SecurityContextRepository.class);
        
        SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
                securityContextRepository, getSessionAuthenticationStrategy(http));

        http.addFilter(sessionManagementFilter);

        if (isConcurrentSessionControlEnabled()) {
            ConcurrentSessionFilter concurrentSessionFilter = createConccurencyFilter(http);

            concurrentSessionFilter = postProcess(concurrentSessionFilter);
            http.addFilter(concurrentSessionFilter);
        }
    }

    private boolean isConcurrentSessionControlEnabled() {
        return this.maximumSessions != null;
    }
}
public class SessionManagementFilter extends GenericFilterBean {
    private final SecurityContextRepository securityContextRepository;
    private SessionAuthenticationStrategy sessionAuthenticationStrategy;
    
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        /*xxx: 认证上下文没有当前请求信息,才会进行处理*/
        if (!securityContextRepository.containsContext(request)) {
            Authentication authentication = SecurityContextHolder.getContext()
                    .getAuthentication();
            if (authentication != null && !trustResolver.isAnonymous(authentication)) {
                sessionAuthenticationStrategy.onAuthentication(authentication,
                        request, response);
                
                securityContextRepository.saveContext(SecurityContextHolder.getContext(),
                        request, response);
            }else{
                if (request.getRequestedSessionId() != null
                        && !request.isRequestedSessionIdValid()) {
                    invalidSessionStrategy
                            .onInvalidSessionDetected(request, response);
                }
            }
        }
        chain.doFilter(request, response);
    }
}
public class ConcurrentSessionFilter extends GenericFilterBean {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        HttpSession session = request.getSession(false);

        /*xxx: 剔除本身session*/
        if (session != null) {
            SessionInformation info = sessionRegistry.getSessionInformation(session
                    .getId());

            if (info != null) {
                if (info.isExpired()) {
                    doLogout(request, response);
                }else{
                    sessionRegistry.refreshLastRequest(info.getSessionId());
                }
            }
        }
        chain.doFilter(request, response);
    }
}

# 过滤器对流程控制的影响

  • 过滤器可以在内部终止流程;
  • 过滤器可以编排成链,从而完成指定的功能;
  • 过滤链的执行逻辑结构为树结构的层级遍历,流程既可以在叶子节点终结,也可以在根节点终结;

# AI精要笔记

# WebSecurityConfigurerAdapter 三个configure的作用

# 1. configure(WebSecurity web)

这个方法主要用于配置哪些请求应该被忽略,即不需要通过Spring Security过滤器链进行处理。通常用来排除静态资源(如CSS、JS文件)、公共访问点(比如登录页面或健康检查端点)等。

常见用途:

  • 忽略某些URL模式的安全检查,比如静态资源路径。
  • 当你有部分请求不希望受到Spring Security的保护时,可以在这里配置。

示例代码:

class Test{
        @Override
        public void configure(WebSecurity web)throws Exception{
            web.ignoring().antMatchers("/resources/**","/static/**","/css/**","/js/**","/images/**");
        }
}

# 2. configure(HttpSecurity http)

这个方法允许你详细配置基于HTTP的特定安全选项,包括但不限于认证机制、授权规则、HTTPS要求、CSRF保护、Session管理等。

常见用途:

  • 配置URL访问权限控制,比如哪些URL需要认证,哪些角色可以访问特定的URL。
  • 设置登录和登出的行为。
  • 启用或禁用特定的安全特性,如CSRF保护、HTTP严格传输安全(HSTS)等。

示例代码:

class Test{
    @Override
    protected void configure(HttpSecurity http)throws Exception{
        http
        .authorizeRequests()
        .antMatchers("/admin/**").hasRole("ADMIN")
        .antMatchers("/user/**").hasAnyRole("USER","ADMIN")
        .anyRequest().permitAll()
        .and()
        .formLogin()
        .loginPage("/login")
        .permitAll()
        .and()
        .logout()
        .permitAll();
        }
}

# 3. configure(AuthenticationManagerBuilder auth)

这个方法用于配置用户认证相关的设置,包括但不限于内存中的用户、JDBC用户存储、LDAP以及使用自定义的UserDetailsService实现。

常见用途:

  • 配置内存中用户的账户信息。
  • 使用数据库或LDAP服务器来验证用户身份。
  • 注册一个自定义的UserDetailsService,用于加载用户特定的数据。

示例代码:

class Test{
    @Override
    protected void configure(AuthenticationManagerBuilder auth)throws Exception{
        auth
        .inMemoryAuthentication()
        .withUser("user").password("{noop}password").roles("USER")
        .and()
        .withUser("admin").password("{noop}admin").roles("ADMIN");
        }
}

注意:在实际应用中,密码应当以加密的形式存储,这里使用了{noop}作为示例,表示没有对密码进行编码,仅用于测试环境。

以上是WebSecurityConfigurerAdapter提供的三个主要的configure方法的作用及其典型应用场景。然而,从Spring Security 5.7.0-M2开始,官方推荐直接使用SecurityFilterChain Bean方式来替代继承WebSecurityConfigurerAdapter的方式进行配置。这是因为直接配置的方法更加灵活,并且避免了一些潜在的继承带来的复杂性。

# 三个configure之间的联系

  • webSecurity-> httpSecurity -> authenticationManagerBuilder ,层层递进的关系;每个层次都需经历: init-> config -> performBuild;
  • webSecurity对应于 WebSecurityConfigurer配置器,其对应的 SecurityConfigurer的泛型为 : <Filter, WebSecurity>
    • 从泛型理解,其作用是通过 构造器构建Filter, 跟踪源码轨迹,可以知道,其最终返回的 FilterChainProxy;
    • 一个FilterChainProxy可以包含多个过滤链SecurityFilterChain,可以是静态的,也可以是动态构建的;
  • httpSecurity对应于 AbstractHttpConfigurer配置器, 其对应的 SecurityConfigurer的泛型为 : <DefaultSecurityFilterChain, B>;
    • 从泛型理解,其作用是通过 构造器构建SecurityFilterChain, 跟踪源码轨迹,可以知道,其最终返回的 DefaultSecurityFilterChain;
    • DefaultSecurityFilterChain包含了多个过滤器,由构造器在配置的过程中加入;
  • AuthenticationManagerBuilder将由用户自定义配置,与前两层级相比,未提供配置化构建器。其对应的 SecurityConfigurer的泛型为 : <AuthenticationManager, AuthenticationManagerBuilder>;
    • 其主要通过 ProviderManagerBuilder的authenticationProvider方法实现配置器的收集(其实也就是自身);
    • 从泛型理解,起作用是通过 构造器构建AuthenticationManager,跟踪源码轨迹,可以知道,其最终返回的 ProviderManager;
    • 一个ProviderManager可以包含多个AuthenticationProvider,ProviderManager包含父子层级关系;一般情况下包含两级;
    • httpSecurity的authenticationProvider方法就是配置AuthenticationManagerBuilder的;
    • 由定义可知,authenticationManager一般默认接口,通过配置authenticationProvider的方式进行扩展;
  • 三个层级的构建器,由WebSecurityConfigurerAdapter联系起来了,因此除了设计上的优势,WebSecurityConfigurerAdapter便是最关键的类;
//O, 代表B将要构建的对象
//B, 代表构建出O的配置器
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {

	void init(B builder) throws Exception;

	void configure(B builder) throws Exception;
}

# 项目关键要素

# 默认情况下,登录密码的校验

  • 校验过程
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    private PasswordEncoder passwordEncoder;
    
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            String presentedPassword = authentication.getCredentials().toString();
            if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }
}
  • 密码校验器的设置过程
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {

    @Autowired
    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
        ObjectPostProcessor<Object> objectPostProcessor = (ObjectPostProcessor)context.getBean(ObjectPostProcessor.class);
        WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(context);
        //xxx:从上下文中注入passwordEncoder,并以该encoder作为后续组件的组成部分,构建执行逻辑
        this.authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
    }
    
}