简体   繁体   中英

Disable WebSession creation when using spring-security with spring-webflux

I am running a stateless spring-boot application with a rest api and want to disable the creation of WebSessions as described https://www.baeldung.com/spring-security-session

I have created my own WebSessionManager that does not store the session.

   @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);
                   }
               });
           }
       };
   }

It works but I wonder if there is a better way to not create a session.

The Issue #6552: Session Creation Policy with Webflux Security is going to be fixed by Spring team.

The problem is that the request cache is being invoked for every request to see if there is a value saved to replay and thus the WebSession is being looked up for every request. Since the WebSession is being looked up with an invalid session id, Spring WebFlux invalidates the SESSION cookie. ~ rwinch

I created gh-7157 to limit when the request cache is being accessed (and thus the WebSession). In the meantime if you don't need the request cache, you can disable it using:

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

You can track the progress of patching in Issue #7157 ServerRequestCacheWebFilter causes WebSession to be read every request .

Additionally DarrenJiang1990 is suggesting more complete solution:

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

The security context in a WebFlux application is stored in a ServerSecurityContextRepository. Its WebSessionServerSecurityContextRepository implementation, which is used by default, stores the context in session. Configuring a NoOpServerSecurityContextRepository instead would make our application stateless


(Previous workaround)

Additionally to overriding WebSessionManager you can disable all the security features and replace the authenticationManager & securityContextRepository with your custom implementations stripping out the session-based functionalities:

@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();
    }
}

Further information: Spring webflux custom authentication for API .

I've disabled WebSessionManager by the following trick

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

All other solutions didn't help for me.

Use the: NoOpServerSecurityContextRepository intended for this purpose.

@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;
    }
}

Did you try:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

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

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

?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM