将 spring-security 与 spring-webflux 一起使用时禁用 WebSession 创建

Posted

技术标签:

【中文标题】将 spring-security 与 spring-webflux 一起使用时禁用 WebSession 创建【英文标题】:Disable WebSession creation when using spring-security with spring-webflux 【发布时间】:2019-09-27 02:41:08 【问题描述】:

我正在运行一个带有 rest api 的无状态 spring-boot 应用程序,并希望按照https://www.baeldung.com/spring-security-session 的描述禁用 WebSessions 的创建

我创建了自己的不存储会话的 WebSessionManager。

   @Bean
   public WebSessionManager webSessionManager() 
       return new WebSessionManager() 
           @Override
           @NonNull
           public Mono<WebSession> getSession(@NonNull final ServerWebExchange exchange) 
               return Mono.just(new WebSession() 

                   @Override
                   @NonNull
                   public String getId() 
                       return "";
                   

                   @Override
                   @NonNull
                   public Map<String, Object> getAttributes() 
                       return new HashMap<>();
                   

                   @Override
                   public void start() 
                   

                   @Override
                   public boolean isStarted() 
                       return true;
                   

                   @Override
                   @NonNull
                   public Mono<Void> changeSessionId() 
                       return Mono.empty();
                   

                   @Override
                   @NonNull
                   public Mono<Void> invalidate() 
                       return Mono.empty();
                   

                   @Override
                   @NonNull
                   public Mono<Void> save() 
                       return Mono.empty();
                   

                   @Override
                   public boolean isExpired() 
                       return false;
                   

                   @Override
                   @NonNull
                   public Instant getCreationTime() 
                       return Instant.now();
                   

                   @Override
                   @NonNull
                   public Instant getLastAccessTime() 
                       return Instant.now();
                   

                   @Override
                   public void setMaxIdleTime(@NonNull final Duration maxIdleTime) 
                   

                   @Override
                   @NonNull
                   public Duration getMaxIdleTime() 
                       return Duration.ofMinutes(1);
                   
               );
           
       ;
   

它有效,但我想知道是否有更好的方法来不创建会话。

【问题讨论】:

【参考方案1】:

Issue #6552: Session Creation Policy with Webflux Security 将由 Spring 团队修复。

问题是每个请求都会调用请求缓存,以查看是否有保存的值要重播,因此正在为每个请求查找 WebSession。由于使用无效会话 id 查找 WebSession,因此 Spring WebFlux 使 SESSION cookie 无效。 ~rwinch

我创建了 gh-7157 来限制何时访问请求缓存(以及 WebSession)。同时,如果您不需要请求缓存,可以使用以下命令禁用它:

http
.requestCache()
    .requestCache(NoOpServerRequestCache.getInstance());

您可以在Issue #7157 ServerRequestCacheWebFilter causes WebSession to be read every request 中跟踪修补进度。

另外DarrenJiang1990 建议更完整的解决方案:

.and().securityContextRepository(NoOpServerSecurityContextRepository.getInstance())

WebFlux 应用程序中的安全上下文存储在 ServerSecurityContextRepository 中。其默认使用的 WebSessionServerSecurityContextRepository 实现将上下文存储在会话中。配置 NoOpServerSecurityContextRepository 将使我们的应用程序无状态


(以前的解决方法)

除了覆盖WebSessionManager,您还可以禁用所有安全功能,并用您的自定义实现替换authenticationManagersecurityContextRepository ,去除基于会话的功能:

@Configuration
public class SecurityConfiguration 
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) 
        // Disable default security.
        http.httpBasic().disable();
        http.formLogin().disable();
        http.csrf().disable();
        http.logout().disable();

        // Add custom security.
        http.authenticationManager(this.authenticationManager);
        http.securityContextRepository(this.securityContextRepository);

        // Disable authentication for `/auth/**` routes.
        http.authorizeExchange().pathMatchers("/auth/**").permitAll();
        http.authorizeExchange().anyExchange().authenticated();

        return http.build();
    

更多信息:Spring webflux custom authentication for API 。

【讨论】:

感谢您的回复!不过,SecurityWebFilterChain 无法解决创建网络会话的问题。 我已经在 Github 问题上写了关于这个 SO 问题的评论,希望他们能尽快将 .sessionCreationPolicy(SessionCreationPolicy.STATELESS) 修补到 WebFlux 中。 Spring 团队正在努力解决这个问题。我已经用建议的解决方法更新了答案。【参考方案2】:

我通过以下技巧禁用了 WebSessionManager

  @Bean
  public WebSessionManager webSessionManager() 
    // Emulate SessionCreationPolicy.STATELESS
    return exchange -> Mono.empty();
  

所有其他解决方案都对我没有帮助。

【讨论】:

【参考方案3】:

使用:NoOpServerSecurityContextRepository 用于此目的。

@Configuration
@EnableWebFluxSecurity
@ComponentScan(value = "my.package.security")
public class SpringSecurityConfig2 
    @Autowired private MyHeaderExchangeMatcher myHeaderExchangeMatcher;
    @Autowired private MyReactiveAuthenticationManager myReactiveAuthenticationManager;
    @Autowired private MyTokenAuthenticationConverter myTokenAuthenticationConverter;

    @Bean
    SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) 
        http.httpBasic().disable().formLogin().disable().csrf().disable().logout().disable();

        http...
                .addFilterAt(webFilter(), SecurityWebFiltersOrder.AUTHORIZATION)
                ...;

        return http.build();
    

    @Bean
    public AuthenticationWebFilter webFilter() 
        AuthenticationWebFilter authenticationWebFilter =
                new AuthenticationWebFilter(myReactiveAuthenticationManager);
        authenticationWebFilter.setServerAuthenticationConverter(myTokenAuthenticationConverter);
        authenticationWebFilter.setRequiresAuthenticationMatcher(myHeaderExchangeMatcher);

        // NoOpServerSecurityContextRepository is used to for stateless sessions so no session or state is persisted between requests.
        // The client must send the Authorization header with every request.
        NoOpServerSecurityContextRepository sessionConfig = NoOpServerSecurityContextRepository.getInstance();

        authenticationWebFilter.setSecurityContextRepository(sessionConfig);
        return authenticationWebFilter;
    

【讨论】:

【参考方案4】:

你试过了吗:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

?

【讨论】:

这个应用程序使用的是 spring-webflux 所以我们没有 HttpSecurity 而是 ServerHttpSecurity 所以它没有工作。 我的错,我打开了多个项目,我确信这是可行的解决方案!我和 spring-webflux 的家伙有点来回折腾,他们有很多关于处理和安全的正在进行的项目......

以上是关于将 spring-security 与 spring-webflux 一起使用时禁用 WebSession 创建的主要内容,如果未能解决你的问题,请参考以下文章

Spring-Security认证和授权

将标头添加到 oauth/token 响应(spring-security)

spring-security权限管理学习目标

您如何在代理后面使用 spring-security OAuth2,但仅将 ForwardedHeaderTransformer 用于 OAuth 组件

Spring-Security

spring-security.xml 文件的解析错误