Can we load different security configuration based on Mediatype, i.e. One of REST and one for web? - spring

I have developed a regular spring mvc application, and want to add some rest controller for developing mobile application. I have written rest controller, and multi spring security configurations.
Problem is, they are in precedence, hence both are loaded at once, and whole application breaks down.I want to use one based upon what type of request it is getting, for example, If I am requesting from Postman, Rest API security configuration should work and if we are using web, web security configuration should work.
Here is my implementation, I don't know how to achieve that, Please suggest what is the right way to doing this. As separating whole Thymeleaf and MVC controller , and moving altogether with Angular is not possible at this stage.
Please note that, we have all rest api defined in /v1/ap1/** and all other mvc part is in /**
Any comments, suggestions would be much appreciated, it is killing my days since 3 days. Thanks in advance
#Configuration
#EnableWebSecurity
public class SecurityConfig {
// ... other codes
#Configuration
#Order(1)
public static class RestAPISecurity extends WebSecurityConfigurerAdapter {
//.. other codes
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/signin/**").permitAll()
.antMatchers("/api/v1/**").hasAnyAuthority("ADMIN", "USER")
.antMatchers("/api/users/**").hasAuthority("ADMIN")
.antMatchers("/api/v1/**").authenticated()
.antMatchers("/login", "/logout", "/register", "/j_spring_security_check").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(new CustomAccessDeniedHandler());
}
// .. other codes
#Configuration
#Order(2)
public static class MVCSecurityConfiguration extends WebSecurityConfigurerAdapter {
//.. other codes
// form login and other MVC stuffs
}
}

You can add a request matcher for the first spring security filter chain and every thing else goes to second chain
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(httpServletRequest -> {
String userAgent = httpServletRequest.getHeader("User-Agent");
//If you want to check based on content type
String contentType = httpServletRequest.getContentType();
return userAgent.contains("....")
//check what value postman sends as user agent and use it
})
.sessionManagement()
....
}

Related

Spring Security OAuth - how to disable login page?

I want to secure my application with Spring Security, using OAuth 2. However, I don't want the server to redirect incoming unauthorized requests, but instead to respond with HTTP 401. Is it possible?
Example: this code redirects requests to a default login page.
application.properties
spring.security.oauth2.client.registration.google.client-id=...
spring.security.oauth2.client.registration.google.client-secret=...
AuthConfig.java
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login();
// https://stackoverflow.com/questions/31714585/spring-security-disable-login-page-redirect
// deos not work
// .and()
// .formLogin().successHandler((request, response, authentication) -> {});
}
}
You need to create new authentication entry point and set it in configuration.
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login();
}
}
public class AuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public AuthenticationEntryPoint() {
super("");
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendError(401, "Unauthorized");
}
}
You need to set oauth2Login.loginPage in your HttpSecurity config and create a controller mapping to return whatever you want. Here's a simple example.
So in your security config
http
.authorizeRequests()
.antMatchers("/noauth").permitAll()
.oauth2Login()
.loginPage("/noauth")
In a controller
#GetMapping("/noauth")
public ResponseEntity<?> noAuth() {
Map<String, String> body = new HashMap<>();
body.put("message", "unauthorized");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(body);
}
You can pass a map or pojo to the body method.
I would like to expand on Petr's answer by explaining that apparently for the time being first of all, the default login page is shown when there are more than one OAuth2 configured providers. I would expect that Spring Boot would have a smart trick to bypass this page easily and choose the right provider automatically, basing e.g. on the existence of the provider's client ID in the original request. I found out the hard way that this is not the case. So the way to do this is.. this not very apparent trick of providing a custom handler for failures - that will REDIRECT the user to the correct OAuth2 endpoint for each provider, based on the original HTTP request URL. I tried this and it works and I spent a whole day trying all manners of other solutions - my original scenario was to pass additional parameters to OAuth2 scheme in order to be able to get them back on successful authentication - they used to do this appending Base64 encoded information to the "state" URL request parameter, but Spring Security does not allow this at the moment. So the only alternative was to call a Spring Security-protected URL with those parameters already there, so when the successful authentication happens, this URL is accessed again automatically with those parameters intact.
Related: Multiple Login endpoints Spring Security OAuth2

AntMatcher and contextPath for API security

I have spring boot application. I have configured OAuth2 - both authorization and resource servers (separated). In the resource server (application.properties) I have:
server.servlet.context-path=/api
as well as:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
(...)
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/actuator/**", "/api-docs/**").permitAll()
.antMatchers("/api/**" ).authenticated();
}
}
The problem is that, api is not actually secured at all. Thanks to doc and #dur's answer I know that
The pattern must not contain the context path
Indeed, changing from:
.antMatchers("/api/**" ).authenticated();
to:
.antMatchers("/**" ).authenticated();
works fine. But the question is: is it possible to use context-path in this use case, instead of using /** ? I could repeat .antMatchers() for each and every controller (or use /**) but maybe there is a way to use context-path ?
Inject the property into variable and use that in code
I am also demonstrating IMHO nicer way of writing the conf with lambda where you do not need to use ".and()" and you can see scope blocks better.
Empty .requestMatchers().and() does nothing, so you can remove it.
which would also be more obvious in lambda notation :
.requestMatchers(matchers -> matchers)
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
(...)
#Value("${server.servlet.context-path:''}")
private String contextPath; // <<<< I am the path !
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.antMatchers(contextPath + "/actuator/**", "/api-docs/**").permitAll()
.antMatchers(contextPath + "/**" ).authenticated()
);
}
}
But if you really want to, you can also write the code the old way too.
It has no effect on using the variable.
:
http.
.authorizeRequests()
.antMatchers(contextPath + "/actuator/**", "/api-docs/**")
.permitAll()
.antMatchers(contextPath + "/**" )
.authenticated()
.and());

Authorisation and role check in a spring boot application

I have a spring boot application where I have several REST APIs. Now I want to do a authorisation check for all the requests. This involves each request using a x509 client certificate and then some business logic for authorisation purpose, something similar to role checking.
what is the best place to do this i.e. should this check be done in DispatcherServlet - doDispatch method? Doing the same check for each request in Controller doesn't make much of a sense.
Can someone suggest where to put these kind of checks in a spring boot application ?
You have two choices. Java config or xml config.
I recommend java config. Create a config class and configure like this
For java config it would look something like
#Configuration
#EnableWebSecurity
public class LoginSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
authenticationMgr.inMemoryAuthentication()
.withUser("jduser").password("jdu#123").authorities("ROLE_USER")
.and()
.withUser("jdadmin").password("jda#123").authorities("ROLE_USER","ROLE_ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/homePage").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
.antMatchers("/userPage").access("hasRole('ROLE_USER')")
.antMatchers("/adminPage").access("hasRole('ROLE_ADMIN')")
.and()
.formLogin().loginPage("/loginPage")
.defaultSuccessUrl("/homePage")
.failureUrl("/loginPage?error")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/loginPage?logout");
}
}

Allowing a client application to configure http security from an already wired WebSecurityConfigurerAdapter

I am using spring-boot and spring-security.
I have a generic WebSecurityConfigurerAdapter setup for a number of different projects. The problem is that I want custom Controller security for each project, everything else remains the same. The most obvious solution was to make it Abstract and force each project to extend it but I suspect there is a better way through events or something.
Here is the configure method of WebSecurityConfigurerAdapter
#Override
protected void configure(final HttpSecurity http) throws Exception {
...
http.authorizeRequests()
.antMatchers("/health*").permitAll()
.antMatchers("/endpoints/**").permitAll()
.antMatchers("/rest/open/**").permitAll()
.antMatchers("/login/impersonate*").hasAnyRole("ADMIN", "ADMINISTRATOR")
// AT THIS POINT I WOULD LIKE EACH PROJECT TO OPTIONALLY CONFIGURE http AS THEY WISH
http.authorizeRequests().antMatchers("/**").authenticated();
...
}
Is there a cool way to do this in spring though a bean configuration or something?
#Bean //something like this perhaps????
public void configureSecurity(final HttpSecurity http) {
http.authorizeRequests()
.antMatchers("/rest/admin*").hasAnyRole("ADMIN", "ADMINISTRATOR")
}
you can use multiple WebSecurityConfigurerAdapter classes, juste make sure each of them are in packages scanned by Springboot autoconfiguration.
if a client project want to override existing security constraints, add the #Order annotation:
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ClientSecurityConfig extends WebSecurityConfigurerAdapter {
public void configure(HttpSecurity http) {
http.antMatcher("/rest/admin*").authorizeRequests().anyRequest().hasAnyRole("ADMIN", "ADMINISTRATOR");
}
}
Pay attention to the antMatcher() that is before the authorizeRequests(), this is done to limit the scope of the client config. without that, it will erase all of the default config (every URL except /rest/admin* will return 403 Unauthorized).

#EnableOAuth2Sso - How to protect / unprotect resources?

I’m trying to use the #EnableOAuth2Sso feature in Spring Cloud Security. Specifically, I’m attempting to protect some resources with OAuth2 while leaving others publicly accessible. I've managed to get this working, but I'm looking at the resulting code and wondering if there is a cleaner way.
I’m following the documentation here: https://github.com/spring-cloud/spring-cloud-security/blob/master/src/main/asciidoc/spring-cloud-security.adoc AND similar guidance from the Spring Boot reference. I have a tiny code example that illustrates my dilemma: https://github.com/kennyk65/oAuthSsoExample.
In a nutshell, I want the localhost:8080/unprotected resource to be publicly available, and I want the localhost:8080/protected resource to require OAuth2 (via github, as configured). I’m able to get the basic OAuth2 behavior to work just fine, but causing /unprotected to be publicly available is problematic.
First, The docs indicate that you can just use the OAuth2SsoConfigurer’s match() method to specify the resources to protect. I've found this doesn't work; when I try I get an IllegalStateException saying that at least one mapping is required. This appears to be referring to the unimplemented configure(HttpSecurity) method.
Next, I’ve tried to specify a mapping in configure(HttpSecurity) that states that the ‘unprotected’ resources should be unprotected. However, this results in Http basic security being applied to that resource. Curiously, this results in the ‘protected’ resource being completely public!
// This results in “unprotected” being protected by HTTP Basic
// and “protected” being completely open!
#Configuration
protected static class OauthConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/protected/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/unprotected/**").permitAll();
}
}
On a whim I tried deliberately adding that the protected resource should be authenticated. This resulted in the protected resource getting OAuth2 protection (hurrah!) but the unprotected resource got http basic security applied (huh?).
// This results in “protected” being protected by OAuth 2
// and “unprotected” being protected by HTTP Basic, even though we say permitAll():
#Configuration
protected static class OauthConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/protected/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/protected/**”).authenticated();
.antMatchers("/unprotected/**").permitAll();
}
}
At wit’s end to try to find the magic combination, I tried simply switching HTTP basic authentication off using security.basic.enabled: false. This worked (hurrah!), though I’m still a bit puzzled what the issue is with the mappings.
So I guess my question is, is this correct? What is the best way to protect some resources with OAuth 2 and leave others alone?
If you match on /protected/** then it makes no sense to then add an access rule to /unprotected/** (the path doesn't match so the rule will never be applied). You either need another filter chain for your "unprotected" resources, or a wider match for the SSO one. In the former case, the default one that you get from Spring Security will do if you don't mind switching off the security it is providing. E.g.
#Configuration
protected static class OauthConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/protected/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**”).authenticated();
}
}
and set security.basic.enabled=false.

Resources