Spring Boot 中多种方法的 MethodSecurityInterceptor [英] MethodSecurityInterceptor for multiple methods in spring boot

查看:30
本文介绍了Spring Boot 中多种方法的 MethodSecurityInterceptor的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This is related to the answer of this question by Mickael Marrache on how to secure services using an authorization logic that can be different across the methods.

我更喜欢 Maciej Ziarko 提供的响应,使用 Method Security AccessDecisionManager 而不是接受的响应,因为它使用相同的注释 @Secured 带有不同的自定义参数.

I liked better the response provided by Maciej Ziarko using Method Security AccessDecisionManager instead of the one accepted, since it uses the same annotation @Secured with diferente custom arguments.

由于我使用的是没有 XML 配置的 Spring-Boot,所以我花了一段时间才弄清楚如何去做.

Since I'm using Spring-Boot with no XML config, took me a while to figure out how to do it.

所以,这是我的答案.

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

It just explains how to replace the xml config with the Java Config configuration.

推荐答案

(在我修改之后,我会添加原始答案以防万一".)

(After my changes, I'll add the original answer "just in case".)

为了替换xml配置:

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

碰巧注释 @EnableGlobalMethodSecurity 没有办法在那里指定 accessDecisionManager.您必须扩展 GlobalMethodSecurityConfiguration 并覆盖 AccessDecisionManager 方法.

It happend that the annotation @EnableGlobalMethodSecurity doesn't have a way to specify the accessDecisionManager right there. You have to extend GlobalMethodSecurityConfiguration and override the AccessDecisionManager method.

并根据原始帖子的需要实施和配置尽可能多的策略

And to implement and configure as many strategies as you want the original post has

<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>

这两种方法都可以像这样使用 Java Config 来完成:

Both tinks can be done using Java Config like this:

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

}

最后是简单明了的网络安全配置.请注意,我使用的是RestWebSecurity...",您可以随意命名.

And, finally, the plain and simple Web Security Config. Note that I'm using a "RestWebSecurity..." you can name it anyway you want.

@Configuration
public class WebSecurityConfig {

    @Configuration
    @Order(1)                                                        
    public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .antMatchers(HttpMethod.GET, "/**").authenticated()
            .and()
                .httpBasic()
            .and()
                .csrf().disable();    
        }
    }
}

为了完整性,当用户不应该继续时,实施的策略必须返回 AccessDeniedExceptionInsufficientAuthenticationException.这是访问参数和所有参数的示例:

Just for completeness, the implemented strategy has to return AccessDeniedException or InsufficientAuthenticationException when the user shouldn't proceed. Here is an example accessing parameters and all:

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

================== 原始答案 ========================

我通过实现自己的 AccessDecisionManager 来实现这一点,该管理器将访问决策委托给我的特殊接口 AccessDecisionStrategy:

I achieved that by implementing my own AccessDecisionManager that delegates access decisions to my special interface AccessDecisionStrategy:

public interface AccessDecisionStrategy {

    void decide(Authentication authentication, MethodInvocation methodInvocation, ConfigAttribute configAttribute);

}

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

Each access decision strategy represents different way of making access decision.

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

You can easily implement your own strategy (even in other language - for instance Scala):

公共类 SomeStrategy 实现 AccessDecisionStrategy { ...

public class SomeStrategy implements AccessDecisionStrategy { ...

如您所见,我的 AccessDecisionManager 有一张策略图.manager 使用的策略基于 annotation 参数.

As you can see, my AccessDecisionManager has a map of strategies. Strategy used by manager is based on annotation argument.

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 注释与策略名称的参数一起放置:

Now when I want to protect my method I put @Secured annotation with argument that is name of the strategy:

@Secured("GetByOwner")
FlightSpotting getFlightSpotting(Long id);

您可以根据需要实施和配置任意数量的策略:

You can implement and configure as many strategies as you want:

<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>

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

To inject that access decision manager you type:

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

我还实现了辅助类来处理 MethodInvocation 参数:

I also implemented helper class to handle MethodInvocation arguments:

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

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

Now you can easily extract interesting arguments in the code of your strategy to make decision:

假设我想获得 Long 类型的参数编号 0:

Let's say I want to get argument number 0 that is of type Long:

MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation);
Long id = extractor.getArg(0);

12 年 11 月 14 日 14:40 回答
马切伊·齐亚科

这篇关于Spring Boot 中多种方法的 MethodSecurityInterceptor的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆