Spring WebFlux Security 如何在会话中更新认证信息无需重新登录
问题原因
你之前使用ReactiveSecurityContextHolder.withAuthentication(authentication)的方式仅将认证信息写入了当前请求的订阅上下文,只对单次请求生效,不会持久化到WebSession中,所以后续请求获取到的还是旧的用户信息。
正确实现方案
你配置的安全上下文是存储在WebSession中,只需要调用你自定义注册的ServerSecurityContextRepository的save方法,将更新后的安全上下文写入当前会话即可,完整实现步骤如下:
- 首先注入你在配置类中声明的
ServerSecurityContextRepository实例
@Autowired private ServerSecurityContextRepository securityContextRepository;
- 在更新用户信息的业务逻辑中,构造更新后的认证信息并写入会话,代码示例如下(以Controller接口为例):
@PutMapping("/api/v1/user/update-info") public Mono<Void> updateUserInfo(@RequestBody UserUpdateDTO updateDTO, ServerWebExchange exchange) { return ReactiveSecurityContextHolder.getContext() .map(SecurityContext::getAuthentication) .flatMap(oldAuth -> { // 构造更新后的用户信息,替换为你的实际业务逻辑 MyAppUserDetails updatedUser = new MyAppUserDetails( updateDTO.getFirstName(), updateDTO.getLastName(), updateDTO.getEmail(), // 保留原有用户ID、组织ID等不可修改的属性,或者按需求更新 ((MyAppUserDetails) oldAuth.getPrincipal()).getUserId(), ((MyAppUserDetails) oldAuth.getPrincipal()).getOrgId() ); // 构造新的认证凭证,注意必须传入用户权限集合,否则会被判定为未认证 // 凭证可以复用原有凭证,不需要修改的话直接传入oldAuth.getCredentials()即可 UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken( updatedUser, oldAuth.getCredentials(), // 如果更新了用户权限,这里传入新的权限集合即可 oldAuth.getAuthorities() ); // 构造新的安全上下文 SecurityContext newSecurityContext = new SecurityContextImpl(newAuth); // 将新的安全上下文保存到当前会话中 return securityContextRepository.save(exchange, newSecurityContext); }); }
注意事项
securityContextRepository.save返回的是Mono<Void>,必须将其加入响应式流的订阅链,不能单独调用否则该操作不会执行,更新不会生效- 构造新的
UsernamePasswordAuthenticationToken时必须传入第三个参数权限集合,否则Spring Security会将其判定为未认证的凭证,后续请求会被拦截要求重新登录 - 你自定义了Session中安全上下文的属性名为
my-security-context,直接使用注入的ServerSecurityContextRepository实例调用save方法会自动匹配你配置的属性名,无需额外处理
内容的提问来源于stack exchange,提问作者user1578872
相关产品推荐
相关产品推荐

