Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Secure MVC Controller methods with @Secured annotation

Here's my single security config for an API made with SpringBoot:

@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private TokenAuthenticationService tokenAuthenticationService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/static/**").permitAll()
                .antMatchers(HttpMethod.POST, "/api/user/registration").permitAll()
                .antMatchers(HttpMethod.POST, "/api/user/authentication").permitAll()
                .anyRequest().authenticated().and()
            .addFilterBefore(new TokenLoginFilter("/api/user/authentication", authenticationManagerBean(), tokenAuthenticationService), 
                    UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(new TokenAuthenticationFilter(tokenAuthenticationService),UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
    }

}

All URLs are mapped to spring web mvc controllers and I would like to manually specify access levels for controllers and their methods like this:

@RestController
@RequestMapping("/api/resource/")
@Secured("ROLE_ANONYMOUS") //as default role
public class ResourceController {
    ...

    @Secured("ROLE_USER")
    some_method...
}

But when I perform an /api/resource/* requests as anonymous user the application responses with 403 status code, but I expect the method call. It looks like @Secured annotation does no effect on authorization, and all controller methods allowed for ROLE_USER only.

The TokenAuthenticationFilter does action only if token present, so I guess it has no effect.

    @Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest httpServletRequest = (HttpServletRequest) req;
    String token = httpServletRequest.getHeader(TokenAuthenticationService.AUTH_HEADER_NAME);
    if (token != null) {
        try {
            SecurityContextHolder.getContext()
                    .setAuthentication(tokenAuthenticationService.verifyToken(new TokenAuthentication(token)));
        } catch (AuthenticationException e) {
            ((HttpServletResponse) res).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
    }
    chain.doFilter(req, res);
}

Update:

Considering comments below this question I realized that @Secured annotation is the part of global method security concept, and not in general part of web security. Now I have folowing tradeoff:

  1. Use @Secured annotation and have method access level info spread over Controller classes, which may lead to situation of diffucult determining method access level in fufture with API grown.

  2. Keep all method access info in the same place(config), but have to support the equality of @RequestMapping value with urls in config.

Which one would you consider the best approach, or tell please if I missed something?

like image 559
Aeteros Avatar asked Sep 27 '22 10:09

Aeteros


1 Answers

In order to tell Spring to watch for @Secured annotation, on your Security Config you must add the following:

@EnableGlobalMethodSecurity(securedEnabled = true)
like image 200
Meriton Husaj Avatar answered Oct 19 '22 08:10

Meriton Husaj