Implementing Service Authorization and Internal Authentication with Custom Annotations and Spring AOP
Spring AOP (Aspect-Oriented Programming) enables the separation of cross-cuting concerns like security from core bussiness logic. By defiinng aspects, join points, pointcuts, and advices, we can add authorization and authentication capabilities to service interfaces without altering the original code.
Defining Custom Annotations
Create two custom annotations: @RequireAuthorization for methods or classes needing authorization, and @InternalOnly for those requiring internal service authantication.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireAuthorization {
String accessLevel() default "STANDARD_USER";
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface InternalOnly {
// Additional properties can be added as needed
}
Creating the AOP Aspect
Construct an AOP aspect to perform authorization and authentication checks before method execution.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SecurityAspect {
@Pointcut("@annotation(RequireAuthorization) || @annotation(InternalOnly)")
public void securityCheckPointcut() {}
@Before("securityCheckPointcut()")
public void performSecurityValidation(JoinPoint joinPoint) throws Throwable {
// Check for @RequireAuthorization annotation
if (hasAuthorizationAnnotation(joinPoint)) {
RequireAuthorization authAnnotation = extractAuthAnnotation(joinPoint);
// Execute authorization logic, e.g., verify user role
System.out.println("Validating access for level: " + authAnnotation.accessLevel());
// Throw an exception or return error on authorization failure
}
// Check for @InternalOnly annotation
if (hasInternalAnnotation(joinPoint)) {
// Execute internal service authentication logic
System.out.println("Performing internal service validation");
// Handle authentication failure appropriately
}
}
private boolean hasAuthorizationAnnotation(JoinPoint joinPoint) {
return joinPoint.getTarget().getClass().isAnnotationPresent(RequireAuthorization.class) ||
joinPoint.getSignature().getAnnotation(RequireAuthorization.class) != null;
}
private boolean hasInternalAnnotation(JoinPoint joinPoint) {
return joinPoint.getTarget().getClass().isAnnotationPresent(InternalOnly.class) ||
joinPoint.getSignature().getAnnotation(InternalOnly.class) != null;
}
private RequireAuthorization extractAuthAnnotation(JoinPoint joinPoint) {
if (joinPoint.getTarget().getClass().isAnnotationPresent(RequireAuthorization.class)) {
return joinPoint.getTarget().getClass().getAnnotation(RequireAuthorization.class);
} else {
return joinPoint.getSignature().getAnnotation(RequireAuthorization.class);
}
}
}
Applying Annotations to Service Interfaces
Apply the @RequireAuthorization and @InternalOnly annotations to service classes or specific methods.
@Service
public class BusinessService {
@RequireAuthorization(accessLevel = "SYSTEM_ADMIN")
public void restrictedAdminOperation() {
// Method accessible only to SYSTEM_ADMIN role
}
@InternalOnly
public void internalServiceCall() {
// Method intended for internal service access only
}
// Other methods without security annotations
}
Configuring Spring for AOP Suppport
Ensure your Spring Boot or Spring application has AOP support enabled. In a Spring Boot project, this typically requires adding the spring-boot-starter-aop dependency.
<!-- Maven Dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>