I saw a lot of example of application who use spring reactive. In all of the example, application is never full reactive.
Like this one
https://github.com/venugopr/Misc/tree/master/Spring/Spring-Session
Ui use spring-web-flux, when user need to authenciate, it call gateway.
Login controller look like
#Autowired
AuthenticationManager authenticationManager;
#PostMapping("/authenticate")
public ResponseEntity<LoginResponse> authenticateUser(#Valid #RequestBody LoginRequest loginRequest) {
....
}
AuthenticationManager is not wrote in a reactive way.
Is there any advantage to do this way?
It's surely not full reactive
Not really. To take full advantage of the reactive stack it should be used throughout the complete application, from the REST API down to the database access.
Regarding AuthenticationManager there is the reactive counterpart ReactiveAuthenticationManager (https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/authentication/ReactiveAuthenticationManager.html) which is the one that you should use in a Spring-WebFlux application.
Related
I'm trying to solve this: How to rewrite URLs with Spring (Boot) via REST Controllers?
by creating some kind of "filter" which would be applied to every incoming HTTP request.
The matter is covered by some answers like for this question: Spring Boot Adding Http Request Interceptors
but interface HandlerInterceptor deals with javax' HttpServletRequest and HttpServletResponse which are not as practical as the new class introduced by Spring i.e. the ServerWebExchange (see the use of setLocation() in the code below) which appears in an interface whose name sounds promising, org.springframework.web.server.WebFilter:
So I ended with something like:
#Component
public class LegacyRestRedirectWebFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
URI origin = exchange.getRequest().getURI();
String path = origin.getPath();
if (path.startsWith("/api/")) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
URI location = UriComponentsBuilder.fromUri(origin).replacePath(path.replaceFirst("/api/", "/rest/")).build().toUri();
response.getHeaders().setLocation(location);
}
return chain.filter(exchange);
}
}
...in the same way people are doing something similar like:
Spring WebFlux add WebFIlter to match specific paths
WebFilter in WebFlux application
Alas, my filter is never called!!!
The thing is: I am not in a "WebFlux" context (on the contrary to the questions above) because:
I don't need to, and
I tried and got the following problems:
Reactive Webfilter is not working when we have spring-boot-starter-web dependency in classpath (but no definitive answer); marked duplicate of:
Don't spring-boot-starter-web and spring-boot-starter-webflux work together?
Spring WebFlux with traditional Web Security (I have the "traditional" spring-boot-starter-security dependency in my pom.xml plus a #Configuration class extending WebSecurityConfigurerAdapter - but not willing to migrate it to... what by the way?)
Also I don't understand why would I need to be in a WebFlux context, because org.springframework.web.server.WebFilter neither deals with reactive nor Webflux, right? ..or does it? This is not very clear in the Javadoc.
In fact, I didn't find a way to make WebFilter work in a non-WebFlux context, but I could successfully implement such a filter, which both implements javax.servlet.Filter (non-reactive) AND org.springframework.web.server.WebFilter (reactive).
Here is my answer to the other related question: https://stackoverflow.com/a/63780659/666414
We have many Spring MVC projects already, which all use gson instead of jackson for response body encode. Our bean classes are all written based on gson annotation. Now I am setting up a Spring Webflux restful server. It would save a lot of work if we can use the old bean classes from our Spring MVC projects.
I have tried spring.http.converters.preferred-json-mapper=gson property to no avail.
I have tried HttpMessageConverter bean, which is included in webflux packages, but that does not work as in the Spring MVC projects.
I googled a lot and the only thing helpful is to implement org.springframework.http.codec.HttpMessageEncoder class and set it to WebFluxConfigurer.configureHttpMessageCodecs() method:
#Configuration
public class WebConfiguration implements WebFluxConfigurer {
#Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.customCodecs().decoder(new GsonHttpMessageDecoder());
configurer.customCodecs().encoder(new GsonHttpMessageEncoder());
}
private static class GsonHttpMessageEncoder implements HttpMessageEncoder {
...
}
private static class GsonHttpMessageDecoder implements HttpMessageDecoder {
...
}
}
I haven't try this out yet, since it is a little complex. Is there some easy way to replace jackson with gson in Spring Webflux?
Any help is appreciated.
Spring Framework doesn't support GSON as a WebFlux Encoder / Decoder for now. Feel free to follow up on the dedicated issue.
Note that as far as I know, GSON doesn't support non-blocking parsing so even if the support is implemented in Framework, it won't be complete and should not cover streaming input use cases.
The authentication provider is set in
#Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider);
}
Is it possible to change the Authentication Provider during runtime in SpringBoot?
Instance of configured AuthenticationManager eventually will be passed to one of the implementations of org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter. This base filter class has setter for AuthenticationManager so yes, essentially you can replace AuthenticationManager in runtime provided that you have access to the configured authentication filter.
But I doubt that it would be a good idea because you can have several authentication filters where AuthenticationManager should be replaced too. Also Spring does not expect this behavior so finally it can lead to inconsistency in the configuration.
Depending on your needs I would suggest to provide custom implementation of AuthenticationManager that changes its logic accordingly to some conditions.
In a spring boot integration test annotated with #SpringBootTest and ran with #RunWith(SpringRunner.class) I can drop real http post calls to my rest controller via #Autowired TestRestTemplate restTemplate and restTemplate.postForEntity(...)
This works fine, just at the end of the controller -> service -> restclient chain I have a rest client bean, which is calling a 3rd party rest endpoint using RestTemplates inside, so I have to mock this endpoint. I found this lib com.github.tomakehurst.wiremock.client.WireMock, which can be used for this, but was wondering whether there is not a nice spring boot way like for example the way to test a rest client with #RestClientTestto achieve this. I tried to mock the MockRestServiceServer so that I can write expectations and responses on it, but it does not seem to get it. The rest templates inside my rest client are always created as real, thus my call to the 3rd party endpoint fails.
You can inject the mock in the #Before using ReflectionTestUtils
#Inject
private Service service;
#Mock
private RestClient restClient;
#Before
public void setup() {
ReflectionTestUtils.setField(service, "restClient", restClient);
}
Can someone tell me the difference between an AuthenticationManager and an AuthenticationProvider in Spring Security?
How are they used and how are they called. It is my understanding that a SecurityFilter will call the AuthenticationManager to authenticate an Authentication object? But then where does the AuthenticationProvider come into play?
Thanks!
I think the AuthenticationManager delegates the fetching of persistent user information to one or more AuthenticationProviders. The authentication-providers (DaoAuthenticationProvider, JaasAuthenticationProvider, LdapAuthenticationProvider, OpenIDAuthenticationProvider for example) specialize in accessing specific user-info repositories.
Something else is mentioned in this part of the reference manual. It says:
You may want to register additional AuthenticationProvider beans with the ProviderManager and you can do this using the element with the ref attribute, where the value of the attribute is the name of the provider bean you want to add.
In other words, you can specify multiple AuthenticationProviders, for example one that looks for users in an LDAP database and another that looks in an SQL database.
Both AuthenticationManager and AuthenticationProvider are interfaces. They have different functionalities in the Spring Security Flow.
Ref-
Spring Boot + Spring Security Architecture
AuthenticationManager - When user tries to access an application, the http request is intercepted by filter/filter chain. Using the Authentication Object created the filter will then call the authenticate method of the Authentication Manager. The Authentication Manager is only a interface and actual implementation of the authenticate method is provided by the ProviderManager.The ProviderManager has a list of AuthenticationProviders. From it's authenticate method it calls the authenticate method of the appropriate AuthenticateProvider. In response it gets the Principal Authentication Object if the authentication is successful.
AuthenticationProvider - The AuthenicationProvider is an interface with an authenticate and a supports method. It has various implementations like CasAuthenticationProvider or DaoAuthenticationProvider. Depending on the implementation an appropriate AuthenicationProvider implementation is used. It is in the AuthenticationProvider implementation authenticate method where all the actual authentication takes place.
From spring reference
The AuthenticationManager is just an interface, so the implementation can be anything we choose
The default implementation in Spring Security is called ProviderManager and rather than handling the authentication request itself, it delegates to a list of configured AuthenticationProvider s, each of which is queried in turn to see if it can perform the authentication. Each provider will either throw an exception or return a fully populated Authentication object.
Also if you check the source code for AuthenticationManager, ProviderManager and AuthenticationProvider you can see this clearly.
ProviderManager implements the AuthenticationManager interface and it has list of AuthenticationProviders. So if you want to have custom authentication mechanism, you'll need to implement new AuthenticationProvider.