1

we have a spring boot app with a java package that has spring controllers with endpoints for admin-like functionality. right now they all start with the same request mapping.

so one way i could do authorization of every endpoint in the package is by WebSecurityConfigurerAdapter implementation...

http.authorizeRequests()
      .antMatchers("/admin/**").access("hasRole('ADMIN')")

but i was thinking it would be nice to use AOP somehow to target the package of the admin controllers to PreAuthorize all controller methods in the package. just in case someone on the project creates a new controller in the proejct with a different request mapping it would automatically be projected. also, if we decided to PreAuthorize at the @Service level instead of the controller level then this way could be used as well. so, is it possible to PreAuthorize at the package level with AOP or some other way?

user2052618
  • 556
  • 2
  • 7
  • 20

1 Answers1

0

Spring provides default AOP interceptor for @Secured and @PreAuthorized annotations, but it works only on class or method level, and AFAIK not intended to be expanded to package level.
To put it simply, Spring intercepts calls to certain methods or all class methods with these annotations and check whether SecurityContextHolder holds Authority object and whether its collection of GrantedAuthority matches any of the annotation's value field values. So, you can do the same thing using AOP, for example like this:

@Aspect
@Component
public class AdminServiceAOPAuthorization {

    private static final List<String> ALLOWED_ROLES = List.of("ADMIN", "SUPER_ADMIN");

    @Pointcut("within(com.example.service.admin.*)") // <- any method in any class of the package
    public void adminServiceLayer() {}

    @Before("adminServiceLayer()")
    public void authorize(JoinPoint jp) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            // throw some exception depending on the logic
        }
        boolean authorized = authentication.getAuthorities().stream()
                .anyMatch(ga -> ALLOWED_ROLES.contains(ga.getAuthority()));
        if (!authorized) {
            throw new AccessDeniedException("Access denied");
            // log or whatever
        }
    }
}

For better performance I'd advise to use it at the service layer to let Spring create proxy using implemented interface (if you use them, of course) instead of proxying controller class.

Andrei Titov
  • 1,220
  • 2
  • 15
  • 1
    I suggest to use the double-dot notation to also include sub-package: `within(com.example.service.admin..*)`. Otherwise you only match the exact package `com.example.service.admin`. – kriegaex Sep 03 '22 at 11:22