Backend Development 11 min read

Using Spring Expression Language (SpEL) for Flexible Permission Control in Spring Boot

This article demonstrates how to integrate Spring Expression Language (SpEL) with custom annotations and AOP in Spring Boot to create a highly flexible, maintainable permission‑checking mechanism that can handle various complex access scenarios without scattering hard‑coded logic throughout the codebase.

Top Architect
Top Architect
Top Architect
Using Spring Expression Language (SpEL) for Flexible Permission Control in Spring Boot

Introduction

In Spring Boot, developers often use custom annotations together with AOP to control interface permissions, but real‑world scenarios (role‑based access, time‑based limits, multiple role requirements, etc.) quickly make simple annotation checks unwieldy.

SpEL Expression

SpEL (Spring Expression Language) is a powerful expression language introduced in Spring 3.0 that can evaluate expressions at runtime and inject values into beans or method parameters.

Implementation

Custom Annotation

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuth {
    /**
     * permissionAll() – any configured role can access
     * hasPermission("MENU.QUERY") – requires specific permission
     * permitAll() – allow all requests
     * denyAll() – only super‑admin can access
     * hasAuth() – must be logged in
     * hasTimeAuth(1,10) – only between 1‑10 o'clock
     * hasRole('admin') – specific role required
     * hasAllRole('admin','chief') – must have all listed roles
     * hasAnyRole('admin','chief') – any one of the roles
     */
    String value();
}

Define Aspect

@Around("@annotation(PreAuthPath) || @within(PreAuthPath)")
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 logic, return true or false
}

Permission Check – Introduce SpEL

private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

Get 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 Expression

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;

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 Auth Functions (AuthFun)

public class AuthFun {
    public boolean permissionAll() { /* TODO */ }
    public boolean hasPermission(String permission) { /* TODO */ }
    public boolean permitAll() { return true; }
    public boolean denyAll() { return hasRole(RoleConstant.ADMIN); }
    public boolean hasAuth() { return Func.isEmpty(AuthUtil.getUser()) ? false : true; }
    public boolean hasTimeAuth(Integer start, Integer end) { return DateUtil.hour() >= start && DateUtil.hour() <= end; }
    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[] roles = Func.toStrArray(user.getRoleName());
        for (String r : role) { if (CollectionUtil.contains(roles, r)) return true; }
        return false;
    }
}

Actual Usage

Place @PreAuth("hasAllRole('Admin','Chief')") on a class or method; the SpEL parser will evaluate the expression at runtime and invoke the corresponding method in AuthFun .

Conclusion

By leveraging SpEL, permission configuration becomes highly flexible; new scenarios only require adding a method to AuthFun , making the solution far more maintainable than traditional hard‑coded checks.

JavaSPELpermissionSpring BootCustom AnnotationAspect
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.