我通过实现我自己AccessDecisionManager
的将访问决定委托给我的特殊接口来实现这一点AccessDecisionStrategy
:
public interface AccessDecisionStrategy {
void decide(Authentication authentication, MethodInvocation methodInvocation, ConfigAttribute configAttribute);
}
每个访问决策策略代表了不同的访问决策方式。
您可以轻松实现自己的策略(即使使用其他语言 - 例如 Scala):
public class SomeStrategy implements 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();
}
}
}
现在,您可以轻松地在策略代码中提取有趣的参数来做出决策:
假设我想获取0
类型为的参数编号Long
:
MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation);
Long id = extractor.getArg(0);