@EnableResourceServer进行资源权限判断,每个微服务视为一个资源 @EnableGlobalMethodSecurity(prePostEnabled = true) 权限验证精确到Controller具体的方法
@EnableResourceServer会增加自动配置ResourceServerSecurityConfigurer,其中含有过滤器,权限验证管理类,token存储,resourceid等权限验证相关的配置,并且使用OAuth2AuthenticationManager验证权限
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ResourceServerConfiguration.class})
public @interface EnableResourceServer {
}
public final class ResourceServerSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
private AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
private OAuth2AuthenticationProcessingFilter resourcesServerFilter;
private AuthenticationManager authenticationManager;
private AuthenticationEventPublisher eventPublisher = null;
private ResourceServerTokenServices resourceTokenServices;
private TokenStore tokenStore = new InMemoryTokenStore();
private String resourceId = "oauth2-resource";
private SecurityExpressionHandler<FilterInvocation> expressionHandler = new OAuth2WebSecurityExpressionHandler();
private TokenExtractor tokenExtractor;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
private boolean stateless = true;
private AuthenticationManager oauthAuthenticationManager(HttpSecurity http) {
OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager();
首先按照resourceId验证权限(ResourceServerSecurityConfigurer中默认的资源id都是相同的) ,
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication == null) {
throw new InvalidTokenException("Invalid token (token not found)");
} else {
String token = (String)authentication.getPrincipal();
OAuth2Authentication auth = this.tokenServices.loadAuthentication(token);
if (auth == null) {
throw new InvalidTokenException("Invalid token: " + token);
} else {
Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
if (this.resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(this.resourceId)) {
throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + this.resourceId + ")");
} else {
this.checkClientDetails(auth);
if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();
if (!details.equals(auth.getDetails())) {
details.setDecodedDetails(auth.getDetails());
}
}
auth.setDetails(authentication.getDetails());
auth.setAuthenticated(true);
return auth;
}
}
}
}
ClientDetailsService
@EnableGlobalMethodSecurity:
//开启spring security 方法权限验证注解
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({GlobalMethodSecuritySelector.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableGlobalMethodSecurity {
final class GlobalMethodSecuritySelector implements ImportSelector {
GlobalMethodSecuritySelector() {
}
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class;
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(annoType.getName(), false);
AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationAttributes);
Assert.notNull(attributes, () -> {
return String.format("@%s is not present on importing class '%s' as expected", annoType.getSimpleName(), importingClassMetadata.getClassName());
});
Class<?> importingClass = ClassUtils.resolveClassName(importingClassMetadata.getClassName(), ClassUtils.getDefaultClassLoader());
boolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class.isAssignableFrom(importingClass);
AdviceMode mode = (AdviceMode)attributes.getEnum("mode");
boolean isProxy = AdviceMode.PROXY == mode;
String autoProxyClassName = isProxy ? AutoProxyRegistrar.class.getName() : GlobalMethodSecurityAspectJAutoProxyRegistrar.class.getName();
boolean jsr250Enabled = attributes.getBoolean("jsr250Enabled");
List<String> classNames = new ArrayList(4);
if (isProxy) {
classNames.add(MethodSecurityMetadataSourceAdvisorRegistrar.class.getName());
}
classNames.add(autoProxyClassName);
if (!skipMethodSecurityConfiguration) {
classNames.add(GlobalMethodSecurityConfiguration.class.getName());
}
if (jsr250Enabled) {
classNames.add(Jsr250MetadataSourceConfiguration.class.getName());
}
return (String[])classNames.toArray(new String[0]);
}
}
下面两行将import GlobalMethodSecurityConfiguration,然后在配置类中完成配置
boolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class.isAssignableFrom(importingClass);
if (!skipMethodSecurityConfiguration) { classNames.add(GlobalMethodSecurityConfiguration.class.getName()); }
@Configuration
public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInitializingSingleton, BeanFactoryAware {
private static final Log logger = LogFactory.getLog(GlobalMethodSecurityConfiguration.class);
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
public <T> T postProcess(T object) {
throw new IllegalStateException(ObjectPostProcessor.class.getName() + " is a required bean. Ensure you have used @" + EnableGlobalMethodSecurity.class.getName());
}
};
private DefaultMethodSecurityExpressionHandler defaultMethodExpressionHandler = new DefaultMethodSecurityExpressionHandler();
private AuthenticationManager authenticationManager;
private AuthenticationManagerBuilder auth;
private boolean disableAuthenticationRegistry;
private AnnotationAttributes enableMethodSecurity;
private BeanFactory context;
private MethodSecurityExpressionHandler expressionHandler;
private MethodSecurityInterceptor methodSecurityInterceptor;