Spring Security OAuth2 架构上分为Authorization Server认证服务器和Resource Server资源服务器。我们可以为每一个Resource Server(一个微服务实例)设置一个resourceid。Authorization Server给client第三方客户端授权的时候,可以设置这个client可以访问哪一些Resource Server资源服务,如果没设置,就是对所有的Resource Server都有访问权限。
在每个ResourceServer实例上设置resourceId,该resourceId作为该服务资源的唯一标识。(假如同一个微服务资源部署多份,resourceId相同)
@Configuration @EnableResourceServer public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { private static final String DEMO_RESOURCE_ID = "test-resource"; @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.resourceId(DEMO_RESOURCE_ID) //...... 还可以有有其他的配置 } }
在AuthorizationServer为客户端client配置ResourceID的目的是:限制某个client可以访问的资源服务。
@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //配置客户端存储到db 代替原来得内存模式 JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource); clientDetailsService.setPasswordEncoder(passwordEncoder); clients.withClientDetails(clientDetailsService); }
这里需要使用JdbcClientDetailsService类和数据库表oauth_client_details进行配置的持久化存储,以及动态配置。
ResourceID当然是在Resource Server资源服务器进行验证(你能不能访问我的资源,当然由我自己来验证)。当资源请求发送到Resource Server的时候会携带access_token,Resource Server会根据access_token找到client_id,进而找到该client可以访问的resource_ids。如果resource_ids包含ResourceServer自己设置ResourceID,这关就过去了,就可以继续进行其他的权限验证。
如果AuthorizationServer认证client1可以访问test-resource,但client1去访问了oauth-rs,会响应如下信息:
{"error":"access_denied","error_description":"Invalid token does not contain resource id (oauth-rs)"}
具体实现resource_id验证的源码:OAuth2AuthenticationManager#authenticate(Authentication authentication)
public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication == null) { throw new InvalidTokenException("Invalid token (token not found)"); } String token = (String) authentication.getPrincipal(); OAuth2Authentication auth = tokenServices.loadAuthentication(token); if (auth == null) { throw new InvalidTokenException("Invalid token: " + token); } Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds(); if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) { throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")"); } checkClientDetails(auth); if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) { OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails(); // Guard against a cached copy of the same details if (!details.equals(auth.getDetails())) { // Preserve the authentication details from the one loaded by token services details.setDecodedDetails(auth.getDetails()); } } auth.setDetails(authentication.getDetails()); auth.setAuthenticated(true); return auth; }
下面这段便是验证resourceid的地方
Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds(); if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) { throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")"); }
在Spring Security的FilterChain中,OAuth2AuthenticationProcessingFilter在FilterSecurityInterceptor的前面,所以会先验证client有没有此resource的权限,只有在有此resource的权限的情况下,才会再去做进一步的进行其他验证的判断。