How can I configure Spring SAML to send "assertionConsumerServiceIndex" instead of "assertionConsumerServiceUrl" in the SAML Authentication Requests?
Another question:
Where can I report an issue of the Spring security SAML project?
Thanks
Got it working but not through config file. I found following comment in the Spring SAML source code:
// AssertionConsumerServiceURL + ProtocolBinding is mutually exclusive with AssertionConsumerServiceIndex, we use first one here
I had to modify buildReturnAddress method of WebSSOProfileImpl class as below to insert AssertionConsumerServiceIndex instead of AssertionConsumerServiceURL + ProtocolBinding when creating new AuthNRequest:
if (service != null) {
request.setAssertionConsumerServiceIndex(service.getIndex());
}
Yes, We need to alter the way buildReturnAddress() behaves in f/w. We can do this simply by extending WebSSOProfileImpl and overriding buildReturnAddress() method in our service provider code.
Related
I'm trying to secure my application with Spring Security oAuth2. Is there a way to return 401 for some URL while other pages go to the login page if a user is not logged in.
For example, return login form for /ui/*, and return 401 for /api/*
I tried to use two SecurityWebFilterChain, but didn't success.
My spring security version is something different, and the codes are something like following:
http.exceptionHandling().authenticationEntryPoint(new DelegatingServerAuthenticationEntryPoint(
new DelegatingServerAuthenticationEntryPoint.DelegateEntry(
ServerWebExchangeMatchers.pathMatchers("/ui/**"),
new RedirectServerAuthenticationEntryPoint("/login")
),
new DelegatingServerAuthenticationEntryPoint.DelegateEntry(
ServerWebExchangeMatchers.pathMatchers("/api/**"),
new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)
))
);
You can configure Spring Security to use a custom AuthenticationEntryPoint, something like:
http
// ... your configuration
.exceptionHandling((ex) -> ex
.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"), new AntPathRequestMatcher("/ui/**"))
.defaultAuthenticationEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), new AntPathRequestMatcher("/api/**"))
);
This way Spring Security will pick up the AuthenticationEntryPoint based on the RequestMatcher#matches method
I'm currently migrating from old deprecated Spring Security SAML Extension 1.0.10 to the SAML implementation in Spring Security 5.6.1.
In the old extension there was the possibility to disable the signature verification of the SAML response (property wantAssertionSigned in Spring Security SAML Extension documentation). This was very helpful for me during testing.
I wonder if this is also possible in Spring Security 5.6.1?
I searched in the source code and found the class OpenSamlMetadataResolver where it seems to me that this is hard-coded and cannot be changed:
private SPSSODescriptor buildSpSsoDescriptor(RelyingPartyRegistration registration) {
SPSSODescriptor spSsoDescriptor = build(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
(...)
spSsoDescriptor.setWantAssertionsSigned(true);
(...)
return spSsoDescriptor;
}
Also the code in OpenSaml4AuthenticationProvider doesn't seem to offer an easy way to configure private variable assertionSignatureValidator to override validation behaviour.
Any help is appreciated.
In Spring Security 5.7.0, which will be released this Monday, May 16, 2022, the hard-coded line is removed. Therefore no more signature verification by default.
You will also be able to customize the EntityDescriptor if you want, something like this:
openSamlMetadataResolver.setEntityDescriptorCustomizer(
(parameters) -> parameters.getEntityDescriptor().setEntityID("overriddenEntityId"));
You can always try the milestone releases before the GA.
Changing the Order of the Spring Security WebFilter
I have an API Gateway implemented using Spring Cloud Gateway that uses Spring Security. Spring Security for WebFlux is implemented as a WebFilter right at the beginning of the filter chain. So after successful authentication the request would be forwarded to Spring Cloud Gateway's RoutePredicateHandlerMapping, which would try to deduce the destination based on the URL pattern, and then it would go to a FilteringWebHandler to execute the other filters of Spring Cloud Gateway.
My problem is the following: I have implemented a customized authentication algorithm which uses query string and header variables as credentials for authentication according to the requirements of the project, an this is working without any problem. The problem occurred when we needed to add a small customization for the authentication algorithm that is path independent. When the request reaches the WebFilter of Spring Security, pattern matching is not yet done so I do not know which application does it point to, for example:
app1:
-Path: /app1/**
app2:
-Path: /app2/**
Which means that instead of having authentication -> route mapping -> filtering web handler I should do route mapping -> authentication -> filtering web handler. Not that these three components are not similar, one of them is a filter another is a mapper and the last one is web handler. Now I know how to customize them but the problem is that I do not know how to intercept the Netty server building process in order to change the order of these operations. I need to wait for the building process to end and alter the content of the server before it starts. How can I do that?
EDIT: here is the final solution:
So here is how I did it:
Goal: removing the WebFilter of Spring Security from the default HttpHandler, and inserting it between RoutePredicateRouteMapping and the FilteringWebHandler of Spring Cloud Gateway
Why: Because I need to know the Application ID while carrying on my customized authentication process. This Application ID is attached to the request by the RoutePredicateRouteMapping by matching the request's URL to a predefined list.
How did I do it:
1- Removing the WebFilter of Spring Security
I created an HttpHandler bean that invokes the default WebHttpHandlerBuilder and then customize the filters. As a bonus, I removed unneeded filters in order to increase the performance of my API Gateway
#Bean
public HttpHandler httpHandler() {
WebHttpHandlerBuilder webHttpHandlerBuilder = WebHttpHandlerBuilder.applicationContext(this.applicationContext);
MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter = this.applicationContext.getBean(MY_AUTHENTICATED_HANDLER_BEAN_NAME, MyAuthenticationHandlerAdapter.class);
webHttpHandlerBuilder
.filters(filters ->
myAuthenticationHandlerAdapter.setSecurityFilter(
Collections.singletonList(filters.stream().filter(f -> f instanceof WebFilterChainProxy).map(f -> (WebFilterChainProxy) f).findFirst().orElse(null))
)
);
return webHttpHandlerBuilder.filters(filters -> filters
.removeIf(f -> f instanceof WebFilterChainProxy || f instanceof WeightCalculatorWebFilter || f instanceof OrderedHiddenHttpMethodFilter))
.build();
}
2- Wrapping Spring Cloud Gateway's FilteringWebHandler with Spring Web's FilteringWebHandler with the added WebFilter
I created my own HandlerAdapter which would match against Spring Cloud Gateway's FilteringWebHandler and wrap it with Spring Web's FilteringWebHandler plus the security filter I extracted in the first step
#Bean
public MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter() {
return new MyAuthenticationHandlerAdapter();
}
public class MyAuthenticationHandlerAdapter implements HandlerAdapter {
#Setter
private List<WebFilter> securityFilter = new ArrayList<>();
#Override
public boolean supports(Object handler) {
return handler instanceof FilteringWebHandler;
}
#Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
org.springframework.web.server.handler.FilteringWebHandler filteringWebHandler = new org.springframework.web.server.handler.FilteringWebHandler((WebHandler) handler, securityFilter);
Mono<Void> mono = filteringWebHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
This way I could achieve better performance with highly customized HttpHandler pipeline that I suppose to be future-proof
END EDIT
Spring Security for WebFlux is implemented as a WebFilter which is executed almost as soon as a request is received. I have implemented custom authentication converter and authentication manager which would extract some variables from the header and URL and use them for authentication. This is working without any problem.
Now I needed to add another variable taken from RoutePredicateRouteMapping before authentication is done. What I want exactly is to remove the WebFilter (called WebFilterChainProxy) from its current position and put it between the RoutePredicateRouteMapping and the FilteringWeHandler.
Here is how the default process goes:
ChannelOperations calls ReactorHttpHandlerAdapter which calls HttpWebHandlerAdapter, ExceptionHandlingWebHandler, and then org.springframework.web.server.handler.FilterWebHandler.
This WebHandler would invoke its filters and then call the DispatchHandler. One of those filters is the WebFilterChainProxy that does the authentication for Spring Security. So first step is removing the filter from here.
Now the DispatchHandler which is called after the filters would invoke RoutePredicateHandlerMapping, which would analyze the routes and give me the route ID that I need, and then it would call the org.springframework.cloud.gateway.handler.FilteringHandler (this is not the same FilteringHandler above), and that in turn would call the other filters of the Spring Cloud Gateway. What I want here is to invoke the filter after RoutePredicatehandlerMapping and before org.springframework.cloud.gateway.handler.FilteringHandler.
What I ended doing was the following:
I created and WebHttpHandlerBuilder that would remove WebFilterChainProxy and pass it as a parameter to a customized DispatcherHandler. Now that the filter is removed the request would pass the first layers without requiring authentication. In my customized DispatcherHandler I would invoke the RoutePredicateHandlerMapping and then pass the exchange variable to the WebFilterChainProxy to do the authentication before passing it to the org.springframework.cloud.gateway.handler.FilteringHandler, which worked perfectly!
I still think that I'm over engineering it and I hope that there is a way to do it using annotations and configuration beans instead of all these customized classes (WebHttpHandlerBuilder and DispatcherHandler).
You should probably implement that security filter as a proper GatewayFilter, since only those are aware of the other GatewayFilter instances and can be ordered accordingly. In your case, you probably want to order it after the routing one.
Also, please don't cross-post, the Spring team is actively monitoring StackOverflow.
I had a similar problem. The accepted solution, while interesting, was a bit drastic for me. I was able to make it work simply by adding my custom filter before SecurityWebFiltersOrder.AUTHENTICATION in the security configuration. This is similar to what I've done with success in a regular Spring mvc application.
Here's an example using oauth authentication. tokenIntrospector is my custom introspector, and requestInitializationFilter is the filter that grabs the tenant id and stashes it in the context.
#AllArgsConstructor
#Configuration
#EnableWebFluxSecurity
public class WebApiGatewaySecurityConfiguration {
private final GatewayTokenIntrospector tokenIntrospector;
private final GatewayRequestInitializationFilter requestInitializationFilter;
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// #formatter:off
http
.formLogin().disable()
.csrf().disable()
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.opaqueToken(c -> c.introspector(tokenIntrospector)))
.addFilterBefore(requestInitializationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
return http.build();
// #formatter:on
}
}
I am trying to write a grails plugin using Spring social core plugin. I get the provider popup and after I enter user and password it is giving me 404. As I debugged the code, it is coming into SpringSocialProviderSignInController handleSignIn() method and it is not getting anything for signup url. In grails plugin this is the code snipet
if (userIds.size() == 0) {
if (log.isDebugEnabled()) {
log.debug("No user found in the repository, creating a new one...")
}
ProviderSignInAttempt signInAttempt = new ProviderSignInAttempt(connection, connectionFactoryLocator, usersConnectionRepository)
request.setAttribute(ProviderSignInAttempt.SESSION_ATTRIBUTE, signInAttempt, RequestAttributes.SCOPE_SESSION)
//TODO: Document this setting
result = request.session.ss_oauth_redirect_on_signIn_attempt ?: config.page.handleSignIn
}
I see that even in the regular spring social web jar this has similar logic. Except in the web there is a default set on signupUrl. I tried giving the same value(/signup) in config.page.handleSignIn but it did not help.
if (userIds.size() == 0) {
ProviderSignInAttempt signInAttempt = new ProviderSignInAttempt(connection, connectionFactoryLocator, usersConnectionRepository);
request.setAttribute(ProviderSignInAttempt.SESSION_ATTRIBUTE, signInAttempt, RequestAttributes.SCOPE_SESSION);
return redirect(signUpUrl);
}
In general, I am trying to understand what this signUpUrl does. I am not able to go further after this. Is it mandatory to give signUpUrl? My understanding was no.
Have asked the same question in spring social forum, but is not getting any response there. Trying my luck here.
Found meaningful answer in spring forum : Spring Forum Link
I am trying to perform Spring Security Authentication using MyBatis.
My spring-security file looks as follows
<global-method-security pre-post-annotations="enabled" />
<beans:bean id="myUserService"
class="com.service.CustomService" />
<authentication-manager>
<authentication-provider user-service-ref="myUserService" />
</authentication-manager>
My CustomService class implements UserDetailsService and in the loadUserByUsername method , I am using my MyBatis Dao to load the Users from the DB.
#Autowired
private MyBatisDao dao;
In my Controller class I am using the same annotation , and in that case it returns the proper object.
But when I use the same in the CustomService class it returns null.
I am unable to understand the reason for it. Am i missing something. Please help
Any example of Spring Authentication using MyBatis would help, I can understand it and then maybe figure out the issue
I fix this issue by another - not recommended way.
In this case, #controller can treat the db work right,
so I do basic auth in controller and send that result to custom service.
Custom service has no valid auth function in this case.
If useename and password is valid, custom service is called,
and that just returns dummy auth result.
If username and password is invalid, i just didn't call the auth in controller.
Though it's not quite right way, it works fine with some special treat for user role.
I want to know there's a better way to solve this problem,
but i have no time to find, right now.
Issue solved by taking an alternative approach in constructing the MyBatis Object.
I created a singleton class which returns the SqlSessionFactory Object, and using the same in my code for calling the Mapper Interfaces methods.
Sample code snippet below
InputStream myBatisConfigStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.xml");
if (null == sqlSessionFactory){
sqlSessionFactory = new SqlSessionFactoryBuilder().build(myBatisConfigStream);
sqlSessionFactory.getConfiguration().addMapper(IOMapper.class);
}
try {
myBatisConfigStream.close();
} catch (IOException e) {
e.printStackTrace();
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
The config.xml file contains only the dataSource.
Hope this helps.