I solve the same problem. I have a list of menu items. Each menu item contains a security expression string (SpEl). I tried using @PostFilter ("filterObject.securityExpression"), but I could not figure out how to evaluate the SpEl line inside the SpEl line.
So, I got a custom bean appraiser. Strongly inspired by org.thymeleaf.extras.springsecurity4.auth.AuthUtils
The evaluator uses the same SecurityExpressionHandler as the web security filters. This means the need to provide a request and response for the evaluation context. But this does not have to be complicated, as Spring introduces these values into controller methods.
appraiser:
@Component public class WebSecurityExpressionEvaluator { private static final FilterChain EMPTY_CHAIN = (request, response) -> { throw new UnsupportedOperationException(); }; private final List<SecurityExpressionHandler> securityExpressionHandlers; public WebSecurityExpressionEvaluator(List<SecurityExpressionHandler> securityExpressionHandlers) { this.securityExpressionHandlers = securityExpressionHandlers; } public boolean evaluate(String securityExpression, HttpServletRequest request, HttpServletResponse response) { SecurityExpressionHandler handler = getFilterSecurityHandler(); Expression expression = handler.getExpressionParser().parseExpression(securityExpression); EvaluationContext evaluationContext = createEvaluationContext(handler, request, response); return ExpressionUtils.evaluateAsBoolean(expression, evaluationContext); } @SuppressWarnings("unchecked") private EvaluationContext createEvaluationContext(SecurityExpressionHandler handler, HttpServletRequest request, HttpServletResponse response) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); FilterInvocation filterInvocation = new FilterInvocation(request, response, EMPTY_CHAIN); return handler.createEvaluationContext(authentication, filterInvocation); } private SecurityExpressionHandler getFilterSecurityHandler() { return securityExpressionHandlers.stream() .filter(handler -> FilterInvocation.class.equals(GenericTypeResolver.resolveTypeArgument(handler.getClass(), SecurityExpressionHandler.class))) .findAny() .orElseThrow(() -> new IllegalStateException("No filter invocation security expression handler has been found! Handlers: " + securityExpressionHandlers.size())); } }
Using as a controller method:
@ModelAttribute("adminMenuItems") public List<AdminMenuItem> getMenuItems(HttpServletRequest request, HttpServletResponse response) { List<AdminMenuItem> menuItems = ... return menuItems.stream().filter(item -> evaluator.evaluate(item.getSecurityExpression(), request, response)).collect(toList()); }
source share