package com.kongjs.admin.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.rsocket.server.RSocketServerCustomizer;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.rsocket.EnableRSocketSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.*;
import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.HeaderWriterServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.WebSessionServerLogoutHandler;
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;
import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;
import org.springframework.security.web.server.csrf.XorServerCsrfTokenRequestAttributeHandler;
import org.springframework.security.web.server.header.ClearSiteDataServerHttpHeadersWriter;
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
import java.util.List;
@EnableCaching
@EnableWebFluxSecurity
@EnableRSocketSecurity
@EnableRedisWebSession
@Configuration
public class WebSecurityConfig {
@Autowired
private CacheManager cacheManager;
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder().username("user").password("user").roles("USER").build();
return new MapReactiveUserDetailsService(user);
}
private void addFilter(ServerHttpSecurity httpSecurity, ReactiveUserDetailsService userDetailsService) {
ReactivePreAuthenticatedAuthenticationManager authenticationManager = new ReactivePreAuthenticatedAuthenticationManager(userDetailsService);
ServerAuthenticationSuccessHandler authenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler("/");
ServerAuthenticationFailureHandler authenticationFailureHandler = new RedirectServerAuthenticationFailureHandler("/");
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(authenticationManager);
authenticationFilter.setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/login"));
authenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
authenticationFilter.setServerAuthenticationConverter(new LoginAuthenticationConverter());
authenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
authenticationFilter.setSecurityContextRepository(NoOpServerSecurityContextRepository.getInstance());
httpSecurity.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.FORM_LOGIN);
httpSecurity.exceptionHandling(exceptionHandling -> exceptionHandling.authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/login")).accessDeniedHandler(new HttpStatusServerAccessDeniedHandler(HttpStatus.UNAUTHORIZED)));
}
private void authorize(ServerHttpSecurity httpSecurity) {
httpSecurity.authorizeExchange(exchanges -> exchanges.pathMatchers("/login").permitAll().anyExchange().authenticated());
}
private void login(ServerHttpSecurity httpSecurity) {
httpSecurity.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable).formLogin(ServerHttpSecurity.FormLoginSpec::disable);
}
private void logout(ServerHttpSecurity httpSecurity) {
DelegatingServerLogoutHandler logoutHandler = new DelegatingServerLogoutHandler(new SecurityContextServerLogoutHandler(), new WebSessionServerLogoutHandler(), new HeaderWriterServerLogoutHandler(new ClearSiteDataServerHttpHeadersWriter(ClearSiteDataServerHttpHeadersWriter.Directive.ALL)));
httpSecurity.logout((logout) -> logout.logoutHandler(logoutHandler));
}
private void cors(ServerHttpSecurity httpSecurity) {
httpSecurity.cors(ServerHttpSecurity.CorsSpec::disable);
}
private void csrf(ServerHttpSecurity httpSecurity) {
httpSecurity.csrf(csrf -> csrf.csrfTokenRepository(new CookieServerCsrfTokenRepository()).csrfTokenRequestHandler(new XorServerCsrfTokenRequestAttributeHandler()));
}
private void requestCache(ServerHttpSecurity httpSecurity) {
httpSecurity.requestCache(ServerHttpSecurity.RequestCacheSpec::disable);
}
private void securityContextRepository(ServerHttpSecurity httpSecurity) {
httpSecurity.securityContextRepository(NoOpServerSecurityContextRepository.getInstance());
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Order(Ordered.HIGHEST_PRECEDENCE)
@Bean
public SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {
http.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")).authorizeExchange((exchanges) -> exchanges.anyExchange().authenticated());
return http.build();
}
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity, ReactiveUserDetailsService userDetailsService) throws Exception {
authorize(httpSecurity);
login(httpSecurity);
logout(httpSecurity);
cors(httpSecurity);
csrf(httpSecurity);
requestCache(httpSecurity);
securityContextRepository(httpSecurity);
addFilter(httpSecurity, userDetailsService);
return httpSecurity.build();
}
@Bean
public RSocketServerCustomizer springSecurityRSocketSecurity(SecuritySocketAcceptorInterceptor interceptor) {
return (server) -> server.interceptors((registry) -> registry.forSocketAcceptor(interceptor));
}
}