How to properly set SecurityContextRepository for a custom UsernamePasswordAuthenticationFilter in Spring Security 6? - spring

Since Spring Security 6.0 an explicit saving of the SecurityContextRepository is required:
https://docs.spring.io/spring-security/reference/migration/servlet/session-management.html#_require_explicit_saving_of_securitycontextrepository
This means that if a custom UsernamePasswordAuthenticationFilter is used, a SecurityContextRepository must be provided to being able to persist the SecurityContext, like this:
#Component
public class InternalUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#PostConstruct
private void setup() {
super.setSecurityContextRepository(
new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
new HttpSessionSecurityContextRepository()));
}
//...
}
It seems to be sufficient to specify the SecurityContextRepository only once in the AuthenticationFilter, although #Max in the example here sets the repository in the SecurityFilterChain as well.
I have tested both variants and it doesn't seem to matter if a SecurityContextRepository is specified in the FilterChain or not. It also doesn't seem to matter if we use a bean or create new repositories for both AuthenticationFilter and FilterChain like this:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity(securedEnabled = true)
public class WebSecurityConfig {
#Bean
public SecurityFilterChain mvcFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authorize -> authorize
.shouldFilterAllDispatcherTypes(true)
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
.requestMatchers("/", "/login/**").permitAll()
.anyRequest().authenticated())
.addFilterAt(internalUsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.securityContext(securityContext -> securityContext
.securityContextRepository(
new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
new HttpSessionSecurityContextRepository()
)))
//...
}
}
This would indicate that only the repository of the AuthenticationFilter is used and the repository of the FilterChain is ignored.
My assumption would therefore be that creating the SecurityContextRepository in the AuthenticationFilter would be sufficient.
So my questions are:
Do I see that right?
Or should I rather define a bean?
And is there any reason to set this bean in the SecurityFilterChain as well?
Many thanks for any help for clarification

Related

Why do unregistered filters get called anyway? [duplicate]

I implemented a customFilter that adds something from the request`s cookies to its headers :
#Component
#Slf4j
public class MyCustomFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
.... some logic...
log.info("Sending request to next chain for validation..");
chain.doFilter(request, response);
log.info("Authentication completed sucessfully");
}
#Bean
// This method is needed to replace the default cookieFilter.json processor of tomcat that ignores the jwt cookieFilter.json
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
return tomcatServletWebServerFactory -> tomcatServletWebServerFactory.addContextCustomizers((TomcatContextCustomizer) context -> {
context.setCookieProcessor(new LegacyCookieProcessor());
});
}
}
My WebSecurityConfigurerAdapter class :
#Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//configuring strategy
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated().and()
.oauth2ResourceServer().jwt().and();
http.csrf().disable();
http.addFilterBefore(new MyCustomFilter (), UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint());
}
}
When I run the code and send a request via postman/curl I see that the filter triggered twice in the
Sending request to next chain for validation..
Sending request to next chain for validation..
Authentication completed sucessfully
Authentication completed sucessfully
I found a few posts about issue and I tried the following solutions :
It happens because spring registers the beans automatically and I add the filter manually in the configure method. Therefore, I removed the manually addition of the filter in the configure() method. The result was that the filter wasnt called at all.
Instead of implementing the filter interface, try to extend the OncePerRequestFilter class. Done that, but the filter still triggered twice.
Tried also to remove the #Component annotation and add the filter manually. In addition I had to move the CookieProcessor bean to the Configuration class. The problem that raised afterwards is that the app fails to start because of the following error :
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
I am using spring-security version 5.3.3.
As a rule of thumb, don't add #Bean methods to #Component classes as those are handled differently than those in #Configuration classes. (See this).
The your code in the #Bean is too complex. Create and return a TomcatContextCustomizer to do the modification. Your code will lead to circulair references which will lead to initializing errors.
Add the following #Bean method to your #SpringBootApplication annotated class
#Bean
public TomactContextCustomizer cookieProcessorCustomizer() {
return (context) -> context.setCookieProcessor(new LegacyCookieProcessor());
}
Now in your Filter either remove the #Component or add an accompying FilterRegistrationBean to prevent it from being added to the regular chain of filters. (Spring Boot automatically registers all detected Filter instances to the regular filter chain).
#Bean
public FilterRegistrationBean<MyFilter> myFilterRegistrationBean(MyFilter myFilter) {
FilterRegistrationBean<MyFilter> frb = new FilterRegistrationBean<>(myFilter);
frb.setEnabled(false);
return frb;
}
If you remove #Component the above snippet isn't needed if you don't then you should reuse the scanned MyFilter instance in your security configuration.
#Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyFilter myFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
//configuring strategy
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated().and()
.oauth2ResourceServer().jwt().and();
http.csrf().disable();
http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint());
}
}

Required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found. message from spring security

I am trying to implement one sample demo for Spring Security with Spring Boot for checking the authentication. I am trying to implement a basic workout for Spring Security and getting the following message,
Description:
Parameter 0 of constructor in com.spacestudy.service.CustomAuthenticationProvider required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.web.AuthenticationEntryPoint' in your configuration.
My security config class SecurityConfig.java,
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationEntryPoint authEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new ApplicationContextHeaderFilter((ApplicationContext) authenticationManager()));
}
}
And my BasicAuthenticationFilter implementation like the following,
#Component
public class CustomAuthenticationProvider extends BasicAuthenticationFilter {
public CustomAuthenticationProvider(AuthenticationManager authenticationManager) {
super(authenticationManager);
// TODO Auto-generated constructor stub
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String bearerToken = request.getHeader("accessToken");
String username = "test";
String password = "test";
if (username != null && !username.isEmpty()) {
return new UsernamePasswordAuthenticationToken(username, null, null);
}
return null;
}
}
How can I resolve this issue?
Lot of problems are there in your code.
(ApplicationContext) authenticationManager()
you can not cast AuthenticationManager to ApplicationContext
.addFilter(new ApplicationContextHeaderFilter(...))
I don't know Why you are using ApplicationContextHeaderFilter for simple demo application.
You should have preferred BasicAuthenticationFilter or even simple default configuration provided for HttpSecurity with .httpBasic()
You should have preferred UsernamePasswordAuthenticationFilter or even simple default configuration provided in HttpSecurity with .formLogin()
CustomAuthenticationProvider extends BasicAuthenticationFilter
An authentication provider is one which implements AuthenticationProvider interface. In your case naming should be xxxAuthFilter.
You have done nothing in below code.(got existing authentication object and set it back without creating an valid authentication object.)
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
Coming to the AuthenticationManager and AuthFilters implementation point of view,
For add filter you can add any implementation of Spring Security provided filters as given below
.addFilter(AnyFilterImplementationFromThisLink)
(But not all filters are auth filters. Where auth filters will attempt to authenticate with the authenticationManager configured)
For example If you consider UsernamePasswordAuthenticationFilter or BasicAuthenticationFilter
you should take care of setting AuthenticationManager where your auth manager should override authenticate() method and it should return Authentication object(where Authentication object will have auth principal, credentials and granted authorities list)
Or
If you don't want to implement authentication manager...
In simple way in your filters(implementation of OncePerRequestFilter) doFilterInternal() method set the `Authentication` object in `SecurityContext`
List<GrantedAuthority> authorityList = new ArrayList<>();
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
authorityList.add(authority);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, authorityList);
//Note UsernamePasswordAuthenticationToken implements Authentication
SecurityContextHolder.getContext().setAuthentication(authToken);
How any auth filter works is if there is a valid Authentication object then filter chain will continue without attempting authentication otherwise it will attemptAuthentication by overrided attemptAuthentication() method.
But your ApplicationContextHeaderFilter is a implementation of OncePerRequestFilter where it has no attemptAuthentication() and i don't know the order of ApplicationContextHeaderFilter if it's order is after creating security context then you can set the authentication object to security context.
Your error seems to be that the AuthenticationManager is not present as a Spring Bean.
Option 1
Register an AuthenticationManager in Spring Bean. All is provided by Spring for do this directly in your SecurityConfig class by overriding the WebSecurityConfigurerAdapter#authenticationManagerBean method like explain in the documentation of it
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Option 2
Avoid to register an AuthenticationManager in Spring, but directly your CustomAuthenticationProvider classs.
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public CustomAuthenticationProvider authenticationProvider() throws Exception {
return new CustomAuthenticationProvider(authenticationManager());
}
}
Don't forget to remove the #Component annotation on the CustomAuthenticationProvider class with this method.
I am not sure but shoudn't the CustomAuthenticationProvider implement AuthenticationProvider and AuthenticationManager is just a container for authentication providers and it seems that you dont have any.
Check this site for more info
https://www.baeldung.com/spring-security-authentication-provider
You can try on this, put it in config security file
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
Create a new java class. And Configure like below:
#Configuration
#RequiredArgsConstructor
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
}

Spring Boot Security PreAuthenticated Scenario with Anonymous access

I have a Spring Boot (1.5.6) application that is using the "pre-authenticated" authentication scenario (SiteMinder) from Spring Security.
I have a need to expose the actuator "health" endpoint anonymously meaning the requests to that endpoint will not go through SiteMinder and as a result, the SM_USER header will not be present in the HTTP Request Header.
The problem I'm facing is that no matter how I try to configure the "health" endpoint, the framework is throwing an org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException because the expected header ("SM_USER") is not present when the request does not go through SiteMinder.
This was my original security config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().antMatchers("/cars/**", "/dealers/**")
.hasAnyRole("CLIENT", "ADMIN")
.and()
.authorizeRequests().antMatchers("/health")
.permitAll()
.and()
.authorizeRequests().anyRequest().denyAll()
.and()
.addFilter(requestHeaderAuthenticationFilter())
.csrf().disable();
}
#Bean
public Filter requestHeaderAuthenticationFilter() throws Exception {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preAuthProvider());
}
#Bean
public AuthenticationProvider preAuthProvider() {
PreAuthenticatedAuthenticationProvider authManager = new PreAuthenticatedAuthenticationProvider();
authManager.setPreAuthenticatedUserDetailsService(preAuthUserDetailsService());
return authManager;
}
#Bean
public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> preAuthUserDetailsService() {
return new UserDetailsByNameServiceWrapper<>(inMemoryUserDetails());
}
#Bean
public UserDetailsService inMemoryUserDetails() {
return new InMemoryUserDetailsManager(getUserSource().getUsers());
}
#Bean
public UserHolder getUserHolder() {
return new UserHolderSpringSecurityImple();
}
#Bean
#ConfigurationProperties
public UserSource getUserSource() {
return new UserSource();
}
I've tried to exclude the /health endpoint a couple different ways to no avail.
Things I've tried:
Configure health endpoint for anonymous access rather than permitAll:
http
.authorizeRequests().antMatchers("/health")
.anonymous()
Configure WebSecurity to ignore the health endpoint:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/health");
}
Turn off security for all actuator endpoints (not idea but I was grasping for straws):
management.security.enabled=false
Looking at the logs, the problem seems to be that the RequestHeaderAuthenticationFilter is getting registered as a top level filter rather than a filter in the existing securityFilterChain:
.s.DelegatingFilterProxyRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*]
o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'webRequestLoggingFilter' to: [/*]
o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestHeaderAuthenticationFilter' to: [/*]
Based on my understanding, because the RequestHeaderAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter, the framework knows where to insert the filter within the chain which is why I'm not tinkering with the addFilterBefore or addFilterAfter variants. Maybe I should be? Does anybody know the correct place to insert the filter explicitly? (I thought the need for explicitly specifying filter order was removed in prior versions of Spring Security)
I know I can configure the RequestHeaderAuthenticationFilter so that it doesn't throw an exception if the header is not present but I'd like to keep that on if at all possible.
I found this SO post that seems to be similar to my problem but unfortunately there's no answer there either.
spring-boot-security-preauthentication-with-permitted-resources-still-authenti
Any help would be greatly appreciated!
Thanks!
The problem was indeed the fact that the RequestHeaderAuthenticationFilter was being registered both as a top level filter (unwanted) and also within the Spring Security FilterChain (desired).
The reason for the "double registration" is because Spring Boot will register any Filter Beans with the Servlet Container automatically.
In order to prevent the "auto-registration" I just had to define a FilterRegistrationBean like so:
#Bean
public FilterRegistrationBean registration(RequestHeaderAuthenticationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
Docs:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-disable-registration-of-a-servlet-or-filter
An alternate/simpler solution:
Just don't mark the RequestHeaderAuthenticationFilter class as an #Bean which is fine because there's no kind of DI needed for that particular filter. By not marking the filter with #Bean, Boot won't try to auto register it which removes the need to define the FilterRegistrationBean.
The provided answer is not complete most likely because Sean Casey made so many trial and error changes that lost track of which configuration actually fixed the problem. I am posting my findings which I believe have the correct configuration:
If you have incorrectly registered the RequestHeaderAuthenticationFilter as a #Bean, as the original answer says, then remove it. Just creating it as a normal instance and adding it as a filter registers it properly:
#Override
protected void configure(HttpSecurity http) throws Exception {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
// configure your filter
http
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilter(filter)
.csrf().disable();
}
The magical configuration which Sean Casey tried but initially failed (due to the double registering of the auth filter) is the WebSecurity configuration:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/health");
}
Notice that adding the HttpSecurity antMatchers does nothing in that case, it seems that WebSecurity is the one taking precedence and controlling what goes through.
EXTRA: Per Spring Security documentation, the WebSecurity.ignore method should be used for static resources, not for dynamic ones which should instead be mapped to allow all users. However in this case it seems the mapping gets overridden by the PreAuthentication filter which forces the use of the more aggressive ignore scenario.

Spring Security Remember Me Does not work With Spring boot 1.5.2, Spring 4.3, Spring Security 4.2 [duplicate]

Right after registration (sign up) I'm logging in my user programmatically via Spring Security:
public register(HttpServletRequest request, String user, String password) {
...
request.login(user, password);
}
This works fine, but it doesn't create the remember-me cookie (although with interactive login the cookie is created fine).
Now I've read in this and this answer, that you have to wire in the implementation of RememberMeServices (I use PersistentTokenBasedRememberMeServices) and then call onLoginSuccess. I haven't been successful to autowire PersistentTokenBasedRememberMeServices.
How to make this work? Is this the right way? Why Spring Security doesn't offer a more convenient way?
P.S.: This is an excerpt from my configuration:
#Configuration
#EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.tokenRepository(new MyPersistentTokenRepository())
.rememberMeCookieName("rememberme")
.tokenValiditySeconds(60 * 60 * 24)
.alwaysRemember(true)
.useSecureCookie(true)
.and()
....
...
}
}
You didn't mention the Spring version. Below configuration will work with Spring 4 but you can modify it for other version. In your WebSecConf class autowire PersistentTokenRepository and UserDetailsService interfaces. Add Bean to get PersistentTokenBasedRememberMeServices instance.
#Configuration
#EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {
#Autowired
PersistentTokenRepository persistenceTokenRepository;
#Autowired
UserDetailsService userDetailsService;
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.tokenRepository(persistenceTokenRepository)
.rememberMeCookieName("rememberme")
.tokenValiditySeconds(60 * 60 * 24)
.alwaysRemember(true)
.useSecureCookie(true)
.and()
....
...
}
#Bean
public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices persistenceTokenBasedservice = new PersistentTokenBasedRememberMeServices("rememberme", userDetailsService, persistenceTokenRepository);
persistenceTokenBasedservice.setAlwaysRemember(true);
return persistenceTokenBasedservice;
}
}
Now in your Controller or class where you are doing programmatic login, autowire PersistentTokenBasedRememberMeServices and add below code inside the method to invoke loginSuccess method.
#Autowired
PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices;
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
persistentTokenBasedRememberMeServices.loginSuccess(request, response, auth);
}
I've stumbled on this issue and struggled a bit to get everything working correctly, for future reference this is how to set things up.
Define a RememberMeService bean configured to your needs.
Use TokenBasedRememberMeServices if you want a simple hash based token system or PersistentTokenBasedRememberMeServices if you'd rather persist the tokens to database. Both solutions are described in further details here : https://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/remember-me.html
Please note that the constructor first argument is not the cookie name but the key used to validate remember-me tokens.
#Configuration
public class SecurityBeans {
#Autowire
PersistentTokenRepository persistenceTokenRepository;
#Autowired
UserDetailsService userDetailsService;
#Bean
public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices persistenceTokenBasedservice = new TokenBasedRememberMeServices("remember-me-key", userDetailsService, persistenceTokenRepository);
persistenceTokenBasedservice.setCookieName("rememberme");
persistenceTokenBasedservice.setTokenValiditySeconds(60 * 60 * 24);
persistenceTokenBasedservice.setAlwaysRemember(true);
persistenceTokenBasedservice.setUseSecureCookie(true);
return persistenceTokenBasedservice;
}
}
You should inject the RememberMeService directly when configuring HttpSecurity. You also have to configure the exact same key as defined in your RememberMeService because the configurer also sets up the RememberMeAuthenticationProvider which checks that the remember-me token key generated by RememberMeService is correct.
#Configuration
#EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {
#Autowired
RememberMeServices rememberMeServices;
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.rememberMeServices(rememberMeServices)
.key("remember-me-key")
.and()
....
...
}
}
And finally you should invoke RememberMeService's loginSuccess in your method doing the programmatic login as described in abaghel's answer.

Spring Boot 1.3.3 #EnableResourceServer and #EnableOAuth2Sso at the same time

I want my server be a ResourceServer, which can accept a Bearer Access token
However, If such token doesn't exist, I want to use the OAuth2Server to authenticate my user.
I try to do like:
#Configuration
#EnableOAuth2Sso
#EnableResourceServer
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
However, in this case, only the #EnableResourceServer annotation works. It returns
Full authentication is required to access this resource
And do not redirect me to the login page
I mentioned that the #Order is important, if I add the #Order(0) annotation,
I will be redirect to the login page, however, I cannot access my resource with the access_token in Http header:
Authorization : Bearer 142042b2-342f-4f19-8f53-bea0bae061fc
How can I achieve my goal? I want it use Access token and SSO at the same time.
Thanks~
Using both configuration on same request would be ambiguous. There could be some solution for that, but more clear to define separate request groups:
OAuth2Sso: for users coming from a browser, we want to redirect them to the authentication provider for the token
ResourceServer: usually for api requests, coming with a token they got from somewhere (most probably from same authentication provider)
For achieving this, separate the configurations with request matcher:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Bean("resourceServerRequestMatcher")
public RequestMatcher resources() {
return new AntPathRequestMatcher("/resources/**");
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.requestMatcher(resources()).authorizeRequests()
.anyRequest().authenticated();
}
}
And exclude these from the sso filter chain:
#Configuration
#EnableOAuth2Sso
public class SsoSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("resourceServerRequestMatcher")
private RequestMatcher resources;
#Override
protected void configure(final HttpSecurity http) throws Exception {
RequestMatcher nonResoures = new NegatedRequestMatcher(resources);
http
.requestMatcher(nonResoures).authorizeRequests()
.anyRequest().authenticated();
}
}
And put all your resources under /resources/**
Of course in this case both will use the same oauth2 configuration (accessTokenUri, jwt.key-value, etc.)
UPDATE1:
Actually you can achieve your original goal by using this request matcher for the above configuration:
new RequestHeaderRequestMatcher("Authorization")
UPDATE2:
(Explanation of #sid-morad's comment)
Spring Security creates a filter chain for each configuration. The request matcher for each filter chain is evaluated in the order of the configurations.
WebSecurityConfigurerAdapter has default order 100, and ResourceServerConfiguration is ordered 3 by default. Which means ResourceServerConfiguration's request matcher evaluated first. This order can be overridden for these configurations like:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration configuration;
#PostConstruct
public void setSecurityConfigurerOrder() {
configuration.setOrder(3);
}
...
}
#Configuration
#EnableOAuth2Sso
#Order(100)
public class SsoSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
So yes, request matcher is not needed for SsoSecurityConfiguration in the above sample. But good to know the reasons behind :)

Resources