How to deny all requests without specific roles - SpringBoot security config - java

I have the following SpringBoot web security configuration.
For authorization, I want to automatically forbid all requests that authentication does not include the roles ADMIN, SUPER_ADMIN, CUSTOMER but this denies all requests and only picks up the denyAll attribute in the springExprFilter hence it votes to deny access.
What am I missing from my configuration?
#EnableWebSecurity
#RequiredArgsConstructor
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final MemberDetailsService memberDetailsService;
private final JwtRequestFilter jwtRequestFilter;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberDetailsService).passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.disable()
.csrf()
.disable()
.authorizeRequests()
// permit all request for authentication
.antMatchers("/v1/authenticate")
.permitAll()
.and()
.authorizeRequests()
// permit all request with the following list of roles
// methods will enforce their own authorization logic
.antMatchers("/v1/members/")
.hasAnyAuthority("ADMIN", "CUSTOMER", "SUPER_ADMIN")
.and()
.authorizeRequests()
.anyRequest()
.denyAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}

I figured out the spring security configuration expression was correct. The issue was that the antmatcher .antMatchers("/v1/members/") was incorrect. It was implying to match a request with the path /v1/members/ which was not the intended functionality.
For any interested party, the request I was making was GET v1/members/:uuid.
I ought to have used a wildcard .antMatchers("/v1/members/**") to catch all request for the member endpoint.

Related

Spring security login returns 302 when app is hosted on AWS

I have the following security config file :
#Configuration
#EnableWebSecurity
public class EvaluariSecurityConfig extends WebSecurityConfigurerAdapter {
// add a reference to our security data source
#Autowired
private DataSource securityDataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// use jdbc authentication ... oh yeah!!!
auth.jdbcAuthentication().dataSource(securityDataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/evaluator/**").hasRole("EVALUATOR")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticateTheUser")
.successHandler(new SavedRequestAwareAuthenticationSuccessHandler())
.permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.and()
.csrf().ignoringAntMatchers("/authenticateTheUser")
.and()
.csrf()
.disable()
.cors();
}
}
My problem is that the login works fine when running locally but when I host the app on AWS Elastic Beanstalk, it sometimes responds with the 302 http code. It's important to say that this is not happening all the time but it's quite annoying when it does. At the moment the app is not working over https yet (it's http).
Any ideas why this is happening ?

401 on request where `permitAll()` specified

I have this WebSecurityConfigurerAdapter config:
#EnableWebSecurity
public class HttpSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.authorizeRequests()
.mvcMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt()
;
}
}
When I make a request to auth, I get a 401, until I pass some authorisation - which isn't appropriate for this endopint.
I would assume this has something to do with .anyRequest().authenticated(). I had read before that this shouldn't affect the permitAll()s - have I misconfigured something?
Your request is probably being rejected because you didn't provide a CSRF token. By default, Spring Security enables it for every POST request and you need to explicitly disable it.
#EnableWebSecurity
public class HttpSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf().disable()
.authorizeRequests()
.mvcMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}
You can add the following property to your application.yml file so you can be able to see why your request is being rejected if CSRF is not the case:
logging:
level:
org.springframework.security: TRACE
If u are using jwt filter,it won't work even if you added permitAll() .if you remove the filter, it will work fine.

spring boot oauth2 feign allow anonymous requests

I have a client service that distribute a single page application. All the requests from the single page app pass through the client service that uses proxies (Feign) to redirect the calls.
I'd like to allow anonymous calls but I'm not able to do that with my current configuration.
So to make it simpler I have three services : a client, an oauth2 server and an oauth2 resource server.
The oauth2 server is also a resource server.
The client is connected to the oauth2-server with this configuration
security:
oauth2:
client:
clientId: autorisation_code_client
clientSecret: *******
accessTokenUri: https://localhost:****/oauth2-server/oauth/token
userAuthorizationUri: https://localhost:****/oauth2-server/oauth/authorize
#tokenCheckUri: https://localhost:****/oauth2-server/oauth/check_token
resource:
userInfoUri: https://localhost:****/oauth2-server/me
Here is the WebSecurityConfigurerAdapter class of the client, when an user try to access to the login path he's redirected to the oauth2-server to authenticate himself.
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers(
"/",
"/index.html",
"/login**",
"/logout**",
//resources
"/assets/**",
"/static/**",
"/*.ico",
"/*.js",
"/*.json").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class);
}
The feign proxy used by the client, I'd like to configure the oauth2-server/user/like/*** path to be accessible by anonymous users.
#RestController
#FeignClient(name = "oauth2-server", url = "https://localhost:****")
public interface ProxyOauth2Server {
#GetMapping(value = "oauth2-server/user/like/{name}")
ResponseEntity<?> getUserLikeName(#PathVariable("name") String name);
}
To transmit the token through Feign I have this configuration in the client Main class.
#EnableConfigurationProperties
#SpringBootApplication
#EnableFeignClients("com.tutosharing.client.proxies")
public class ClientUiApplication {
#Autowired
private SecurityPropertiesConfig config;
#Bean
protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri(config.getAccessTokenUri());
resource.setUserAuthorizationUri(config.getUserAuthorizationUri());
resource.setClientId(config.getClientId());
resource.setClientSecret(config.getClientSecret());
return resource;
}
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails resource) {
return new OAuth2FeignRequestInterceptor(oauth2ClientContext, resource);
}
}
Now the oauth2 server which also serves as a resource server
#SpringBootApplication
#EnableResourceServer
#EnableAuthorizationServer
#EnableConfigurationProperties
public class AuthorizationServerApplication {}
the oauth2 server WebSecurityConfigurerAdapter class
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/",
"/login",
"/login.do",
"/oauth/authorize**")
.and()
.authorizeRequests()
.antMatchers(
"/",
"/login",
"/login.do")
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login.do")
.usernameParameter("*********")
.passwordParameter("*********")
.and()
.userDetailsService(userDetailsServiceBean())
.requiresChannel()
.anyRequest()
.requiresSecure();
}
}
The Rest controler method I'd like to allow to anonymous users
#RestController
public class UserRControllerRest {
#GetMapping({"/user/like/{name}"})
#JsonView(View.SimpleUser.class)
#PreAuthorize("hasRole('ROLE_USER')")
public ResponseEntity getUserLikeName(#PathVariable String name) {
Set<AuthUser> users = this.userRepository.findByNameLike(name);
return new ResponseEntity(users, HttpStatus.OK);
}
}
If I configure the Rest method with #PreAuthorize("hasRole('ROLE_ANONYMOUS')")
and the WebSecurityConfigurerAdapter like this
http.requestMatchers()
.antMatchers(
...
"/user/like/**",
...)
.and()
.authorizeRequests()
.antMatchers("/user/like/**")
.anonymous()
...
}
} // #formatter:on
I'm able to get an answer if I contact directly the oauth2-server with Postman, but not if I pass through the client service that uses Feign, I'm always redirected to the login page.
So how can I allow anonymous request Through Feign ?
I've found a solution but I'm not sure this is the Best way. So if you have another solution you are welwome.
So far I used this configuration to get the Token from the oauth2-server anytime an user made a request from the client through Feign.
#Bean
protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri(config.getAccessTokenUri());
resource.setUserAuthorizationUri(config.getUserAuthorizationUri());
resource.setClientId(config.getClientId());
resource.setClientSecret(config.getClientSecret());
return resource;
}
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor(#Qualifier("oauth2ClientContext") OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails resource) {
return new OAuth2FeignRequestInterceptor(oauth2ClientContext, resource);
}
The problem with that configuration is that anytime I made a request with Feign a request is sent to the oauth2-client to the /oauth/authorize endpoint. But if the user is not connected it fails, so an unauthenticated user cannot make any request from the client service.
So I used another RequestInterceptor.
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return requestTemplate -> {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (!principal.equals("anonymousUser")) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
SecurityContextHolder.getContext().getAuthentication().getDetails();
requestTemplate.header("Authorization", "bearer " + details.getTokenValue());
}
};
}
This way the token that the client service already has, once the user is connected, is added to the request whitout making another request to the /oauth/authorize endpoint. I think the token is sent with every request, I don't think it's a good practice for security matters.
Also in the WebSecurityConfigurerAdapter classes of the client-server I need to add the path so that it is accessible to non-connected users
http.antMatcher("/**")
.authorizeRequests()
.antMatchers(
"/oauth2-server/user/like/**",
...)
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
...;
same for the oauth2-server
http.antMatcher("/**")
.authorizeRequests()
.antMatchers(
"/user/like/**",
...)
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
...;
With that configuration an unauthenticated user can make a request to an unprotected endpoint.

Ignore filters executing on requests made to specific endpoints

I have the following config method for my WebSecurityConfig that extends WebSecurityConfigurerAdapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.and()
.authorizeRequests()
.antMatchers("/signup").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(
new JWTAuthenticationFilter(userDetailsServiceBean()),
UsernamePasswordAuthenticationFilter.class);
}
JWTAuthenticationFilter actually filters requests received on all endpoints, and checks if they have the correct JWT authentication token in their headers.
I don't expect this filter to execute on requests made to the /login endpoint ! Is there a way to ignore the filter for special endpoints ? (here /login and /signup).
Is there any good reason to keep this filter executing on requests made to all endpoints including login and signup ?
You can try adding the following
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/login**", "/signup**");
}

Authenticate only selected rest end points : spring boot

I have a Spring Boot web application exposing few rest endpoints. I wanted to know how we can enable basic authentication only for selected rest endpoints. Let's say I want only /employee/{id} request to be authenticated and ignore all the other rest endpoints. I am using the following code. My question is will the antMatcher only authenticate the request specified? Currently its enabling authentication for all rest endpoints:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// How does it work will it only authenticate employee &
// ignore any other request?? Its authenticating all the requests currently.
http
.authorizeRequests()
.antMatchers("/employee/*").authenticated()
.and()
.httpBasic()
.and()
.csrf()
.disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");
}
}
By default Spring Boot will secure all endpoints when Spring Security is on the classpath.
You need to explicitly add an exclusion for all other endpoints to be permitted without authentication.
Example:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/employee/*").authenticated()
.anyRequest().permitAll()
.and()
.httpBasic()
.and()
.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");
}
}

Categories