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.
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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.