【问题标题】:Spring Session: How to create separate session management policies for different URLsSpring Session:如何为不同的 URL 创建单独的会话管理策略
【发布时间】:2020-11-25 10:59:04
【问题描述】:

在使用spring session的spring boot项目中,如何为不同的url配置两个session管理策略?

  1. 对于 Angular 前端,我想使用默认实现并创建 X-Auth-Token 令牌(如果需要,创建会话)
  2. 但对于暴露的 API 端点,我想使用无状态会话管理

我尝试了以下配置,但根本没有创建会话。我认为第二个块正在覆盖sessionCreationPolicy,因为它位于末尾

SecurityConfig.java

@Override
    public void configure(HttpSecurity http) throws Exception
    {


//Requests from angular app
http.authorizeRequests()
        .antMatchers("/", "/login","/api/v1/user/login", "/api/v1/user/authenticate", "/api/v1/user/logout", "/api/v1/health/find/status").permitAll()
        .antMatchers("/api/v1/person/**").hasAnyAuthority(ROLE_USER)
        .and()
        .httpBasic()
        .and()
    .exceptionHandling().authenticationEntryPoint(customBasicAuthenticationEntryPoint)
        .and()
        .logout()
        .invalidateHttpSession(true).clearAuthentication(true)
        .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);

//Requests from external systems
http.authorizeRequests()
        .antMatchers("/api/v1/external/**").hasAnyAuthority(ROLE_API_USER)
        .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

}

更新

为 API 端点和 Angular 应用添加了自定义 WebSecurity 配置器适配器,如下所示。添加后,API 端点不会创建会话,但 Angular HTTP 请求也不会创建会话

@Configuration
@EnableWebSecurity
public class SecurityConfig
{
 ....

    @Configuration
    @Order(1)
    public class ExternalApiSecurityConfig extends WebSecurityConfigurerAdapter
    {
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth)
        {
            auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).eraseCredentials(true);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception
        {
            http.authorizeRequests()
                    .antMatchers("/api/v1/search/**").hasAnyAuthority(ROLE_API_USER, ROLE_SYS_ADMIN)
                    .and()
                    .httpBasic()
                    .and()
                    .exceptionHandling().authenticationEntryPoint(customBasicAuthenticationEntryPoint)
                    .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

            http.headers()
                    .frameOptions().disable();

            // Uses CorsConfigurationSource bean defined below
            http.cors().configurationSource(corsConfigurationSource());

            http.csrf().disable();
        }
    }

    @Configuration
    @Order(2)
    public class DefaultSecurityConfig extends WebSecurityConfigurerAdapter
    {
        @Bean(BeanIds.AUTHENTICATION_MANAGER)
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception
        {
            return super.authenticationManagerBean();
        }

        @Override
        public void configure(WebSecurity webSecurity)
        {
            webSecurity.ignoring().antMatchers("/static/**");
        }

        @Override
        public void configure(HttpSecurity http) throws Exception
        {
    http.authorizeRequests()
        .antMatchers("/", "/login","/api/v1/user/login", "/api/v1/user/authenticate", "/api/v1/user/logout", "/api/v1/health/find/status").permitAll()
        .antMatchers("/api/v1/person/**").hasAnyAuthority(ROLE_USER)
        .and()
        .httpBasic()
        .and()
    .exceptionHandling().authenticationEntryPoint(customBasicAuthenticationEntryPoint)
        .and()
        .logout()
        .invalidateHttpSession(true).clearAuthentication(true)
        .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        }

        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth)
        {
            auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).eraseCredentials(true);
            auth.authenticationProvider(getDaoAuthenticationProvider()).eraseCredentials(true);
        }

    }
 ....
}

【问题讨论】:

    标签: spring-boot spring-security spring-session


    【解决方案1】:

    您需要创建两个不同的过滤器链,当您创建 WebSecurityConfigurerAdapter 时,您正在创建一个包含所需安全 http 过滤器的代理过滤器链。

    如果您在日志中查找DefaultSecurityFilterChain,您应该能够在启动时看到这一点,您应该再次看到类似于Creating filter chain: any request, [Filters...] 的日志记录语句

    DefaultSecurityFilterChain 默认情况下是任何请求 (/**) 都会通过它。为了能够分离出对不同过滤器链的请求,您需要创建第二个 WebSecurityConfigurerAdapter,将其范围限定为任何路径并将您想要的配置应用于每个路径。

    一个问题是您还需要对适配器应用更高的优先级,最终代码看起来像这样。

      @Order(Ordered.HIGHEST_PRECEDENCE + 1)
        @Configuration
        public static class ApiSecurity extends WebSecurityConfigurerAdapter {
            @Override
            protected void configure(final HttpSecurity http) throws Exception {
                http.antMatcher("/api/v1/external/**")
                        .authorizeRequests()
                        .anyRequest()
                        .hasAnyAuthority(ROLE_API_USER)
                        .and()
                        .sessionManagement()
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
            }
        }
    
        @Order(Ordered.HIGHEST_PRECEDENCE + 2)
        @Configuration
        public static class DefaultSecurityFilter extends WebSecurityConfigurerAdapter {
            @Override
            protected void configure(final HttpSecurity http) throws Exception {
                //Requests from angular app
                http.authorizeRequests()
                        .antMatchers("/", "/login","/api/v1/user/login", "/api/v1/user/authenticate", "/api/v1/user/logout", "/api/v1/health/find/status").permitAll()
                        .antMatchers("/api/v1/person/**").hasAnyAuthority(ROLE_USER)
                        .and()
                        .httpBasic()
                        .and()
                        .exceptionHandling().authenticationEntryPoint(customBasicAuthenticationEntryPoint)
                        .and()
                        .logout()
                        .invalidateHttpSession(true).clearAuthentication(true)
                        .and().sessionManagement()
                        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
            }
        }
    

    在此之后,您应该会看到在日志中创建了两个 DefaultSecurityFilterChain,第一个是 "/api/v1/external/**",第二个是 any request,用于捕获任何其他请求。

    【讨论】:

    • @Jadda 确保限定过滤器之一。查看您的更新,只有其中一个过滤器会被击中。在我的apiFilter 中,您可以看到http.antMatcher("/api/v1/external/**") 这一行,它将整个过滤器的范围限定为仅在此路径上触发。
    猜你喜欢
    • 2016-12-03
    • 2016-08-10
    • 2020-02-22
    • 2017-09-17
    • 2015-10-31
    • 1970-01-01
    • 1970-01-01
    • 2019-09-18
    • 2015-06-13
    相关资源
    最近更新 更多