Backend Development 10 min read

Implementing Flexible Permission Control in Spring Boot Using Custom Annotations and SpEL

This article demonstrates how to create a customizable permission‑checking mechanism in Spring Boot by defining a @PreAuth annotation, an AOP aspect, and leveraging Spring Expression Language (SpEL) to express complex access rules such as role, time, and permission constraints.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Implementing Flexible Permission Control in Spring Boot Using Custom Annotations and SpEL

Hello everyone, I'm Chen.

In Spring Boot, many developers use a custom annotation combined with an aspect to control interface permissions, but the basic approach quickly becomes insufficient for real‑world scenarios that require various conditions such as role‑based access, permission checks, time windows, or super‑admin restrictions.

SpEL Expression

SpEL (Spring Expression Language) allows runtime evaluation of expressions to inject values into beans or method parameters, making it ideal for dynamic permission logic.

Getting Started

Custom Annotation

Define a simple annotation that only contains a value attribute to hold the SpEL expression.

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuth {
    /**
     * permissionAll() – any configured role can access
     * hasPermission("MENU.QUERY") – requires MENU.QUERY permission
     * permitAll() – allow all requests
     * denyAll() – only super‑admin can access
     * hasAuth() – only logged‑in users can access
     * hasTimeAuth(1,10) – only between 1‑10 o'clock
     * hasRole('Admin') – requires Admin role
     * hasAllRole('Admin','Chief Engineer') – requires both roles
     */
    String value();
}

Define the Aspect

The aspect intercepts methods or classes annotated with @PreAuth. It uses @within to apply class‑level annotations to all methods within the class.

@Around("@annotation(PreAuth) || @within(PreAuth)")
public Object preAuth(ProceedingJoinPoint point) throws Throwable {
    if (handleAuth(point)) {
        return point.proceed();
    }
    throw new SecureException(ResultCode.REQ_REJECT);
}

private boolean handleAuth(ProceedingJoinPoint point) {
    // TODO: implement actual permission logic, return true or false
}

Permission Validation with SpEL

Introduce SpEL parser and retrieve the expression from the annotation.

Inject SpEL Parser

private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

Obtain Expression from Annotation

MethodSignature ms = point.getSignature() instanceof MethodSignature ? (MethodSignature) point.getSignature() : null;
Method method = ms.getMethod();
PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
String condition = preAuth.value();
if (StringUtil.isNotBlank(condition)) {
    // TODO: parse expression
}

Parse and Evaluate Expression

private boolean handleAuth(ProceedingJoinPoint point) {
    MethodSignature ms = point.getSignature() instanceof MethodSignature ? (MethodSignature) point.getSignature() : null;
    Method method = ms.getMethod();
    PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
    String condition = preAuth.value();
    if (StringUtil.isNotBlank(condition)) {
        Expression expression = EXPRESSION_PARSER.parseExpression(condition);
        Object[] args = point.getArgs();
        StandardEvaluationContext context = getEvaluationContext(method, args);
        return expression.getValue(context, Boolean.class);
    }
    return false;
}

/**
 * Build evaluation context with method parameters and an AuthFun helper object.
 */
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
    StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());
    context.setBeanResolver(new BeanFactoryResolver(applicationContext));
    for (int i = 0; i < args.length; i++) {
        MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
        context.setVariable(methodParam.getParameterName(), args[i]);
    }
    return context;
}

Custom Helper Class (AuthFun)

AuthFun implements the actual permission checks that can be referenced inside SpEL expressions.

public class AuthFun {
    /** Check if any role can access */
    public boolean permissionAll() { /* TODO */ }

    /** Check specific permission */
    public boolean hasPermission(String permission) { /* TODO */ }

    /** Allow all requests */
    public boolean permitAll() { return true; }

    /** Only super‑admin */
    public boolean denyAll() { return hasRole(RoleConstant.ADMIN); }

    /** Logged‑in user */
    public boolean hasAuth() {
        if (Func.isEmpty(AuthUtil.getUser())) {
            // TODO: throw exception
        } else {
            return true;
        }
    }

    /** Time‑based access */
    public boolean hasTimeAuth(Integer start, Integer end) {
        int hour = DateUtil.hour();
        return hour >= start && hour <= end;
    }

    /** Role check */
    public boolean hasRole(String role) { return hasAnyRole(role); }

    public boolean hasAllRole(String... role) {
        for (String r : role) {
            if (!hasRole(r)) return false;
        }
        return true;
    }

    public boolean hasAnyRole(String... role) {
        BladeUser user = AuthUtil.getUser();
        if (user == null) return false;
        String userRole = user.getRoleName();
        if (StringUtil.isBlank(userRole)) return false;
        String[] roles = Func.toStrArray(userRole);
        for (String r : role) {
            if (CollectionUtil.contains(roles, r)) return true;
        }
        return false;
    }
}

Practical Usage

Apply @PreAuth on a class or method and write the SpEL expression using the helper methods, e.g.:

@PreAuth("hasAllRole('Admin','Chief Engineer')")
public T someApi(...) { ... }

Parameters must be enclosed in single quotes.

Underlying Principle

During runtime, the SpEL engine parses the string inside @PreAuth, resolves it to a method call on the AuthFun instance, and evaluates the result to decide whether to allow the request.

Conclusion

Using SpEL makes permission configuration highly flexible; new scenarios only require adding corresponding methods to AuthFun, avoiding repetitive AOP code.

Final Note

If this tutorial helped you, please like, share, or follow the author.

JavaAOPSPELpermissionSpring BootCustom Annotation
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.