如何创建用于Spring安全性表达语言注释的自定义方法


92

我想创建一个类,该类添加用于Spring安全表达语言的自定义方法,以通过注释进行基于方法的授权。

例如,我想创建一个自定义方法,例如“ customMethodReturningBoolean”,以这种方式使用:

  @PreAuthorize("customMethodReturningBoolean()")
  public void myMethodToSecure() { 
    // whatever
  }

我的问题是这个。如果可能的话,我应该子类化什么类来创建我的自定义方法,我该如何在spring xml配置文件中对其进行配置,然后有人给我一个以这​​种方式使用的自定义方法的示例?


1
我现在没有时间输入答案,但是我遵循了该指南,并且效果很好:baeldung.com/… 我正在使用Spring Security 5.1.1。
保罗

Answers:


35

您需要子类化两个类。

首先,设置一个新的方法表达式处理程序

<global-method-security>
  <expression-handler ref="myMethodSecurityExpressionHandler"/>
</global-method-security>

myMethodSecurityExpressionHandler将是的一个子类DefaultMethodSecurityExpressionHandler,其覆盖createEvaluationContext(),设置的一个子类MethodSecurityExpressionRootMethodSecurityEvaluationContext

例如:

@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
    MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
    MethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(auth);
    root.setTrustResolver(trustResolver);
    root.setPermissionEvaluator(permissionEvaluator);
    root.setRoleHierarchy(roleHierarchy);
    ctx.setRootObject(root);

    return ctx;
}

嗯,听起来是个好主意,但是DefaultMethodSecurityExpressionHandler的所有属性都是私有的,没有访问器,因此我很好奇您如何扩展类而不进行任何丑陋的思考。谢谢。
约瑟夫·卢斯特

1
您是指trustResolver等吗?这些都制定者在DefaultMethodSecurityExpressionHandler(至少在Spring Security 3.0中)请参见:static.springsource.org/spring-security/site/apidocs/org/...
sourcedelica

3
@ericacm你如何获得周围MethodSecurityExpressionRoot包私有
C. Ross

175

所提到的技术都将不再起作用。看来Spring已经竭尽全力防止用户覆盖SecurityExpressionRoot。

编辑11/19/14设置Spring以使用安全注释:

<beans ... xmlns:sec="http://www.springframework.org/schema/security" ... >
...
<sec:global-method-security pre-post-annotations="enabled" />

创建一个像这样的bean:

@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(String key) {
        return true;
    }
}

然后在您的jsp中执行以下操作:

<sec:authorize access="@mySecurityService.hasPermission('special')">
    <input type="button" value="Special Button" />
</sec:authorize>

或注释方法:

@PreAuthorize("@mySecurityService.hasPermission('special')")
public void doSpecialStuff() { ... }

另外,您可以在批注中使用Spring Expression Language@PreAuthorize访问当前身份验证以及方法参数。

例如:

@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(Authentication authentication, String foo) { ... }
}

然后更新@PreAuthorize以匹配新的方法签名:

@PreAuthorize("@mySecurityService.hasPermission(authentication, #foo)")
public void doSpecialStuff(String foo) { ... }

6
您的hasPermission方法中的@Bosh可以Authentication auth = SecurityContextHolder.getContext().getAuthentication();用来获取当前的身份验证令牌。
詹姆斯·沃特金斯

2
谢谢詹姆斯的回答。我必须在spring配置文件中定义mySecurityService吗?
WowBow

2
如果您对该服务所在的软件包具有组件扫描设置,则无需在任何XML文件中定义mySecurityService。如果没有匹配的组件扫描,则必须使用xml bean定义。@PreAuthorize来自org.springframework.security
James Watkins

3
您可能需要像这样为注释指定bean的名称:@Component(“ mySecurityService”)或使用@Named注释。
詹姆斯·沃特金斯

1
@VJS请查看我所做的编辑。您将需要配置spring以使用这些注释。我很惊讶没有其他人抱怨这个重要的缺失细节:)
James Watkins 2014年

14

谢谢ericacm,但是由于以下几个原因它不起作用:

  • DefaultMethodSecurityExpressionHandler的属性是私有的(反射可见性不理想)
  • 至少在我的Eclipse中,我无法解析MethodSecurityEvaluationContext对象

区别在于,我们调用现有的createEvaluationContext方法,然后添加我们的自定义根对象。最后,我刚刚返回了StandardEvaluationContext对象类型,因为MethodSecurityEvaluationContext无法在编译器中解析(它们都来自同一接口)。这是我现在在生产中使用的代码。

使MethodSecurityExpressionHandler使用我们的自定义根目录:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler  {

    // parent constructor
    public CustomMethodSecurityExpressionHandler() {
        super();
    }

    /**
     * Custom override to use {@link CustomSecurityExpressionRoot}
     * 
     * Uses a {@link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation and
     * configures it with a {@link MethodSecurityExpressionRoot} instance as the expression root object.
     */
    @Override
    public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
        // due to private methods, call original method, then override it's root with ours
        StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
        ctx.setRootObject( new CustomSecurityExpressionRoot(auth) );
        return ctx;
    }
}

这通过扩展SecurityExpressionRoot替换了默认根。在这里,我将hasRole重命名为hasEntitlement:

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot  {

    // parent constructor
    public CustomSecurityExpressionRoot(Authentication a) {
        super(a);
    }

    /**
     * Pass through to hasRole preserving Entitlement method naming convention
     * @param expression
     * @return boolean
     */
    public boolean hasEntitlement(String expression) {
        return hasRole(expression);
    }

}

最后,更新securityContext.xml(并确保已从您的applcationContext.xml中引用了它):

<!-- setup method level security using annotations -->
<security:global-method-security
        jsr250-annotations="disabled"
        secured-annotations="disabled"
        pre-post-annotations="enabled">
    <security:expression-handler ref="expressionHandler"/>
</security:global-method-security>

<!--<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">-->
<bean id="expressionHandler" class="com.yourSite.security.CustomMethodSecurityExpressionHandler" />

注意: @Secured批注将通过其他验证处理程序运行,因此不会接受此替代。因此,在上面的xml中,我禁用了它们以防止以后出现混乱。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.