我想实现简单的Spring Security WebFlux应用。
我想使用JSON消息,如
{
'username': 'admin',
'password': 'adminPassword'
}
in body(POST request to /signin)来登录我的应用。
我做了什么?
我创建了这个配置
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity(proxyTargetClass = true)
public class WebFluxSecurityConfig {
@Autowired
private ReactiveUserDetailsService userDetailsService;
@Autowired
private ObjectMapper mapper;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(11);
}
@Bean
public ServerSecurityContextRepository securityContextRepository() {
WebSessionServerSecurityContextRepository securityContextRepository =
new WebSessionServerSecurityContextRepository();
securityContextRepository.setSpringSecurityContextAttrName("securityContext");
return securityContextRepository;
}
@Bean
public ReactiveAuthenticationManager authenticationManager() {
UserDetailsRepositoryReactiveAuthenticationManager authenticationManager =
new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
authenticationManager.setPasswordEncoder(passwordEncoder());
return authenticationManager;
}
@Bean
public AuthenticationWebFilter authenticationWebFilter() {
AuthenticationWebFilter filter = new AuthenticationWebFilter(authenticationManager());
filter.setSecurityContextRepository(securityContextRepository());
filter.setAuthenticationConverter(jsonBodyAuthenticationConverter());
filter.setRequiresAuthenticationMatcher(
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/signin")
);
return filter;
}
@Bean
public Function<ServerWebExchange, Mono<Authentication>> jsonBodyAuthenticationConverter() {
return exchange -> {
return exchange.getRequest().getBody()
.cache()
.next()
.flatMap(body -> {
byte[] bodyBytes = new byte[body.capacity()];
body.read(bodyBytes);
String bodyString = new String(bodyBytes);
body.readPosition(0);
body.writePosition(0);
body.write(bodyBytes);
try {
UserController.SignInForm signInForm = mapper.readValue(bodyString, UserController.SignInForm.class);
return Mono.just(
new UsernamePasswordAuthenticationToken(
signInForm.getUsername(),
signInForm.getPassword()
)
);
} catch (IOException e) {
return Mono.error(new LangDopeException("Error while parsing credentials"));
}
});
};
}
@Bean
public SecurityWebFilterChain securityWebFiltersOrder(ServerHttpSecurity httpSecurity,
ReactiveAuthenticationManager authenticationManager) {
return httpSecurity
.csrf().disable()
.httpBasic().disable()
.logout().disable()
.formLogin().disable()
.securityContextRepository(securityContextRepository())
.authenticationManager(authenticationManager)
.authorizeExchange()
.anyExchange().permitAll()
.and()
.addFilterAt(authenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
.build();
}
}
但是我使用jsonBodyAuthenticationConverter(),它读取传入请求的Body。正文只能读取一次,因此出现错误
java.lang.IllegalStateException: Only one connection receive subscriber allowed.
实际上,它的工作,但有例外(会话设置在cookie)。我怎样才能重新制作而不出现这个错误呢?
现在我只创建了这样的东西:
@PostMapping("/signin")
public Mono<Void> signIn(@RequestBody SignInForm signInForm, ServerWebExchange webExchange) {
return Mono.just(signInForm)
.flatMap(form -> {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
form.getUsername(),
form.getPassword()
);
return authenticationManager
.authenticate(token)
.doOnError(err -> {
System.out.println(err.getMessage());
})
.flatMap(authentication -> {
SecurityContextImpl securityContext = new SecurityContextImpl(authentication);
return securityContextRepository
.save(webExchange, securityContext)
.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)));
});
});
}
从配置中删除AuthenticationWebFilter
。
3条答案
按热度按时间9vw9lbht1#
你就快到了。下面的转换器为我工作:
此外,您不需要登录控制器;
spring-security
将在过滤器中为您检查每个请求。以下是我如何使用ServerAuthenticationEntryPoint
配置spring-security:希望这对你有帮助。
esyap4oy2#
最后,我配置WebFlux安全性(注意注销处理,注销没有任何标准的5.0.4.RELEASE可用配置,你必须禁用默认注销配置,因为默认注销规范默认创建新的SecurityContextRepository,不允许你设置你的仓库)。
**UPDATE:**只有在SecurityContextRepository中为web session设置自定义SpringSecurityContextAttributeName时,默认注销配置才不起作用。
jchrr9hc3#
您可以将安全上下文保存在存储库中,并选择将其放入上下文中:
只有在正在进行的请求中使用SecurityContext时,才需要contextWrite行。在随后的请求中,AuthorizationFilter无论如何都会建立上下文。