Я настроил Spring Webflux с JWT. Создал кастом ServerSecurityContextRepository
и добавил в SecurityWebFilterChain
. Но когда я получаю SecurityContext во время входящих запросов, контекст безопасности всегда возвращает null
.
Следующие коды являются моими конфигурациями:
SecurityContextRepository.class
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityContextRepository.class);
private static final Pattern PATTERN = Pattern.compile("Bearer (.+)");
private final JWTVerifier verifier;
private final UserRepository userRepository;
@SuppressFBWarnings("EXS_EXCEPTION_SOFTENING_NO_CONSTRAINTS")
public SecurityContextRepository(
SignatureAlgorithmProvider algorithmProvider, UserRepository userRepository) {
this.userRepository = requireNonNull(userRepository);
try {
verifier = JWT.require(algorithmProvider.algorithm()).withIssuer(JwtTokenGenerator.ISSUER).build();
} catch (Exception e) {
throw new BeanInitializationException("Could not initialize JWT verifier", e);
}
}
@Override
public Mono<Void> save(ServerWebExchange serverWebExchange, SecurityContext securityContext) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Mono<SecurityContext> load(ServerWebExchange serverWebExchange) {
var token = extractToken(serverWebExchange.getRequest());
if (token == null) {
return Mono.empty();
} else {
try {
var jwt = verifier.verify(token);
var userId = UUID.fromString(jwt.getSubject());
var userOptional = userRepository.fetchOneById(userId);
if (userOptional.isPresent()) {
var user = userOptional.get();
return Mono.just(new SecurityContextImpl(new JwtAuthenticationToken(securityUser, roles(user.roles()))));
}
return Mono.empty();
} catch (JWTDecodeException | IllegalArgumentException e) {
LOGGER.warn("Bearer token cannot be decoded as JWT: {}", token, e);
return Mono.error(new UnauthorizedException("Bearer token cannot be decoded as JWT.", e));
} catch (SignatureVerificationException e) {
LOGGER.warn("Received access token with an invalid signature: {}", token, e);
return Mono.error(
new UnauthorizedException("Received access token with an invalid signature.", e));
} catch (TokenExpiredException e) {
LOGGER.warn("JWT token expired.", e);
return Mono.error(new UnauthorizedException("JWT token expired.", e));
} catch (Exception e) {
LOGGER.warn("Exception verifying JWT: {}", token, e);
return Mono.error(new UnauthorizedException("Exception verifying JWT.", e));
}
}
}
}
SecurityConfiguration.class
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfiguration {
private final SecurityContextRepository securityContextRepository;
@Autowired
public SecurityConfiguration(SecurityContextRepository securityContextRepository) {
this.securityContextRepository = requireNonNull(securityContextRepository);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@SuppressFBWarnings(value = "SPRING_CSRF_PROTECTION_DISABLED")
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
// Disable default security.
return http.httpBasic()
.disable()
.formLogin()
.disable()
.csrf()
.disable()
.logout()
.disable()
.cors()
.and()
// Add custom security.
.securityContextRepository(securityContextRepository)
.addFilterAt(
new SecurityContextServerWebExchangeWebFilter(),
SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)
.authorizeExchange()
.pathMatchers("/api/authentication/v1/*", "/status", "/info", "/health", "/docs/**")
.permitAll()
.pathMatchers(HttpMethod.GET, "/api/locations/v1/**")
.permitAll()
.anyExchange()
.authenticated()
.and()
.exceptionHandling()
.and()
.build();
}
}
Версия Jwt: com.auth0:java-jwt:3.10.3
Версия SpringBoot: 2.3.2:RELEASE