How to access OAuth2AuthorizedClient in Webflux Functional Endpoints? - java

With the classic MVC-style RestController it's easy to get the OAuth2AuthorizedClient, all I need to do is this:
#GetMapping("/foo")
public Foo getFoo(#RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient client){
return new Foo(client.getPrincipalName());
}
With Webflux's functional endpoints, however, how can I access the OAuth2AuthorizedClient in a similar fashion?
EDIT:
To make it more clear, I'm aware that the reactive approach works the same as the non-reactive one. What I'm curious about is how I can access the OAuth2AuthorizedClient from Webflux's functional endpoints:
PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> route = route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
public class PersonHandler {
// ...
public Mono<ServerResponse> listPeople(ServerRequest request) {
// ...
}
public Mono<ServerResponse> createPerson(ServerRequest request) {
// ...
}
public Mono<ServerResponse> getPerson(ServerRequest request) {
// ...
}
}
For example, in the createPerson method, how do I access OAuth2AuthorizedClient?

Found a solution!
#Component
#RequiredArgsConstructor
public class OAuth2Utils {
private final ServerOAuth2AuthorizedClientRepository authorizedClientRepository;
public Mono<OAuth2AuthorizedClient> extractOAuth2AuthorizedClient(ServerRequest request) {
return request.principal()
.filter(principal -> principal instanceof OAuth2AuthenticationToken)
.cast(OAuth2AuthenticationToken.class)
.flatMap(auth -> authorizedClientRepository.loadAuthorizedClient(auth.getAuthorizedClientRegistrationId(), auth, request.exchange()));
}
}

Looks like it works the same as in non reactive approach.
Please take a look on:
docs:
https://docs.spring.io/spring-security/site/docs/5.1.0.RELEASE/reference/html/webflux-roac.html
example: https://github.com/spring-projects/spring-security/tree/5.1.0.RELEASE/samples/boot/oauth2webclient-webflux

Related

Is there a way to get request URI in Spring?

Whenever a request is made, I need to get the request URI for some internal calculations.
For some time I've been doing it like this:
public Mono<Response> example(ServerHttpRequest req) { ... }
And then using req.getURI(), but that becomes a pain once you need to pass it down multiple times. I need the URI object to extract scheme, schemeSpecificPart, host, port from it.
Is there a way to get these properties without extracting them from a request?
UPD: I see that for Web MVC there are convenient methods to retrieve request URI. But I need the same for reactive stack (netty).
It can be achieved by creating WebFilter that puts ServerHttpRequest into the Context:
#Component
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveRequestContextFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
return chain
.filter(exchange)
.contextWrite(ctx -> ctx.put(ReactiveRequestContextHolder.CONTEXT_KEY, request));
}
}
Additionally, create a class that provides static access to request data:
public class ReactiveRequestContextHolder {
public static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;
public static Mono<ServerHttpRequest> getRequest() {
return Mono.deferContextual(Mono::just).map(ctx -> ctx.get(CONTEXT_KEY));
}
public static Mono<URI> getURI() {
return getRequest().map(HttpRequest::getURI);
}
}
Methods can be accessed through the class name directly without having to instantiate them. Just be aware that it should not be accessed before the filter is executed.
Example of usage:
#RestController
#RequestMapping
public class TestController {
#GetMapping("/test")
public Mono<URI> test() {
return ReactiveRequestContextHolder.getURI();
}
}
Reference
You can try this :
public Mono<Response> example(WebRequest request){
System.out.println(request.getDescription(false));
.......
}
You can turn this false to true in getDescription as false will only give you the Uri which i think is the only thing you need.
You can inject it in any bean.
#Autowired
private HttpServletRequest request;

How to test Controller that returns Mono<ResponseEntity<Void>>?

I'm pretty new to webflux and I am struggling to understand how to test this Controller function.
public Mono<ResponseEntity<Void>> functionName(final Request request) {
RequestDto Dto = RequestMapper.
INSTANCE.toDto(request);
service.functionName(Dto);
return Mono.just(new ResponseEntity<Void>(HttpStatus.OK));
}
You could use WebTestClient that provides fluent API for verifying responses. Here is a short example
#WebFluxTest
class ControllerTest {
#Autowired
private WebTestClient client;
#BeforeEach
void setUp(ApplicationContext context) {
client = WebTestClient.bindToApplicationContext(context).build();
}
#Test
void test() {
client.get()
.uri("/test")
.exchange()
.expectStatus().isOk()
.expectBody().isEmpty();
}
}
In addition, pay attention to endpoint implementation. In reactive you need to build the flow and Webflux will subscribe to it for every request. In case, service.functionName is blocking (non-reactive), consider running it on a separate Scheduler using .subscribeOn(Schedulers.boundedElastic()). For details, check How Do I Wrap a Synchronous, Blocking Call?.
If the Callable resolves to null, the resulting Mono completes empty and we could return 404 applying switchIfEmpty operator.
#GetMapping(path = "/test")
public Mono<ResponseEntity<Void>> functionName(Request request) {
return Mono.fromCallable(() -> {
RequestDto Dto = RequestMapper.INSTANCE.toDto(request);
return service.functionName(Dto);
}).subscribeOn(Schedulers.boundedElastic())
.map(res -> new ResponseEntity<>(HttpStatus.OK))
.switchIfEmpty(Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)));
}

Quarkus Annotation-Based Interceptor with Non-Optional Arguments

This seems to be a hot topic based on the amount of questions asked but I have not found the answer I am looking for just yet. I want to implement a simple authorization service in my Quarkus app, but I seem to be repeating code over and over again.
Basically, I take in the JWT from the Authorization Http header and check if the role supplied in it is sufficient to access my endpoint:
public void someApiCall(#Context HttpHeaders headers) {
authService.validate(ApiToken.SOME_API_CALL, headers); // Throws an exception when unauthorized
//…
}
Now, I think this looks really clunky and I do not like the additional parameter that I need for every single Http endpoint. I have done some research into AOP and know how to add an interceptor which could validate the Http headers through an annotation which would be applied to my method:
#Authorize
public void someApiCall(/*…*/) { /*…*/ }
The issue is, I do not know how to pass in arguments into this annotation to specify the required role. I want something like this:
#Authorize(UserRole.SYSADMIN)
This seems pretty simple but I cannot figure it out. Below you will find the interceptor and annotation classes (Missing the required role of course):
Authorize.java
#Retention(value=RUNTIME)
#Target(value=METHOD)
public #interface Authorize {}
AuthorizeInterceptor.java
#Interceptor
#Priority(3000)
#Authorize
public class AuthorizeInterceptor {
#Inject
AuthorizationService authService;
#AroundInvoke
public void validateRole(InvokationContext ctx) {
authService.validate(ApiToken.ALL, ((RestEndpoint)ctx.getTarget()).getHttpHeaders());
}
}
RestEndpoint.java
public class RestEndpoint {
#Context
HttpHeaders headers;
public HttpHeaders getHttpHeaders() { return headers; }
}
SomeResource.java
public class SomeResource extends RestEndpoint {
#GET
#Authorize
public Object someApiCall() {
/* do code directly */
}
}
So, in conclusion, where I write #Authorize, I want to have #Authorize(UserRole.SOME_ROLE).
Thanks in advance!
So, I managed to figure it out. It turns out that it isn't that hard, I just didn't know where to look.
Here are the modified classes:
Authorize.java
#InterceptorBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Authorize {
// Nonbinding is very important. It makes the interceptor fire regardless of the value
#Nonbinding ApiToken value();
}
AuthorizeInterceptor.java
#Interceptor
#Priority(3000)
#Authorize(ApiToken.NULL)
public class AuthorizeInterceptor {
/* fields */
public Object validate(InvokationContext ctx) throws Exception {
authService.validate(/* stays the same */);
return ctx.proceed();
}
}
SomeResource.java
public class SomeResource {
#GET
#Authorize(ApiToken.SOME_API_CALL)
public Object someApiCall() { /* implementation */ }
}
As Turing85 pointed out, a similar API already exists in JavaEE which implements the authorization functionality in the same way.

Map containing Retrofit API interface method from ServiceHelper

I am trying to follow the REST client implementation pattern described in the Google I/O Dobjanschi video here and am using Retrofit2 for the REST API calls.
Based on the REST client pattern described above I introduced a ServiceHelper layer that calls the actual API method via Retrofit. However I don't have a clean way to call the interface methods from the ServiceHelper layer.
I currently have an enum of the available API calls and pass that from the ServiceHelper. And in my ApiProcessor introduced a function that uses an giant if..else if ladder that returns the appropriate Retrofit API interface call based on the enum passed in. I haven't really found a better/cleaner approach to this.
Is there a better / cleaner way to map these? Or any other ideas to do this?
You should throw away that monolithic ServiceHelper and create several repositories following the repository pattern in order to encapsulate and distribute responsibilities between classes.
Actually, the Retrofit API itself favors composition over inheritance, so you can easily create as much interfaces as needed and use them in the right repository.
Without the code it is a bit hard to "inspect" your solution. :)
As You asked the question it is not really the best way to solve the problem in this way (in my opinion). Altough there are a ton approaches like "if it works it is OK".
In my opinion a bit cleaner solution would be the following: Your helper is a good thing. It should be used to hide all the details of the API You are using.
It is a good thing to hide those API specific stuff because if it changes You only forced to change only your helper/adapter. My recommendation is to use multiple method in apiprocessor and not enums. It is a bit easier to maintain and fix if something is changing. Plus you do not have to take care of your Enum.
TLDR: If it works probably it is OK. You do not have to write million dollar production code to test something, but if you would like to get a good habit You should consider the refactor that code into separate processor methods.
You Can follow service pattern:
a) Create resource interface which are your exposed rest methods eg:
public interface AppResource {
#Headers({"Accept: application/json", "Content-Type: application/json"})
#GET(ApiConstants.API_VERSION_V1 + "/users")
Call<List<User>> getUsers();
}
b) Create RetrofitFactory
public class RetrofitFactory {
private static Retrofit userRetrofit;
#NonNull
private static Retrofit initRetrofit(String serverUrl) {
final HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// set your desired log level
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
final Request original = chain.request();
final Request request = original.newBuilder()
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
})
.addInterceptor(logging)
.build();
return new Retrofit.Builder()
.baseUrl(serverUrl)
.addConverterFactory(JacksonConverterFactory.create())
.client(okHttpClient)
.build();
}
public static Retrofit getUserRetrofit() {
if (userRetrofit == null) {
final String serverUrl = context.getString(R.string.server_url); //Get context
userRetrofit = initRetrofit(serverUrl);
}
return userRetrofit;
}
}
c) Create a abstract BaseService which your every service will extend
public abstract class BaseService<Resource> {
protected final Resource resource;
final Retrofit retrofit;
public BaseService(Class<Resource> clazz) {
this(clazz, false);
}
public BaseService(Class<Resource> clazz) {
retrofit = RetrofitFactory.getUserRetrofit();
resource = retrofit.create(clazz);
}
protected <T> void handleResponse(Call<T> call, final ResponseHandler<T> responseHandler) {
call.enqueue(new Callback<T>() {
#Override
public void onResponse(final Call<T> call, final Response<T> response) {
if (response.isSuccess()) {
if (responseHandler != null) {
responseHandler.onResponse(response.body());
}
} else {
final ErrorResponse errorResponse = parseError(response);
if (responseHandler != null) {
responseHandler.onError(errorResponse);
}
}
}
#Override
public void onFailure(final Call<T> call, final Throwable throwable) {
if (responseHandler != null) {
responseHandler.onFailure(throwable);
}
}
});
}
}
d) Now your user service with their response handler
public interface UserService {
void getUsers(ResponseHandler<List<User>> userListResponse);
}
e) Now your user service implementation class which extends baseservice
public class UserServiceImpl extends BaseService<UserResource> implements UserService {
public UserServiceImpl () {
super(AppResource.class);
}
#Override
public void getUsers(ResponseHandler<List<User>> userListResponse) throws UserServiceException {
final Call<List<User>> response = resource.getUsers();
handleResponse(response, userListResponse);
}
f) Create a service factory which you will reuse to call services eg:
public class ServiceFactory {
private static UserService userservice;
UserService getUserService(){
if (UserService == null) {
UserService = new UserServiceImpl();
}
return UserService ;
}
g) Now simply call service and pass your response handler
ServiceFactory.getUserService().getUsers(getUserListResponseHandler());
} catch (your exception handler) {
//log your excp
}

Spring MVC customized method parameter binding

I'm looking for a way to customize the default Spring MVC parameter binding. Take this method as an example:
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#RequestParam String param) {
...
This is easy, when I have just a Stringthat I want to extract from the request. However, I want to populate a more complete object, so that my method looks like this:
#RequestMapping(value="/index.html")
public ModelAndView doIndex(Foo bar) {
...
What I'm looking for is some way to declare a binding like this;
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#FooPopulator Foo bar) {
...
And have some other kind of implementor (determined by the #FooPopulator annotation) that does this:
public void doBind(Foo target, ServletRequest originalRequest) {
target.setX(this.computeStuffBasedOn(originalRequest));
target.sety(y);
}
So far I've found out about the #InitBinderbinder annotaion but I'm unsure whether that's really the right choice for this scenarion.
What's the best way?
It is very easy. You can use Converters (that work like one way PropertyEditors but are stateless).
See chapter 5.5 Spring 3 Type Conversion in Spring reference.
If such an converter is registered once, you do not need any additional information, you can simply use
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#RequestParam Foo param) {
For example a simple converter that load an object by its id:
#Component
#CustomConverter //custom qualifyer
public class BUdToUserConverter implements Converter<String, User> {
#Resource
private UserDao userDao;
#Override
public User convert(String source) {
Integer id = Integer.parse(source);
return this.userDao.getByBusinessId(id);
}
}
A "helper" that registers all Beans with #CustomConverter anntoation
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {
#Resource
#CustomConverter
private List<Converter<?, ?>> customConverter;
#Override
protected void installFormatters(final FormatterRegistry registry) {
super.installFormatters(registry);
for (Converter<?, ?> converter : customConverter) {
registry.addConverter(converter);
}
}
}
How to use it
UserController {
...
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView show(#PathVariable("id") User user) {
return new ModelAndView("users/show", "user", user);
}
}
just a quick thank you and the info, that I've found the "correct" solution to the problem. Spring already provides the WebArgumentResolver for this scenario.
http://sergialmar.wordpress.com/2011/03/29/extending-handler-method-argument-resolution-in-spring-mvc/
http://scottfrederick.blogspot.com/2011/03/customizing-spring-3-mvcannotation.html

Categories

Resources