5

我开始需要发明一种新型注释,其中一个字段是 Spring Expression Language (aka 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

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

即使它不是包本地的,我仍然对在哪里获取方法和类的对象的理解很弱setTrustResolver,这setRoleHierarchy似乎是它正常运行所必需的。setPermissionEvaluatorSecurityExpressionRoot

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

4

2 回答 2

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()));
    }
}

用作控制器方法:

@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());
}
于 2017-02-23T13:31:32.913 回答
0

我设法在没有任何新注释的情况下实现了这一点。您需要做的第一件事是将菜单项包装在 sec:authorize 标记中,其中 sec 命名空间来自 spring security taglibs。我们用:

<sec:authorize access="hasRole('${menuItem.permission}')"></sec:authorzie>

where${menuItem.permission}permission当前menuItem对象的字段(我们正在循环通过我们从服务器检索到的 menuItems)。SpEl在类hasRole()中由spring实现。org.springframework.security.access.expression.SecurityExpressionOperations

但这不会给你安全感,它只会让 gui 变得更好。服务器还需要通过以下方式保护:

@PreAuthorize("hasRole('...')")

@PreAuthorize注释也来自 spring security,它会阻止客户端在您的服务器上执行方法,除非用户具有给定的角色。为了完成这项工作,我们必须实施org.springframework.security.cas.userdetails.AbstractCasAssertionUserDetailsService. 大多数身份管理服务器都存在类似的类。我们也必须实现org.jasig.services.persondir.support.ldap.LdapPersonAttributeDao,但我们也在使用 ldap。YMMV。

于 2013-06-23T22:28:42.147 回答