如何评估自定义 Java 代码中的 SpEL 安全表达式?

Ale*_*min 5 spring spring-security spring-el

我开始需要发明一种新型的注释,其中一个字段是 Spring 表达式语言(又名 SpEL)表达式字符串。

经过一番谷歌搜索和检查现有类后,我发现评估表达式的方式可能是这样的(如果我有任何错误,请纠正我):

    ExpressionParser parser = new SpelExpressionParser();
    Expression exp = parser.parseExpression("isAnonymous()"); // well, this is only an example
    SecurityExpressionRoot context = ... obtaining the instance of subclass of SecurityExpressionRoot ...
    System.out.println(exp.getValue(context)); // just an example
Run Code Online (Sandbox Code Playgroud)

但问题是:最适合我的情况的 MethodSecurityExpressionRoot 是包本地的。甚至有一项在 Spring Security JIRA 中公开任务,一年来没有得到开发人员的任何关注。

而且,即使它不是包的地方,我还有哪里获得方法的对象弱的理解setTrustResolversetRoleHierarchysetPermissionEvaluatorSecurityExpressionRoot类,它似乎有需要为它的正常运作。

所以,我的问题是:如何正确获取正确的SecurityExpressionRoot-subclass 实例以及如何使用所需的对象填充它?

Vác*_*žel 1

我正在解决同样的问题。我有一个菜单项列表。每个菜单项都包含一个安全表达式字符串 (SpEl)。我尝试使用 @PostFilter("filterObject.securityExpression") 但我不知道如何评估 SpEl 字符串内的 SpEl 字符串。

所以我最终得到了自定义评估器 bean。深受 org.thymeleaf.extras.springsecurity4.auth.AuthUtils 的启发

评估器使用与 Web 安全过滤器相同的 SecurityExpressionHandler。这意味着有必要为评估上下文提供请求和响应。但这应该不会很复杂,因为 Spring 将这些值注入到控制器方法中。

评估人:

@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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

作为控制器方法的用法:

@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());
}
Run Code Online (Sandbox Code Playgroud)