实际上,您可以实现自定义强类型安全注释,尽管这相当麻烦。声明批注
enum Permission {
USER_LIST,
USER_EDIT,
USER_ADD,
USER_ROLE_EDIT
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Permissions {
Permission[] value();
}
声明要由安全管道使用的自定义实现org.springframework.security.access.ConfigAttribute
class SecurityAttribute implements ConfigAttribute {
private final List<Permission> permissions;
public SecurityAttribute(List<Permission> permissions) {
this.permissions = permissions;
}
@Override
public String getAttribute() {
return permissions.stream().map(p -> p.name()).collect(Collectors.joining(","));
}
}
声明 的自定义实现,以创建 from 批注的实例org.springframework.security.access.method.MethodSecurityMetadataSource
SecurityAttribute
class SecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
@Override
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
//consult https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/access/prepost/PrePostAnnotationSecurityMetadataSource.java
//to implement findAnnotation
Permissions annotation = findAnnotation(method, targetClass, Permissions.class);
if (annotation != null) {
return Collections.singletonList(new SecurityAttribute(asList(annotation.value())));
}
return Collections.emptyList();
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
}
最后声明自定义实现org.springframework.security.access.AccessDecisionVoter
public class PermissionVoter implements AccessDecisionVoter<MethodInvocation> {
@Override
public boolean supports(ConfigAttribute attribute) {
return attribute instanceof SecurityAttribute;
}
@Override
public boolean supports(Class<?> clazz) {
return MethodInvocation.class.isAssignableFrom(clazz);
}
@Override
public int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {
Optional<SecurityAttribute> securityAttribute = attributes.stream()
.filter(attr -> attr instanceof SecurityAttribute).map(SecurityAttribute.class::cast).findFirst();
if(!securityAttribute.isPresent()){
return AccessDecisionVoter.ACCESS_ABSTAIN;
}
//authorize your principal from authentication object
//against permissions and return ACCESS_GRANTED or ACCESS_DENIED
}
}
现在把它们都放在你的MethodSecurityConfig
@Configuration
@EnableGlobalMethodSecurity
class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return new ScpSecurityMetadataSource();
}
@Override
protected AccessDecisionManager accessDecisionManager() {
return new AffirmativeBased(Collections.singletonList(new PermissionVoter()));
}
}