as the title suggests, I have configured security in my Spring WebFlux application by using @EnableWebFluxSecurity and @EnableReactiveMethodSecurity.
I am using RouterFunction to handle the request routing. The following code is for the router:
@Component
public class UserServiceRequestRouter {
@Autowired
private UserServiceRequestHandler requestHandler;
@Bean
public RouterFunction<ServerResponse> route() {
//@formatter:off
return RouterFunctions
.route(GET("/user/{userId}"), requestHandler::getUserDetails);
//@formatter:on
}
}
And the request handler is:
@Component
public class UserServiceRequestHandler {
@Autowired
private UserService userService;
@PreAuthorize("@userServiceRequestAuthorizer.authorizeGetUserDetails(authentication, #request)")
public Mono<ServerResponse> getUserDetails(ServerRequest request) {
//@formatter:off
return userService.getUserDetails(request.pathVariable("userId"))
.convert()
.with(toMono())
.flatMap(
(UserDetails userDetails) -> ServerResponse.ok()
.contentType(APPLICATION_NDJSON)
.body(Mono.just(userDetails), UserDetails.class)
);
//@formatter:on
}
}
Note: The @Autowired UserService is to fetch data from the database in a reactive way.
Next, I have defined a @Component as:
@Component
@SuppressWarnings("unused")
@Qualifier("userServiceRequestAuthorizer")
public class UserServiceRequestAuthorizer {
public boolean authorizeGetUserDetails(JwtAuthenticationToken authentication, ServerRequest request) {
// @formatter:off
if (authentication == null) {
return false;
}
Collection<String> roles = authentication.getAuthorities()
.stream()
.map(Objects::toString)
.collect(Collectors.toSet());
if (roles.contains("Admin")) {
return true;
}
Jwt principal = (Jwt) authentication.getPrincipal();
String subject = principal.getSubject();
String userId = request.pathVariable("userId");
return Objects.equals(subject, userId);
// @formatter:on
}
}
It is notable here that I am using Spring OAuth2 Authorization Server, which is why the parameter authentication is of type JwtAuthenticationToken.
The application is working as per the expectation. But I am wondering if I am doing it the right way, meaning is this the best practice of doing method level Authorization in a reactive way?
The followings are my stack:
- JDK 17
- org.springframework.boot:3.0.0-M4
- org.springframework.security:6.0.0-M6
Any advice you could give would be much appreciated.
Update
As mentioned by M. Deinum in the comment why shouldn't I use hasAuthority("Admin") or principal.subject == #userId, the reason is that the authorization code I provided is merely for demonstration purposes. It can get complicated and even if that complicacy might be managed by SpEL, I would rather not for the sake of simplicity.
Also the question is not about using inline SpEL, it's more about its reactiveness. I don't know if the SpEL mentioned in the @PreAuthorize is reactive! If it is reactive by nature then I can assume any expression mentioned in the @PreAuthorize would be evaluated reactively.