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.
Related
I want to restrict usage of endpoints based on roles: admin/user.
So I'm trying to implement Spring Security using NoOpPasswordEncoder (for testing purpose),
but the problem is:
all endpoints return status 200 and unresponsive to constraints as antMatchers.
To clarify: I want to log in as user and get the error because of antMatcher:
.antMatchers("/api/addresses")
.hasAuthority("ROLE_ADMIN")
but I'm getting 200 using current configuration now.
I've tested Spring Security configuration in format:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager());
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
final Properties users = new Properties();
users.put("admin","{noop}admin,ROLE_ADMIN,enabled");
users.put("user","{noop}user,ROLE_USER,enabled");
return new InMemoryUserDetailsManager(users);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/addresses")
.access("hasAuthority('ROLE_ADMIN')")
.antMatchers("/api/address/**")
.access("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_USER')")
.anyRequest()
.authenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
based on example config.
While investigating, I've tried to comment, e.g. lines:
.antMatchers("/api/address/**")
.access("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_USER')")
to check what happens and still receive 200 when log in as user.
also I've tried to use hasAuthority() methods like:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager());
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
final Properties users = new Properties();
users.put("ADMIN","{noop}admin,ROLE_ADMIN,enabled");
users.put("USER","{noop}user,ROLE_USER,enabled");
return new InMemoryUserDetailsManager(users);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/addresses")
.hasAuthority("ROLE_ADMIN")
.antMatchers("/api/address/**")
.hasAnyAuthority("ROLE_ADMIN", "ROLE_USER")
.anyRequest()
.authenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
following the correct order with antMatchers(), when more specific rules have to go first, but it still doesn't help me.
Does anyone know what I'm missing here? Thanks in advance for any ideas.
UPD #1:
I've tried to clear cookies in Postman, log in as user, but I'm still getting 200.
I'm getting 401 only if I don't use Basic Auth in Postman for GET request:
UPD #2:
I've reproduced this issue using versions of technologies:
Java 11
spring-boot-starter-parent 2.5.3
spring-boot-starter-security 2.5.3
Cause & solutions:
The cause of issue was redundant configuration option:
server.servlet.context-path=/api
because /api prefix was already present in .antMatchers()
Solution #1:
To fix it I've removed it from application.properties file and add this prefix directly to endpoints.
Solution #2:
It can be solved vice versa: remove prefix /api in antMatchers() and leave:
server.servlet.context-path=/api
using application.properties
Solution #3:
Also I've solved the problem using another configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager());
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
final Properties users = new Properties();
users.put("admin","{noop}admin,ROLE_ADMIN,enabled");
users.put("user","{noop}user,ROLE_USER,enabled");
return new InMemoryUserDetailsManager(users);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest()
.permitAll()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
with adding annotations on methods of controller:
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
and accordingly:
#PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_USER')")
As result I'm getting 403 instead of 200:
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.
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");
}
}
I am trying to give role based authorization for resources. It works with out roles if I do it like
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/hello"),
new AntPathRequestMatcher("/user")
))
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.resourceId("openid");
}
}
If I use below method it won't work for test resources.
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.requestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/hello"),
new AntPathRequestMatcher("/user")
))
.authorizeRequests()
.antMatchers("/test").hasRole("ADMIN")
.anyRequest().access("#oauth2.hasScope('read')");
}
It completely ignores token based authorization. How can I implement this? Another issue I am getting is if I remove requestMatcher block, Oauth client can not get the authorization code, after submitting user credentials to login form it reloads login page again. But with the previous block of code it works fine. What I am doing wrong here?
Here is my security configuration class
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/img/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.defaultSuccessUrl("/hello")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout");
}
}
When you use roles in spring you have to use prefix ROLE (for example ROLE_ADMIN) to make it work with default settings.
I want to enable the use of "ROLE_ANONYMOUS" to allow anonymous access to some urls in my app. And I used the below configuration.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestCache()
.requestCache(new NullRequestCache()).and()
.anonymous().authorities("ROLE_ANONYMOUS").and()
.exceptionHandling().and()
.servletApi().and()
.headers().cacheControl().and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/profile/image").permitAll()
.antMatchers("/favicon.ico").permitAll()
.antMatchers("/resources/**").permitAll()
//.antMatchers(HttpMethod.GET, "/login/**").permitAll()
//.antMatchers(HttpMethod.GET, "/location/**").permitAll()
.anyRequest().authenticated()/*.and()
.apply(new SpringSocialConfigurer())*/;
// custom Token based authentication based on the header previously given to the client
//.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class);
}
My controller looks like:
#RestController
#RequestMapping(value="/login", produces="application/json")
public class LoginController {
#Secured( value={"ROLE_ANONYMOUS"})
#RequestMapping(method=RequestMethod.GET)
public String get(){
return "hello";
}
}
But when I try to hit "/login" I get 403 access denied error.
Please help me how I can enable annotation based anonymous access.
This should solve your issue.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.formLogin().loginPage("/login").permitAll()
...
But if you prefer not to use permitAll but to stick to anonymous roled user (it would be the same effect on both situation but yet if that's wht you prefer) then try this in the controller.
#Secured("ROLE_ANONYMOUS")
#RequestMapping(method=RequestMethod.GET)
public String get(){
...
As Faraj Farook wrote, you have to permit access to your login page URL. You commented the relevant line out:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.anonymous()
.authorities("ROLE_ANONYMOUS")
.and()
.headers()
.cacheControl()
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/profile/image").permitAll()
.antMatchers("/favicon.ico").permitAll()
.antMatchers("/resources/**").permitAll()
.antMatchers(HttpMethod.GET, "/login/**").permitAll()
.anyRequest().authenticated()
}
But if you prefer not to use permitAll() you could use hasAuthority("ROLE_ANONYMOUS"). In this case you don't need to annotate your method with
#Secured( value={"ROLE_ANONYMOUS"}).