且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在Spring Security @ PreAuthorize/@ PostAuthorize批注中使用自定义表达式

更新时间:2022-11-20 09:56:02

1)首先,您必须重新实现MethodSecurityExpressionRoot,其中包含额外的方法特定功能.最初的Spring Security实现是程序包私有的,因此不可能仅对其进行扩展.我建议检查给定类的源代码.

1) First you have to reimplement MethodSecurityExpressionRoot which contains extra method-specific functionality. The original Spring Security implementation is package private and hence it is not possible to just extend it. I suggest checking the source code for the given class.

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    // copy everything from the original Spring Security MethodSecurityExpressionRoot

    // add your custom methods

    public boolean isAdmin() {
        // do whatever you need to do, e.g. delegate to other components

        // hint: you can here directly access Authentication object 
        // via inherited authentication field
    }

    public boolean isOwner(Long id) {
        // do whatever you need to do, e.g. delegate to other components
    }
}

2)接下来,您必须实现将使用上面定义的CustomMethodSecurityExpressionRoot的自定义MethodSecurityExpressionHandler.

2) Next you have to implement custom MethodSecurityExpressionHandler that will use the above defined CustomMethodSecurityExpressionRoot.

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

    @Override
    public void setReturnObject(Object returnObject, EvaluationContext ctx) {
        ((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
    }

    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
        MethodInvocation invocation) {
        final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
        root.setThis(invocation.getThis());
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(this.trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());

        return root;
    }
}

3)在您的上下文中定义表达式处理程序bean,例如通过XML,您可以执行以下操作

3) Define expression handler bean in your context, e.g. via XML you can do it as follows

<bean id="methodSecurityExpressionHandler"
    class="my.package.CustomMethodSecurityExpressionHandler">
    <property name="roleHierarchy" ref="roleHierarchy" />
    <property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>

4)注册上面定义的处理程序

4) Register the above defined handler

<security:global-method-security pre-post-annotations="enabled">
    <security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>

5)然后只需在@PreAuthorize和/或@PostAuthorize批注中使用定义的表达式

5) Then just use the defined expressions in your @PreAuthorize and/or @PostAuthorize annotations

@PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(@PathVariable int id, @ModelAttribute currentGame) {
    // do whatever needed
}

还有一件事.使用方法级别的安全性来保护控制器方法的安全性不是很常见,而是使用业务逻辑来保护方法(也就是您的服务层方法).然后,您可以使用类似下面的内容.

And one more thing. It is not very common to use method level security to secure controller methods but rather to secure methods with business logic (a.k.a. your service layer methods). Then you could use something like the below.

public interface GameService {

    // rest omitted

    @PreAuthorize("principal.admin or #game.owner = principal.username")
    public void delete(@P("game") Game game);
}

但是请记住,这只是一个例子.它希望实际的主体具有isAdmin()方法,并且游戏具有getOwner()方法,返回所有者的用户名.

But keep in mind that this is just an example. It expects that the actual principal has isAdmin() method and that the game has getOwner() method returning username of the owner.