Java 用于spring boot中多个方法的MethodSecurityInterceptor

Java 用于spring boot中多个方法的MethodSecurityInterceptor,java,spring,spring-security,spring-boot,Java,Spring,Spring Security,Spring Boot,这与Mickeel Marrache关于如何使用不同方法的授权逻辑保护服务的回答有关 我更喜欢Maciej Ziarko使用而不是接受的响应,因为它使用相同的注释@Secured和不同的自定义参数 因为我使用的是没有XML配置的SpringBoot,所以我花了一段时间才弄明白如何做 这是我的答案 它只是解释了如何用Java配置替换xml配置。(在我的更改之后,我将添加原始答案“以防万一”。) 要替换xml配置,请执行以下操作: <sec:global-method-security sec

这与Mickeel Marrache关于如何使用不同方法的授权逻辑保护服务的回答有关

我更喜欢Maciej Ziarko使用而不是接受的响应,因为它使用相同的注释
@Secured
和不同的自定义参数

因为我使用的是没有XML配置的SpringBoot,所以我花了一段时间才弄明白如何做

这是我的答案

它只是解释了如何用Java配置替换xml配置。

(在我的更改之后,我将添加原始答案“以防万一”。)

要替换xml配置,请执行以下操作:

<sec:global-method-security secured-annotations="enabled"
                            access-decision-manager-ref="methodSecurityAccessDecisionManager">
</sec:global-method-security>
这两项工作都可以使用Java配置完成,如下所示:

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;


@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    public AccessDecisionManager accessDecisionManager() {

        logger.debug("accessDecisionManager config...");

        Map<String, AccessDecisionStrategy> strategyMap = new HashMap<String, AccessDecisionStrategy>();

        strategyMap.put("GetByOwner", new GetByOwnerStrategy());

        return new MethodSecurityAccessDecisionManager(strategyMap);
    }

}
仅为了完整性,当用户不应继续时,已实施的策略必须返回
AccessDeniedException
InsufficientAuthenticationException
。以下是访问参数和所有参数的示例:

public class GetByOwnerStrategy implements AccessDecisionStrategy {
    @Override
    public void decide(Authentication authentication,
            MethodInvocation methodInvocation, ConfigAttribute configAttribute) {

        MethodInvocationExtractor<Object> extractor = new MethodInvocationExtractor<>(methodInvocation);
        Person person = (Person) extractor.getArg(0);
        String userId = (String) extractor.getArg(1);

        String username = authentication.getName();

        if (! ((userId.equals(username)) && (person.getSomeData().equals("SOMETHING") ) && ....) {
            throw new AccessDeniedException("Not enough privileges");
        }
    }
}
每个访问决策策略代表了不同的访问决策方式

您可以轻松地实施自己的策略(即使使用其他语言,例如Scala):

公共类SomeStrategy实现AccessDecisionStrategy{

如您所见,我的AccessDecisionManager有一个策略图。manager使用的策略基于注释参数

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);
    }
}
您可以实施和配置任意数量的策略:

<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();
        }
    }
}
import org.aopalliance.intercept.MethodInvocation;
公共类方法调用提取器{
私有方法调用方法调用;
公共MethodInvocation提取器(MethodInvocation MethodInvocation){
this.methodInvocation=methodInvocation;
}
公共参数类型getArg(int num){
试一试{
Object[]arguments=methodInvocation.getArguments();
返回(ArgumentType)参数[num];
}catch(ClassCastException | ArrayIndexOutOfBoundsException e){
抛出新的非法状态异常();
}
}
}
现在,您可以轻松地在策略代码中提取有趣的参数来做出决策:

假设我想得到Long类型的参数0:

MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation);
Long id = extractor.getArg(0);
MethodInvocationExtractor=newmethodinvocationextractor(methodInvocation);
Long id=extractor.getArg(0);
12年11月14日14:40回答
马切吉·齐亚科

<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>
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();
        }
    }
}
MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation);
Long id = extractor.getArg(0);