Spring WebFlux 安全 - 在会话中更新身份验证详细信息
SpringWebFlux security - update auth details in session
我正在使用 Spring 具有 webflux 安全性的 boot 2.5.6。
@EnableWebFluxSecurity
public class AdminSecurityConfig {
@Bean
public SecurityWebFilterChain securitygWebFilterChain(final ServerHttpSecurity http,
final ReactiveAuthenticationManager authManager,
final ServerSecurityContextRepository securityContextRepository,
final MyAuthenticationFailureHandler failureHandler) {
http.securityContextRepository(securityContextRepository);
return http.authorizeExchange().matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.pathMatchers(props.getSecurity().getIgnorePatterns()).permitAll()
.pathMatchers("/api/v1/service/test").hasAuthority("DEFAULT")
.anyExchange().authenticated()
.and()
.formLogin()
.loginPage("/login")
.authenticationSuccessHandler(authSuccessHandler())
.and()
.exceptionHandling()
.authenticationEntryPoint((exchange, exception) -> Mono.error(exception))
.accessDeniedHandler((exchange, exception) -> Mono.error(exception))
.and()
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public ReactiveAuthenticationManager authenticationManager() {
final UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(
userDetailsService);
authenticationManager.setPasswordEncoder(passwordEncoder());
return authenticationManager;
}
@Bean
public ServerSecurityContextRepository securityContextRepository() {
final WebSessionServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();
securityContextRepository.setSpringSecurityContextAttrName("my-security-context");
return securityContextRepository;
}
}
Mono<Principal> principal = ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication).cast(Principal.class);
最终 MyAppUserDetails 用户 = (MyAppUserDetails) ((UsernamePasswordAuthenticationToken) 主体)
.getPrincipal();
在这里我可以检索登录用户的详细信息。 MyAppUserDetails 将包含用户详细信息,如名字、姓氏、电子邮件、用户 ID、组织 ID,......
现在,我想在用户登录后更新会话中的用户详细信息,比如更改用户名而不要求用户注销和登录。
我尝试了下面的代码,但不确定如何设置凭据并将更新的用户设置到安全上下文中,以便下一次从安全上下文调用获取当前用户时 return 更新的用户。
final MyAppUserDetails appUser = new MyAppUserDetails("firstName", "lastName", "email", 1, 4);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(appUser, ....);
ReactiveSecurityContextHolder.withAuthentication(authentication);
- 从请求中获取会话。
- 从会话中获取上下文
- 创建新用户
- 在上下文中设置新用户
public Mono<ServerResponse> updateSession(ServerRequest request) {
return request.exchange().getSession().flatMap(session -> {
final SecurityContext context = session.getAttribute("app-security-context");
AppUserDetails newUser = AppUserDetails.of(...);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(newUser, pwd, authorities);
context.setAuthentication(token);
}
}
它正在运行,但不确定为什么我们需要使用以下方法保存上下文。
serverSecurityContextRepository.save(request.exchange(), 上下文);
没有上面的调用也能正常工作。
我正在使用 Spring 具有 webflux 安全性的 boot 2.5.6。
@EnableWebFluxSecurity
public class AdminSecurityConfig {
@Bean
public SecurityWebFilterChain securitygWebFilterChain(final ServerHttpSecurity http,
final ReactiveAuthenticationManager authManager,
final ServerSecurityContextRepository securityContextRepository,
final MyAuthenticationFailureHandler failureHandler) {
http.securityContextRepository(securityContextRepository);
return http.authorizeExchange().matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.pathMatchers(props.getSecurity().getIgnorePatterns()).permitAll()
.pathMatchers("/api/v1/service/test").hasAuthority("DEFAULT")
.anyExchange().authenticated()
.and()
.formLogin()
.loginPage("/login")
.authenticationSuccessHandler(authSuccessHandler())
.and()
.exceptionHandling()
.authenticationEntryPoint((exchange, exception) -> Mono.error(exception))
.accessDeniedHandler((exchange, exception) -> Mono.error(exception))
.and()
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public ReactiveAuthenticationManager authenticationManager() {
final UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(
userDetailsService);
authenticationManager.setPasswordEncoder(passwordEncoder());
return authenticationManager;
}
@Bean
public ServerSecurityContextRepository securityContextRepository() {
final WebSessionServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();
securityContextRepository.setSpringSecurityContextAttrName("my-security-context");
return securityContextRepository;
}
}
Mono<Principal> principal = ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication).cast(Principal.class);
最终 MyAppUserDetails 用户 = (MyAppUserDetails) ((UsernamePasswordAuthenticationToken) 主体) .getPrincipal();
在这里我可以检索登录用户的详细信息。 MyAppUserDetails 将包含用户详细信息,如名字、姓氏、电子邮件、用户 ID、组织 ID,...... 现在,我想在用户登录后更新会话中的用户详细信息,比如更改用户名而不要求用户注销和登录。
我尝试了下面的代码,但不确定如何设置凭据并将更新的用户设置到安全上下文中,以便下一次从安全上下文调用获取当前用户时 return 更新的用户。
final MyAppUserDetails appUser = new MyAppUserDetails("firstName", "lastName", "email", 1, 4);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(appUser, ....);
ReactiveSecurityContextHolder.withAuthentication(authentication);
- 从请求中获取会话。
- 从会话中获取上下文
- 创建新用户
- 在上下文中设置新用户
public Mono<ServerResponse> updateSession(ServerRequest request) { return request.exchange().getSession().flatMap(session -> { final SecurityContext context = session.getAttribute("app-security-context"); AppUserDetails newUser = AppUserDetails.of(...); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(newUser, pwd, authorities); context.setAuthentication(token); } }
它正在运行,但不确定为什么我们需要使用以下方法保存上下文。
serverSecurityContextRepository.save(request.exchange(), 上下文);
没有上面的调用也能正常工作。