Spring multiple authentication methods for different api endpoints - spring

I want to check for different authentication methods for different endpoints. Methods i want to use are x509 and jwt. I need to use only x509 for certain endpoint and use JWT for all other requests.
Here's my web security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Configuration
#Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/transaction/testf").authenticated().and()
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.userDetailsService(new X509UserDetailsService())
;
}
}
#Configuration
#Order(2)
public static class ApiTokenSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/token", "/api/dealer/login").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
;
}
}
}
This configuration only checks /api/transaction/testf endpoint for x509 certificate and allows all other endpoints to respond. I need other endpoints to return 503 without a jwt token.

You have two filter chains. Neither of them have an entry point pattern properly configured http.antMatcher. That means they are configured to use /** as their entry point pattern.
For example
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
is the same thing as saying:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.anyRequest().fullyAuthenticated()
What we are saying here is
http - the security filter chain
http.antMatcher - the entry point to the security filter chain
http.authorizeRequests - start of my endpoint access restrictions
http.authorizeRequests.antMatchers - list of URLs with specific access
So what you need to do is change your #Order(1) filter chain to narrow down the pattern. For example: http.antMatcher("/api/transaction/**")
Your configuration will now look like
#Configuration
#Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/transaction/**") //customized entry point
.authorizeRequests()
.antMatchers("/api/transaction/testf").authenticated().and()
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.userDetailsService(new X509UserDetailsService())
;
}
}
#Configuration
#Order(2)
public static class ApiTokenSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**") //this is default
.authorizeRequests()
.antMatchers("/oauth/token", "/api/dealer/login").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
;
}
With your existing configuration the filter chain named ApiWebSecurityConfig will trap all calls. The other filter chain, ApiTokenSecurityConfig, is never used.

Since WebSecurityConfigurerAdapter is #Deprecated, preferable use multiple SecurityFilterChain (see updated Multiple HttpSecurity):
#Bean
#Order(1)
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeHttpRequests(authorize -> authorize
.anyRequest().hasRole("ADMIN")
)
.httpBasic(withDefaults());
return http.build();
}
#Bean
public SecurityFilterChain formLoginFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}

Related

Spring Resource Server Multi-tenancy with BasicAuth

So I tried Resource Server Multi-tenancy using Spring docs and
everything works fine, I can pass two different token to access my resource. My question is how do I add BasicAuth functionality to this. I want my resource to be accessible by either of the oauth2 tokens or BasicAuth. I have individual solutions but I am not able to combine both for some reason.
Working solution -> Resource server Multi-tenancy
#EnableWebSecurity
#Order(1)
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.httpBasic()
.disable()
.authorizeRequests(auth -> auth
.anyRequest().authenticated()
).oauth2ResourceServer(oauth2ResourceServer -> {
oauth2ResourceServer.authenticationManagerResolver(this.authenticationManagerResolver);
});
}
//...
Working Solution -> Oauth and BasicAuth both to access same resource
I came across this solution which does this
https://stackoverflow.com/a/36243650/2188126
#EnableWebSecurity
#Order(1)
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatcher(new BasicRequestMatcher())
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(new OAuth2AuthenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
//...
Here's what I have tried(and few other variations) but doesn't work. -> Multi-tenancy + Basic Auth
#EnableWebSecurity
#Order(1)
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatcher(new BasicRequestMatcher())
.authorizeRequests().anyRequest().authenticated()
.and()
.oauth2ResourceServer(oauth2ResourceServer -> {
oauth2ResourceServer.authenticationManagerResolver(this.authenticationManagerResolver);
})
.httpBasic()
.authenticationEntryPoint(new OAuth2AuthenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
//...
Here's my resource server config file
#Configuration
#EnableResourceServer
public class Oauth2SecurityConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated();
}
}

Multiple authentication methods for one endpoint? [duplicate]

I am trying to use Spring Security and I have a use case where I want different login pages and different set of URLs to be secured.
Here is my configuration:
#Configuration
#Order(1)
public static class ProviderSecurity extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/login").permitAll()
.antMatchers("/admin/**").access("hasRole('BASE_USER')")
.and()
.formLogin()
.loginPage("/admin/login").permitAll()
.defaultSuccessUrl("/admin/home")
.failureUrl("/admin/login?error=true").permitAll()
.usernameParameter("username")
.passwordParameter("password")
.and()
.csrf()
.and()
.exceptionHandling().accessDeniedPage("/Access_Denied");
}
}
#Configuration
#Order(2)
public static class ConsumerSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/consumer/login").permitAll()
.antMatchers("/consumer/**").access("hasRole('BASE_USER')")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/consumer/login").permitAll()
.defaultSuccessUrl("/consumer/home")
.failureUrl("/consumer/login?error=true").permitAll()
.usernameParameter("username")
.passwordParameter("password")
.and().csrf()
.and()
.exceptionHandling().accessDeniedPage("/Access_Denied");
}
}
These classes are inner classes of another class MultipleHttpSecurityConfig that has annotation #EnableWebSecurity.
The security for admin/** is working fine, but none of the consumer/** pages are secured, no redirection is happening for login page. I've searched for other answers but none worked.
Look at the Spring Security Reference:
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) { 1
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
#Configuration
#Order(1) 2
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") 3
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Configuration 4
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
1 Configure Authentication as normal
2 Create an instance of WebSecurityConfigurerAdapter that contains #Order to specify which WebSecurityConfigurerAdapter should be considered first.
3 The http.antMatcher states that this HttpSecurity will only be applicable to URLs that start with /api/
4 Create another instance of WebSecurityConfigurerAdapter. If the URL does not start with /api/ this configuration will be used. This configuration is considered after ApiWebSecurityConfigurationAdapter since it has an #Order value after 1 (no #Order defaults to last).
Your second configuration is not used, because your first configuration matches /** (no antMatcher configured). And your first configuration restricts only /admin/**, all other URLs are permitted by default.
Your first WebSecurityConfigurerAdapter's
http
.authorizeRequests()
matches all the URLs, limit it to only URLs start with /admin by using antMatcher:
#Configuration
#Order(1)
public static class ProviderSecurity extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/admin/**")
.authorizeRequests()
.antMatchers("/admin/login").permitAll()
.antMatchers("/admin/**").access("hasRole('BASE_USER')")
.and()
...

how to configure a Content Security Policy in a SecurityConfig class

In my java/spring-boot application, I have a SecurityConfig class as follows:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final BasicAuthenticationProvider basicAuthenticationProvider;
#Autowired
public SecurityConfig(BasicAuthenticationProvider basicAuthenticationProvider) {
this.basicAuthenticationProvider = basicAuthenticationProvider;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.antMatcher(PATH_NAME)
.httpBasic()
.and().authorizeRequests().anyRequest().authenticated()
.and().csrf().disable();
}
}
I need to configure a ContentSecurityPolicy by using the code .and().headers().contentSecurityPolicy("script-src 'self'") within the configure method, but I've tried in the possible places and keep getting errors (e.g. cannot resolve method .and() when trying the below code)
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.antMatcher(PATH_NAME)
.httpBasic()
.and().authorizeRequests().anyRequest().authenticated()
.and().csrf().disable()
.and().headers().contentSecurityPolicy("script-src 'self'");
}
Would anyone know how to properly do this?
You simply don't have to call .and() after csrf().disable(), since disable() already returns the HttpSecurity object that you have to configure.
So something like
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.antMatcher(PATH_NAME)
.httpBasic()
.and().authorizeRequests().anyRequest().authenticated()
.and().csrf().disable()
// notice that .and() is removed from the next line
.headers().contentSecurityPolicy("script-src 'self'");
}

Cannot access to unsecured endpoints in Spring Boot

In my controller I have two endpoints where one is secured and one is public:
#GetMapping("/public")
public String getPublic() {
return "public";
}
#PreAuthorize("hasRole('USER')")
#GetMapping("/private")
public String getPrivate() {
return "public";
}
Secured endpoint works only when I am logged and token with right role is placed in request header. But when I want access to public endpoint without token I always got status 401 with error
Full authentication is required to access this resource
Here is my security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.csrf().disable();
}
}
and authorization server config:
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final UserDetailsService appUserDetailService;
private final AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancer())
.authenticationManager(authenticationManager)
.userDetailsService(appUserDetailService);
}
}
I also tried change .authorizeRequests().anyRequest().authenticated() to this : .authorizeRequests().anyRequest().permitAll() with no change. My preferred way is handle security with annotations. Thank you.
You have two options, can go with either.
Option 1: In your endpoint, change like this.
#PreAuthorize("permitAll()")
#GetMapping("/public")
public String getPublic() {
return "public";
}
And change your configure(HttpSecurity http) method, do like this.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().permitAll()
.and()
.csrf().disable();
}
Option 2: In your configure(HttpSecurity http) method, just do like this.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/public").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
}
antMatchers() will do the trick. We use it a lot. It is also better to have insecured endpoints in different class and control security on class level through request mapping.
antMatchers("/public").permitAll()
Link to spring security api - https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/builders/HttpSecurity.html#antMatcher-java.lang.String-

Spring Security WebSecurityConfigurerAdapter Configuration

I have a simple Spring Boot application with the following 2 endpoints:
int: requires Shibboleth SSO && Authorized Role
ext: no SSO, no authorization required
I've implemented a PreAuthenticationFilter to work with SSO. Below is
the configuration that is not working:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/ext/**").permitAll()
.anyRequest().authenticated()
.and()
.authorizeRequests()
.and()
.addFilter(preAuthenticationFilter());
}
}
Shouldn't PreAuthenticationFilter bypass the /ext endpoint? However, the above configuration forces both endpoints to go to the PreauthenticationFilter. Also tried
web.ignoring().antMatchers("/ext/**")
to no avail.
Here's the rest of my program:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/ext/**").permitAll()
.anyRequest().authenticated()
.and()
.authorizeRequests()
.and()
.addFilter(preAuthenticationFilter());
}
#Override
public void configure(WebSecurity web) throws Exception {
//web.ignoring().antMatchers("/ext/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
authenticationProvider.setPreAuthenticatedUserDetailsService(new ShibbolethUserDetailsService());
auth.authenticationProvider(authenticationProvider);
}
#Bean
RequestHeaderAuthenticationFilter preAuthenticationFilter() throws Exception {
ShibbolethRequestHeaderAuthenticationFilter filter = new ShibbolethRequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}

Resources