1

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.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Denis Stephanov
  • 4,563
  • 24
  • 78
  • 174

2 Answers2

1

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();
}
Supun Wijerathne
  • 11,964
  • 10
  • 61
  • 87
  • Thank you for answer. My first try failed and I always got error. Next I tried configure `HttpSecurity` in `ResourceServerConfigurerAdapter` and it looks it works byt I am confused about that. Can you tell me what is difference between `configure(HttpSecurity http)` in `ResourceServerConfigurerAdapter` and `WebSecurityConfig` ? Which one should I used? – Denis Stephanov Feb 09 '20 at 11:43
  • @DenisStephanov you can find this answer which explains it clearly. It's about the precedence. https://stackoverflow.com/a/28604260/5715934 . So if you have the latter configured, it gets the higher precedence in filter chain. – Supun Wijerathne Feb 09 '20 at 12:03
  • So if I understand `ResourceServerConfigurerAdapter ` override `WebSecurityConfig ` configuration due to order and for securing endpoints can I use only resource server HttpSecurity ? – Denis Stephanov Feb 09 '20 at 12:07
  • @DenisStephanov It's not exactly an override. It's chaining. If the higher ordered filter passes something forward, then the lower level filter can access that and perform filtering on. But if the higher level guy doesn't (blocks), then the lower level guy will not get it. Your second point is correct, which means resource server is the best place, if you want to isolate the endpoint level authorization. – Supun Wijerathne Feb 09 '20 at 12:13
  • @SupunWijerathne Your first option will not work without you second option. You should make this clear in your answer. – dur Feb 10 '20 at 16:52
  • @dur I think the configuration he already had in his code is good enough. Isn't that so? – Supun Wijerathne Feb 10 '20 at 17:15
  • @SupunWijerathne No, in his question there is no `.antMatchers("/public").permitAll()`, which is neccessary. OP's `authorizeRequests().anyRequest().authenticated()`will fail and the `@PreAuthorize("permitAll()")` will never executed. However, we cannot see OP's resource server configuration, hence we don't know, if there is such a permitting. – dur Feb 10 '20 at 17:26
  • @dur, i get you. And will generify the answer :) . Thank You. – Supun Wijerathne Feb 11 '20 at 01:37
0

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-

kann
  • 687
  • 10
  • 22