这与Mickael Marrache的this question答案有关如何使用授权逻辑保护服务安全有关,该授权逻辑在方法上可能会有所不同。
我更喜欢Maciej Ziarko使用Method Security AccessDecisionManager 而不是接受的响应,因为它使用相同的注释@Secured
和不同的自定义参数。
由于我使用的是没有XML配置的Spring-Boot,所以花了我一段时间来弄清楚该如何做。
所以,这是我的答案。
它仅说明了如何用Java Config配置替换xml配置。
最佳答案
(更改之后,我将添加原始答案“以防万一”。)
为了替换xml配置:
<sec:global-method-security secured-annotations="enabled"
access-decision-manager-ref="methodSecurityAccessDecisionManager">
</sec:global-method-security>
碰巧注释
@EnableGlobalMethodSecurity
无法在此处指定accessDecisionManager。您必须扩展GlobalMethodSecurityConfiguration
并覆盖AccessDecisionManager
方法。并根据您希望的原始情况实施和配置尽可能多的策略
<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来完成这两种操作:
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);
}
}
最后,简单明了的Web安全配置。
请注意,我使用的是“ RestWebSecurity ...”,您可以随时为它命名。
@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();
}
}
}
仅出于完整性考虑,当用户不应继续执行时,已实施的策略必须返回
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");
}
}
}
=================原始答案========================
我通过实现自己的AccessDecisionManager(将访问决策委派给我的特殊接口AccessDecisionStrategy)来实现:
public interface AccessDecisionStrategy {
void decide(Authentication authentication, MethodInvocation methodInvocation, ConfigAttribute configAttribute);
}
每种访问决策策略都代表做出访问决策的不同方式。
您可以轻松地实施自己的策略(甚至使用其他语言,例如Scala):
公共类SomeStrategy实现AccessDecisionStrategy {...
如您所见,我的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();
}
}
}
现在,您可以轻松地从策略代码中提取有趣的参数来做出决策:
假设我想获取类型为Long的参数编号0:
MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation);
Long id = extractor.getArg(0);
12年11月14日在14:40回答
Maciej Ziarko