How to avoid Spring WebFlux filter from being called twice? - spring

After a long exhaustive search, asking for help on this. I'm writing a JwtTokenFilter in Spring WebFlux coupled with Spring Security. The problem is this filter is being called twice on a single request. The filter code is below.
#Component
class JwtTokenAuthenticationFilter(
#Autowired val tokenProvider: JwtTokenProvider
) : WebFilter {
private val HEADER_PREFIX = "Bearer "
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void?> {
val token = resolveToken(exchange.request)
return if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
val authentication: Authentication = tokenProvider.getAuthentication(token)
chain.filter(exchange)
.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))
} else
chain.filter(exchange)
}
private fun resolveToken(request: ServerHttpRequest): String? {
val bearerToken = request.headers.getFirst(HttpHeaders.AUTHORIZATION)
return if (StringUtils.hasText(bearerToken) && bearerToken!!.startsWith(HEADER_PREFIX)) {
bearerToken.substring(7)
} else null
}
}
This problem can be avoided by removing #Component annotation on the filter but that would mean I'll have to create this object and all its dependencies in the path, which totally takes away the advantage of dependency inversion of Spring. Therefore, I want to retain using the Spring annotations to get the work done. However, this creates the problem that the filter is registered at two places i.e.., at servlet container and Spring Security.
This solution presented in this article talks about it.
But couldn't find a way to apply the solution on that article to WebFilter as the solution in the article is for servlet Filters.
So, is there a way to avoid this bean from being invoked by servlet container and allow it to be invoked by Spring Security only. Or something else is the issue in the code that you see?

Related

Spring WebFlux Security PreAuthorize Best Practice

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.
As far as I know, SpEL expressions evaluation is synchronous.
Unless your UserServiceRequestAuthorizer does more than checking access-token claims against static strings or request params and payload, I don't know why this would be an issue: it should be very, very fast.
Of course, if you want to check it against data from DB or a web-service this would be an other story, but I'd say that your design is broken and that this data access should be made once when issuing access-token (and set private claims) rather than once per security evaluation (which can happen several times in a single request).
Side notes
It is notable here that I am using Spring OAuth2 Authorization Server, which is why the parameter authentication is of type JwtAuthenticationToken.
I do not agree with that. It would be the same with any authorization-server (Keycloak, Auth0, Microsoft IdentityServer, ...). You have a JwtAuthenticationToken because you configured a resource-server with a JWT decoder and kept the default JwtAuthenticationConverter. You could configure any AbstractAuthenticationToken instead, as I do in this tutorial.
It can get complicated and even if that complicacy might be managed by SpEL, I would rather not for the sake of simplicity.
I join #M.Deinum point of view, writing your security rules in a service, like you do, makes it far less readable than inlining expressions: hard to guess what is checked while reading the expression => one has to quit current source file, open security service one and read the code.
If you refer to the tutorial already linked above, it is possible to enhance security DSL and write stuff like: #PreAuthorize("is(#username) or isNice() or onBehalfOf(#username).can('greet')") to stick to your sample, this would give #PreAuthorize("is(#userId) or isAdmin()).

Spring GraphQL with WebMvc getting request headers

I have a Spring GraphQL project. Each data fetcher (#SchemaMapping) will get data from a remote API protected by authentication.
I need to propagate the authorization header from the original request (that I can see inside the #QueryMapping method) to the data fetcher.
In the data fetcher I can use RequestContextHolder to get the request and the headers like this:
val request = (RequestContextHolder.getRequestAttributes() as ServletRequestAttributes?)?.getRequest()
val token = request?.getHeader("authorization")
This works but I am worried it could break.
Spring GraphQL documentation states that:
A DataFetcher and other components invoked by GraphQL Java may not always execute on the same thread as the Spring MVC handler, for example if an asynchronous WebInterceptor or DataFetcher switches to a different thread.
I tried adding a ThreadLocalAccessor component but it seems to me from debugging and reading source code that the restoreValue method gets called only in a WebFlux project.
How can I be sure to get the right RequestContextHolder in a WebMvc project?
UPDATE
I will add some code to better explain my use case.
CurrentActivity is the parent entity while Booking is the child entity.
I need to fetch the entities from a backend with APIs protected by authentication. I receive the auth token in the original request (the one with the graphql query).
CurrentActivityController.kt
#Controller
class CurrentActivityController #Autowired constructor(
val retrofitApiService: RetrofitApiService,
val request: HttpServletRequest
) {
#QueryMapping
fun currentActivity(graphQLContext: GraphQLContext): CurrentActivity {
// Get auth token from request.
// Can I use the injected request here?
// Or do I need to use Filter + ThreadLocalAccessor to get the token?
val token = request.getHeader("authorization")
// Can I save the token to GraphQL Context?
graphQLContext.put("AUTH_TOKEN", token)
return runBlocking {
// Authenticated API call to backend to get the CurrentActivity
return#runBlocking entityretrofitApiService.apiHandler.activitiesCurrent(mapOf("authorization" to token))
}
}
}
BookingController.kt
#Controller
class BookingController #Autowired constructor(val retrofitApiService: RetrofitApiService) {
#SchemaMapping
fun booking(
currentActivity: CurrentActivity,
graphQLContext: GraphQLContext,
): Booking? {
// Can I retrieve the token from GraphQL context?
val token: String = graphQLContext.get("AUTH_TOKEN")
return runBlocking {
// Authenticated API call to backend to get Booking entity
return#runBlocking currentActivity.currentCarBookingId?.let { currentCarBookingId ->
retrofitApiService.apiHandler.booking(
headerMap = mapOf("authorization" to token),
bookingId = currentCarBookingId
)
}
}
}
}
The ThreadLocalAccessor concept is really meant as a way to store/restore context values in an environment where execution can happen asynchronously, on a different thread if no other infrastructure already supports that.
In the case of Spring WebFlux, the Reactor context is already present and fills this role. A WebFlux application should use reactive DataFetchers and the Reactor Context natively.
ThreadLocalAccessor implementations are mostly useful for Spring MVC apps. Any ThreadLocalAccessor bean will be auto-configured by the starter.
In your case, you could follow one of the samples and have a similar arrangement:
Declare a Servlet filter that extracts the header value and set it as a request attribute with a well-known name
Create a ThreadLocalAccessor component and use it to store request attributes into the context
Fetch the relevant attribute from your DataFetcher
I tried adding a ThreadLocalAccessor component but it seems to me from
debugging and reading source code that the restoreValue method gets
called only in a WebFlux project.
Note that the restoreValue is only called if the current Thread is not the one values where extracted from originally (nothing needs to be done, values are already in the ThreadLocal).
I've successfully tested this approach, getting the "authorization" HTTP header value from the RequestContextHolder. It seems you tried this approach unsuccessfully - could you try with 1.0.0-M3 and let us know if it doesn't work? You can create an issue on the project with a link to a sample project that reproduces the issue.
Alternate solution
If you don't want to deal with ThreadLocal-bound values, you can always use a WebInterceptor to augment the GraphQLContext with custom values.
Here's an example:
#Component
public class AuthorizationWebInterceptor implements WebInterceptor {
#Override
public Mono<WebOutput> intercept(WebInput webInput, WebInterceptorChain chain) {
String authorization = webInput.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
webInput.configureExecutionInput((input, inputBuilder) ->
inputBuilder
.graphQLContext(contextBuilder -> contextBuilder.put("Authorization", authorization))
.build()
);
return chain.next(webInput);
}
}
With that, you can fetch that value from the GraphQL context:
#QueryMapping
public String greeting(GraphQLContext context) {
String authorization = context.getOrDefault("Authorization", "default");
return "Hello, " + authorization;
}

#RestController over interface + spring

I accidentally found this technique of using RestController annotation.
(I saw this not on the internet, but in one of the examples that I was shown. I couldn't find it on the internet)
#RestController
#RequestMapping("api/user/point")
public interface ExampleRestController {
#PostMapping("{key}" )
#Operation(summary = "Manage...",
description = "Allow user ...")
public HttpStatus changePoints(
#PathVariable
#NotBlank
#Parameter(description = "Id user") String key,
#RequestParam("point")
#Min(0) #Parameter(description = "count something..", required = true) Long point,
#RequestParam("type")
#Parameter(description = "Type of...", required = true) TypeOperation type
);
}
What is this technique, who knows ?
I've seen the Rest Api just like this. Can Spring also dynamically create an endpoint method in the RestController, as it does when executing ...extend CrudRepository ? And how does AOP work in this case ? After all, in order for the bean to be configured, we need to put the annotation above the class (like how it is done at the service level), if you put it above the interface, the full automatic configuration of the bean is not guaranteed
It is spring-interface-driven-controllers.
This is new feature of Spring MVC. ( starting with Spring 5.1)

Reactive Spring Security PostAuthorize annotation doesn't work

Using Webflux and Reactive Spring Security, how do you do post processing via annotations to control access to methods?
Trying a very basic sample, I'm not able to get the value from the PostAuthorize annotation. For example
#GetMapping
#PostAuthorize("#email == authentication.principal.email")
public Flux<Project> sampleTest(final String email) {
log.info("email: {}", email);
return Flux.empty();
}
The email will always be null. I have the basic wiring working to the fact if I set something like #PreAuthorize("hasRole('ADMIN')") I'll get back a 403.
I can extract the Authentication out with a helper like:
public Mono<Authentication> getAuthentication() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.flatMap(Mono::just);
}
I may not be understanding your question correctly, but the PostAuthorize uses the return object - the body of the method doesn't have access to anything in the SPEL expression.
Something like this might work -
#GetMapping
#PostAuthorize("returnObject == someCondition")
public Flux<Project> sampleTest(final String email) {
// get some data and return it
}
But maybe you want to filter the items in the Flux?
You might look at the #PostFilter annotation -
// assuming there's an email property on your Project object.
#GetMapping
#PostFilter("filterObject.getEmail() == authentication.principal.email")
public Flux<Project> sampleTest() {
// get some data and return it
}

How to set header variables in GraphQL-SPQR

I'm running a GraphQL API using GraphQL-SPQR and Spring Boot.
At the moment, I am throwing RuntimeExceptions to return GraphQL errors. I have a customExceptionHandler that implements DataFetcherExceptionHandler that returns errors in the correct format, as shown below:
class CustomExceptionHandler : DataFetcherExceptionHandler {
override fun onException(handlerParameters: DataFetcherExceptionHandlerParameters?): DataFetcherExceptionHandlerResult {
// get exception
var exception = handlerParameters?.exception
val locations = listOf(handlerParameters?.sourceLocation)
val path = listOf(handlerParameters?.path?.segmentName)
// create a GraphQLError from your exception
if (exception !is GraphQLError) {
exception = CustomGraphQLError(exception?.localizedMessage, locations, path)
}
// cast to GraphQLError
exception as CustomGraphQLError
exception.locations = locations
exception.path = path
val errors = listOf<GraphQLError>(exception)
return DataFetcherExceptionHandlerResult.Builder().errors(errors).build()
}
}
I use the CustomExceptionHandler as follows (in my main application class):
#Bean
fun graphQL(schema: GraphQLSchema): GraphQL {
return GraphQL.newGraphQL(schema)
.queryExecutionStrategy(AsyncExecutionStrategy(CustomExceptionHandler()))
.mutationExecutionStrategy(AsyncSerialExecutionStrategy(CustomExceptionHandler()))
.build()
}
I'd like to set a header variable for a UUID that corresponds to the exception, for logging purposes. How would I do that?
Even better, is it possible to create a Spring Bean that puts the UUID in the header for all queries and mutations?
Thanks!
when you're using spring boot, there's two options:
you're using the spring boot graphql spqr starter (which brings it's own controller to handle all graphQL requests)
you're using plain graphql-spqr and have your own controller to handle GraphQL requests
In any case, you've got a few options:
Making your CustomExceptionHandler a Spring Bean and Autowiring HttpServletResponse
That would probably be the easiest way to go - and it would probably work in any case: You could simply make your CustomExceptionHandler a Spring bean and have it autowire the HttpServletRequest - in the handler method, you could then set it to whatever you would like it to be. Here's some dummy code in Java (sorry, I am not proficient enough in Kotlin):
#Component
class CustomExceptionHandler implements DataFetcherExceptionHandler {
private final HttpServletResponse response;
public CustomExceptionHandler(HttpServletResponse response) {
this.response = response;
}
#Override
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
response.setHeader("X-Request-ID", UUID.randomUUID().toString());
// ... your actual error handling code
}
}
This is going to work because spring will realise that HttpServletRequest differs for each request. It will therefore inject a dynamic proxy into your error handler that will point to the actual HttpServletResponse instance for every request.
I would argue, that it's not the most elegant way, but it will certainly solve your problem.
for the graphql-spqr spring boot starter
There's a default controller implementation that is used in projects using this starter. That controller will handle every graphql request that you receive. You can customise it, by implementing your own GraphQLExecutor and making it a spring bean. That executor is responsible to call the GraphQL engine, pass the parameters in and output the response. Here's the default implementation, that you might want to base your work on.
Similarly to the previous solution, you could autowire the HttpServletResponse in that class and set a HTTP Response header.
That solution would allow you to decide, if you want to set a request id in all cases, or just in specific error cases. (graphql.execute returns an object from which you can get the information if and what errors existed)
when using graphql-spqr without the spring boot starter
Locate your GraphQL controller, add an argument to that method of type HttpServletRequest - and then add headers to that as you prefer (see previous section on some more specific suggestions)

Resources