MethodSecurityInterceptor用于多个方法

时间:2012-11-14 09:35:30

标签: java spring-security

我想使用Spring Security保护我的服务层。如文档中所述,我需要使用MethodSecurityInterceptor来检查是否允许方法调用。

要确定是否允许给定用户进行服务方法调用,影响调用方法所需的角色(使用MethodSecurityMetadataSource)对我来说是不够的,因为它还取决于传递给方法的参数。正如文档中所建议的,我可以编写自定义AccessDecisionVoter并通过安全对象访问参数(在本例中为MethodInvocation)。

但是,我的授权逻辑在各种方法中都有所不同。例如,多个方法之间的参数可能不同,授权逻辑也不同。

我看到两个选项:

  • 我可以在AccessDecisionVoter中使用条件逻辑来确定要使用的调用方法和授权逻辑,但这似乎是一个难看的解决方案。
  • 我可以为每种方法定义一个MethodSecurityInterceptor来保护。根据Spring文档,MethodSecurityInterceptor用于保护许多方法,因此它让我觉得还有另一种方法。

在方法调用(使用AfterInvocationProvider)之后存在相同的访问决策问题。

有哪些替代方案?

2 个答案:

答案 0 :(得分:4)

我通过实现自己的AccessDecisionManager来实现这一目标,该AccessDecisionStrategy将访问决策委托给我的特殊界面public interface AccessDecisionStrategy { void decide(Authentication authentication, MethodInvocation methodInvocation, ConfigAttribute configAttribute); }

public class SomeStrategy implements AccessDecisionStrategy { ...

每个访问决策策略都代表了不同的访问决策方式。

您可以轻松实现自己的策略(即使是其他语言 - 例如Scala):

AccessDecisionManager

如您所见,我的public class MethodSecurityAccessDecisionManager implements AccessDecisionManager { private Map<String, AccessDecisionStrategy> strategyMap; public MethodSecurityAccessDecisionManager(Map<String, AccessDecisionStrategy> strategyMap) { this.strategyMap = strategyMap; } @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { ConfigAttribute configAttribute = getSingleConfigAttribute(configAttributes); AccessDecisionStrategy accessDecisionStrategy = strategyMap.get(configAttribute.getAttribute()); if (accessDecisionStrategy == null) { throw new IllegalStateException("AccessDecisionStrategy with name " + configAttribute.getAttribute() + " was not found!"); } try { accessDecisionStrategy.decide(authentication, (MethodInvocation) object, configAttribute); } catch (ClassCastException e) { throw new IllegalStateException(); } } private ConfigAttribute getSingleConfigAttribute(Collection<ConfigAttribute> configAttributes) { if (configAttributes == null || configAttributes.size() != 1) { throw new IllegalStateException("Invalid config attribute configuration"); } return configAttributes.iterator().next(); } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class<?> clazz) { return clazz.equals(MethodInvocation.class); } } 有一张策略图。经理使用的策略基于注释参数。

@Secured

现在,当我想要保护我的方法时,我将@Secured("GetByOwner") FlightSpotting getFlightSpotting(Long id); 注释与作为策略名称的参数放在一起:

<bean id="methodSecurityAccessDecisionManager"
      class="some.package.MethodSecurityAccessDecisionManager">

    <constructor-arg>
        <map>
            <entry key="GetByOwner">
                <bean class="some.package.GetByOwnerStrategy"/>
            </entry>

            <entry key="SomeOther">
                <bean class="some.package.SomeOtherStrategy"/>
            </entry>
        </map>
    </constructor-arg>

</bean>

您可以根据需要实施和配置多个策略:

<sec:global-method-security secured-annotations="enabled"
                            access-decision-manager-ref="methodSecurityAccessDecisionManager">
</sec:global-method-security>

要注入您输入的访问决策管理器:

MethodInvocation

我还实现了帮助类来处理import org.aopalliance.intercept.MethodInvocation; public class MethodInvocationExtractor<ArgumentType> { private MethodInvocation methodInvocation; public MethodInvocationExtractor(MethodInvocation methodInvocation) { this.methodInvocation = methodInvocation; } public ArgumentType getArg(int num) { try { Object[] arguments = methodInvocation.getArguments(); return (ArgumentType) arguments[num]; } catch (ClassCastException | ArrayIndexOutOfBoundsException e) { throw new IllegalStateException(); } } } 个参数:

0

现在,您可以轻松地在策略代码中提取有趣的参数来做出决定:

我想说我希望获得Long类型的参数号MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation); Long id = extractor.getArg(0);

{{1}}

答案 1 :(得分:3)

您可以基于Spring @PreAuthorize("")构造实现自己的方法安全注释。

要获取有关方法的额外信息(超出方法参数值)到SpEL评估上下文,您可以实现自己的MethodSecurityExpressionHandler

@Service
public class MySecurityExpressionHandler extends
    DefaultMethodSecurityExpressionHandler {

    @Override
    public StandardEvaluationContext createEvaluationContextInternal(
        Authentication auth, MethodInvocation mi) {

    StandardEvaluationContext evaluationContext = super
            .createEvaluationContextInternal(auth, mi);

    SomeMethodInfoData methodInfoData = mi.getMethod(). ...;

    evaluationContext.setVariable("someData", <value computed based on method info data>);
    }

    return evaluationContext;
} 

并在global-method-security声明

中注册
<security:global-method-security
        pre-post-annotations="enabled">
        <security:expression-handler
            ref="mySecurityExpressionHandler" />
    </security:global-method-security>

现在您可以创建自定义安全注释(如果需要,还可以在MySecurityExpressionHandler中添加额外的流程注释数据)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("#<someData>")
public @interface CustomSecurityAnnotation { ... }
例如,您可以创建自定义注释来检查用户角色,而不会弄乱字符串:

@MyUserRoleCheck(MyAppRole.Admin)
public void someMethod()