我正在使用Spring Webflux,并且在实现ReactiveAuthenticationManager之后,下一个任务是将会话保留在Redis中。
我不是从UUID随机创建会话ID(redis键),而是要从请求标头中选择用户名标头,并将其设置为要保留在redis中的sessionId。
原因:我的客户已经通过第三方服务进行了身份验证,我只想从redis中检查其授予的权限(而不是每次都从数据库中检查)。请求中没有包含sessionId的cookie,而且我希望将会话映射到userName标头,而不是spring生成的会话ID(随机UUID)。
我尝试过的事情:
我尝试通过在 Webfilter 和 ServerSecurityContextRepository 加载中获取 ServerWebExchange 来更改 Websession 方法,但是这里的 ServerWebExchange 不允许我将sessionId更改为固定值(或某些生成逻辑)
代码段:
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {
private final DaoAuthenticationManager authenticationManager;
@Autowired
public SecurityContextRepository(DaoAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
ServerHttpRequest request = swe.getRequest();
if (request.getHeaders().containsKey("userName") &&
!Objects.requireNonNull(request.getHeaders().get("userName")).isEmpty()) {
String userName = Objects.requireNonNull(swe
.getRequest()
.getHeaders()
.get("userName")).get(0);
Authentication auth = new UsernamePasswordAuthenticationToken(userName,
Security.PASSWORD);
return this.authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
} else {
return Mono.empty();
}
}
}
@Component
public class DaoAuthenticationManager implements ReactiveAuthenticationManager {
private final DaoUserDetailsService userDetailsService;
private final Scheduler scheduler;
@Autowired
public DaoAuthenticationManager(DaoUserDetailsService userDetailsService,
Scheduler scheduler) {
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
this.userDetailsService = userDetailsService;
this.scheduler = scheduler;
}
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
final String username = authentication.getName();
return this.userDetailsService.findByUsername(username)
.publishOn(this.scheduler)
.switchIfEmpty(
Mono.defer(() -> Mono.error(new
UsernameNotFoundException("Invalid Username"))))
.map(u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(),
u.getAuthorities()));
}
}
@Configuration
@EnableRedisWebSession(redisNamespace = Constants.DEFAULT_SESSION_NAMESPACE, maxInactiveIntervalInSeconds = Constants.SESSION_LIFECYCLE)
public class RedisConfig {
}
UserDetailsService基于UserRepository,后者从关系表中获取用户的角色。
也欢迎其他最佳做法或解决方法。