Spring Security OAuth2之scopes配置详解

官方文档说明

地址:https://projects.spring.io/spring-security-oauth/docs/oauth2.html

scope: The scope to which the client is limited. If scope is undefined or empty (the default) the client is not limited by scope.

  • 用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围
  • scope中文翻译就是作用域,用来限制客户端权限访问的范围,可以用来设置角色或者权限,也可以不设置

虽然官方网站说是服务器端的client配置scopes可以为空,但是经过实际操作及跟踪源码来看password模式下调用/oauth/token端点拿用户token信息服务端可以为空,
但是客户端必须传scopes;refresh_token模式服务端及client端的scopes都需要配置,所以即使我们用不到scopes前后端最好都配置上scopes("all");

资料信息:https://stackoverflow.com/questions/39756748/spring-oauth-authorization-server-requires-scope

scopes的校验是在TokenEndpoint进行的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@RequestMapping(
value = {"/oauth/token"},
method = {RequestMethod.POST}
)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
} else {
String clientId = this.getClientId(principal);
ClientDetails authenticatedClient = this.getClientDetailsService().loadClientByClientId(clientId);
//OAuth2RequestFactory接口的实现类DefaultOAuth2RequestFactory创建token请求对象
TokenRequest tokenRequest = this.getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
if (clientId != null && !clientId.equals("") && !clientId.equals(tokenRequest.getClientId())) {
throw new InvalidClientException("Given client ID does not match authenticated client");
} else {
//校验scope客户端和服务器端的设置是否匹配
if (authenticatedClient != null) {
this.oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
}

if (!StringUtils.hasText(tokenRequest.getGrantType())) {
throw new InvalidRequestException("Missing grant type");
} else if (tokenRequest.getGrantType().equals("implicit")) {
throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
} else {
if (this.isAuthCodeRequest(parameters) && !tokenRequest.getScope().isEmpty()) {
this.logger.debug("Clearing scope of incoming token request");
tokenRequest.setScope(Collections.emptySet());
}

if (this.isRefreshTokenRequest(parameters)) {
tokenRequest.setScope(OAuth2Utils.parseParameterList((String)parameters.get("scope")));
}

OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
} else {
return this.getResponse(token);
}
}
}
}
}

进入this.oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);查看处理逻辑

oAuth2RequestValidator对象是DefaultOAuth2RequestValidator的实例,进入看下实现逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.security.oauth2.provider.request;

import java.util.Iterator;
import java.util.Set;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.OAuth2RequestValidator;
import org.springframework.security.oauth2.provider.TokenRequest;

public class DefaultOAuth2RequestValidator implements OAuth2RequestValidator {
public DefaultOAuth2RequestValidator() {
}

public void validateScope(AuthorizationRequest authorizationRequest, ClientDetails client) throws InvalidScopeException {
this.validateScope(authorizationRequest.getScope(), client.getScope());
}
//校验客户端scope和服务端scope方法
public void validateScope(TokenRequest tokenRequest, ClientDetails client) throws InvalidScopeException {
this.validateScope(tokenRequest.getScope(), client.getScope());
}
//实际的校验方法
private void validateScope(Set<String> requestScopes, Set<String> clientScopes) {
//客户端scope不为空并且scope在服务端scope限制范围之内通过校验
//客户端scope不为空,服务端为空或不设置通过校验
if (clientScopes != null && !clientScopes.isEmpty()) {
Iterator var3 = requestScopes.iterator();

while(var3.hasNext()) {
String scope = (String)var3.next();
if (!clientScopes.contains(scope)) {
throw new InvalidScopeException("Invalid scope: " + scope, clientScopes);
}
}
}
//如果客户端的scope为空将会抛出异常,所以客户端不可以为空
if (requestScopes.isEmpty()) {
throw new InvalidScopeException("Empty scope (either the client or the user is not allowed the requested scopes)");
}
}
}

DefaultOAuth2RequestFactory实现类组装token请求及校验scopes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.security.oauth2.provider.request;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.DefaultSecurityContextAccessor;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.SecurityContextAccessor;
import org.springframework.security.oauth2.provider.TokenRequest;

public class DefaultOAuth2RequestFactory implements OAuth2RequestFactory {
private final ClientDetailsService clientDetailsService;
private SecurityContextAccessor securityContextAccessor = new DefaultSecurityContextAccessor();
private boolean checkUserScopes = false;

public DefaultOAuth2RequestFactory(ClientDetailsService clientDetailsService) {
this.clientDetailsService = clientDetailsService;
}

public void setSecurityContextAccessor(SecurityContextAccessor securityContextAccessor) {
this.securityContextAccessor = securityContextAccessor;
}

public void setCheckUserScopes(boolean checkUserScopes) {
this.checkUserScopes = checkUserScopes;
}

public AuthorizationRequest createAuthorizationRequest(Map<String, String> authorizationParameters) {
String clientId = (String)authorizationParameters.get("client_id");
String state = (String)authorizationParameters.get("state");
String redirectUri = (String)authorizationParameters.get("redirect_uri");
Set<String> responseTypes = OAuth2Utils.parseParameterList((String)authorizationParameters.get("response_type"));
Set<String> scopes = this.extractScopes(authorizationParameters, clientId);
AuthorizationRequest request = new AuthorizationRequest(authorizationParameters, Collections.emptyMap(), clientId, scopes, (Set)null, (Collection)null, false, state, redirectUri, responseTypes);
ClientDetails clientDetails = this.clientDetailsService.loadClientByClientId(clientId);
request.setResourceIdsAndAuthoritiesFromClientDetails(clientDetails);
return request;
}

public OAuth2Request createOAuth2Request(AuthorizationRequest request) {
return request.createOAuth2Request();
}
//创建请求入口类
public TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient) {
String clientId = (String)requestParameters.get("client_id");
if (clientId == null) {
clientId = authenticatedClient.getClientId();
} else if (!clientId.equals(authenticatedClient.getClientId())) {
throw new InvalidClientException("Given client ID does not match authenticated client");
}

String grantType = (String)requestParameters.get("grant_type");
//获取客户端传递或者服务端的scope
Set<String> scopes = this.extractScopes(requestParameters, clientId);
TokenRequest tokenRequest = new TokenRequest(requestParameters, clientId, scopes, grantType);
return tokenRequest;
}

public TokenRequest createTokenRequest(AuthorizationRequest authorizationRequest, String grantType) {
TokenRequest tokenRequest = new TokenRequest(authorizationRequest.getRequestParameters(), authorizationRequest.getClientId(), authorizationRequest.getScope(), grantType);
return tokenRequest;
}

public OAuth2Request createOAuth2Request(ClientDetails client, TokenRequest tokenRequest) {
return tokenRequest.createOAuth2Request(client);
}
//如果参数中的scope为null,则从服务端配置的scope中取,并且根据this.checkUserScopes的值判断是否校验scopes的有效性
private Set<String> extractScopes(Map<String, String> requestParameters, String clientId) {
Set<String> scopes = OAuth2Utils.parseParameterList((String)requestParameters.get("scope"));
ClientDetails clientDetails = this.clientDetailsService.loadClientByClientId(clientId);
if (scopes == null || scopes.isEmpty()) {
scopes = clientDetails.getScope();
}

if (this.checkUserScopes) {
scopes = this.checkUserScopes(scopes, clientDetails);
}

return scopes;
}

private Set<String> checkUserScopes(Set<String> scopes, ClientDetails clientDetails) {
if (!this.securityContextAccessor.isUser()) {
return scopes;
} else {
Set<String> result = new LinkedHashSet();
Set<String> authorities = AuthorityUtils.authorityListToSet(this.securityContextAccessor.getAuthorities());
Iterator var5 = scopes.iterator();

while(true) {
String scope;
do {
if (!var5.hasNext()) {
return result;
}

scope = (String)var5.next();
} while(!authorities.contains(scope) && !authorities.contains(scope.toUpperCase()) && !authorities.contains("ROLE_" + scope.toUpperCase()));

result.add(scope);
}
}
}
}

本文转载参考 原文 并加以调试

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :