Skip to content

Commit 75f81eb

Browse files
committed
Add comments
1 parent 89bc692 commit 75f81eb

23 files changed

+1090
-87
lines changed

device-authorization-flow/device-authorization-server/src/main/java/com/relive/authentication/DeviceClientAuthenticationConverter.java

+57-2
Original file line numberDiff line numberDiff line change
@@ -21,46 +21,94 @@
2121
import java.util.Map;
2222

2323
/**
24+
* {@code DeviceClientAuthenticationConverter} 类实现了 {@link AuthenticationConverter} 接口,
25+
* 用于将设备授权请求或设备访问令牌请求转换为 {@link DeviceClientAuthenticationToken} 实例。
26+
*
27+
* <p>它主要处理以下两类请求:</p>
28+
* <ul>
29+
* <li>设备授权请求(包含 "device_code" 参数)。</li>
30+
* <li>设备访问令牌请求(包含 "grant_type=device_code" 和 "device_code" 参数)。</li>
31+
* </ul>
32+
*
33+
* <p>该转换器对请求参数进行严格验证,如校验 client_id 是否唯一且有效。</p>
34+
*
2435
* @author: ReLive27
2536
* @date: 2024/1/23 22:43
2637
*/
2738
public class DeviceClientAuthenticationConverter implements AuthenticationConverter {
39+
40+
/**
41+
* 匹配设备授权请求的条件。
42+
*/
2843
private final RequestMatcher deviceAuthorizationRequestMatcher;
44+
45+
/**
46+
* 匹配设备访问令牌请求的条件。
47+
*/
2948
private final RequestMatcher deviceAccessTokenRequestMatcher;
3049

50+
/**
51+
* 构造函数,初始化请求匹配器。
52+
*
53+
* @param deviceAuthorizationEndpointUri 设备授权端点的 URI,用于匹配设备授权请求。
54+
*/
3155
public DeviceClientAuthenticationConverter(String deviceAuthorizationEndpointUri) {
56+
// 匹配设备授权请求:路径匹配、响应类型为 device_code 且包含 client_id 参数
3257
this.deviceAuthorizationRequestMatcher = new AndRequestMatcher(
3358
new AntPathRequestMatcher(
3459
deviceAuthorizationEndpointUri, HttpMethod.POST.name()),
3560
request -> "device_code".equals(request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE)),
3661
request -> request.getParameter(OAuth2ParameterNames.CLIENT_ID) != null);
62+
63+
// 匹配设备访问令牌请求:grant_type 为 device_code,且包含 device_code 和 client_id 参数
3764
this.deviceAccessTokenRequestMatcher = request ->
3865
AuthorizationGrantType.DEVICE_CODE.getValue().equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) &&
3966
request.getParameter(OAuth2ParameterNames.DEVICE_CODE) != null &&
4067
request.getParameter(OAuth2ParameterNames.CLIENT_ID) != null;
4168
}
4269

70+
/**
71+
* 转换请求为 {@link DeviceClientAuthenticationToken}。
72+
*
73+
* @param request 当前的 HTTP 请求。
74+
* @return 如果请求匹配设备授权或设备访问令牌请求,返回一个 {@link DeviceClientAuthenticationToken},否则返回 {@code null}。
75+
* @throws OAuth2AuthenticationException 如果请求参数无效(如缺少或多于一个 client_id)。
76+
*/
4377
@Nullable
4478
@Override
4579
public Authentication convert(HttpServletRequest request) {
80+
// 请求不匹配设备授权请求或设备访问令牌请求,返回 null
4681
if (!this.deviceAuthorizationRequestMatcher.matches(request) &&
4782
!this.deviceAccessTokenRequestMatcher.matches(request)) {
4883
return null;
4984
}
5085

51-
// client_id (REQUIRED)
86+
// 提取 client_id,确保非空且唯一
5287
String clientId = request.getParameter(OAuth2ParameterNames.CLIENT_ID);
5388
if (!StringUtils.hasText(clientId) ||
5489
request.getParameterValues(OAuth2ParameterNames.CLIENT_ID).length != 1) {
5590
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
5691
}
92+
93+
// 提取额外的请求参数,排除 client_id 参数
5794
Map<String, Object> additionalParameters = getParametersIfMatchesDeviceCodeGrantRequest(request,
5895
OAuth2ParameterNames.CLIENT_ID);
96+
97+
// 返回一个新的 DeviceClientAuthenticationToken 实例
5998
return new DeviceClientAuthenticationToken(clientId, ClientAuthenticationMethod.NONE, null, additionalParameters);
6099
}
61100

101+
/**
102+
* 提取请求中的参数,排除指定的参数名(如 client_id)。
103+
*
104+
* @param request 当前的 HTTP 请求。
105+
* @param exclusions 要排除的参数名。
106+
* @return 返回一个包含请求参数的 Map,排除了指定的参数。
107+
*/
62108
static Map<String, Object> getParametersIfMatchesDeviceCodeGrantRequest(HttpServletRequest request, String... exclusions) {
63109
MultiValueMap<String, String> multiValueParameters = getParameters(request);
110+
111+
// 排除指定的参数
64112
for (String exclusion : exclusions) {
65113
multiValueParameters.remove(exclusion);
66114
}
@@ -72,17 +120,24 @@ static Map<String, Object> getParametersIfMatchesDeviceCodeGrantRequest(HttpServ
72120
return parameters;
73121
}
74122

123+
/**
124+
* 提取请求中的所有参数,并返回为 {@link MultiValueMap}。
125+
*
126+
* @param request 当前的 HTTP 请求。
127+
* @return 包含请求参数的 {@link MultiValueMap}。
128+
*/
75129
static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
76130
Map<String, String[]> parameterMap = request.getParameterMap();
77131
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
132+
78133
parameterMap.forEach((key, values) -> {
79134
if (values.length > 0) {
80135
for (String value : values) {
81136
parameters.add(key, value);
82137
}
83138
}
84139
});
140+
85141
return parameters;
86142
}
87143
}
88-

device-authorization-flow/device-authorization-server/src/main/java/com/relive/authentication/DeviceClientAuthenticationProvider.java

+45-5
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,67 @@
1414
import org.springframework.util.Assert;
1515

1616
/**
17+
* {@code DeviceClientAuthenticationProvider} 实现了 {@link AuthenticationProvider} 接口,
18+
* 用于验证设备授权客户端的身份(无客户端凭证,基于 client_id)。
19+
*
20+
* <p>该 Provider 支持以下功能:</p>
21+
* <ul>
22+
* <li>校验客户端认证方法是否为 {@code none}(无认证)。</li>
23+
* <li>验证 client_id 是否存在并匹配已注册客户端。</li>
24+
* <li>确保客户端支持无认证方法 {@link ClientAuthenticationMethod#NONE}。</li>
25+
* </ul>
26+
*
27+
* <p>如果验证失败,则会抛出 {@link OAuth2AuthenticationException} 异常,错误信息符合 RFC 6749 标准。</p>
28+
*
1729
* @author: ReLive27
1830
* @date: 2024/1/23 22:41
1931
*/
2032
@Slf4j
2133
public class DeviceClientAuthenticationProvider implements AuthenticationProvider {
34+
2235
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1";
36+
37+
/**
38+
* 客户端注册仓库,用于查找已注册的客户端。
39+
*/
2340
private final RegisteredClientRepository registeredClientRepository;
2441

42+
/**
43+
* 构造函数,初始化 {@link RegisteredClientRepository}。
44+
*
45+
* @param registeredClientRepository 客户端注册仓库,不能为 {@code null}。
46+
*/
2547
public DeviceClientAuthenticationProvider(RegisteredClientRepository registeredClientRepository) {
2648
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
2749
this.registeredClientRepository = registeredClientRepository;
2850
}
2951

52+
/**
53+
* 对设备客户端认证请求进行身份验证。
54+
*
55+
* @param authentication 设备客户端认证请求,包含 {@code client_id} 和认证方法。
56+
* @return 如果验证成功,返回新的 {@link DeviceClientAuthenticationToken} 实例;
57+
* 如果认证方法不支持,返回 {@code null}。
58+
* @throws OAuth2AuthenticationException 如果验证失败(如 {@code client_id} 无效或不支持的认证方法)。
59+
*/
3060
@Override
3161
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
3262
DeviceClientAuthenticationToken deviceClientAuthentication =
3363
(DeviceClientAuthenticationToken) authentication;
3464

65+
// 仅支持无客户端认证方法(none)
3566
if (!ClientAuthenticationMethod.NONE.equals(deviceClientAuthentication.getClientAuthenticationMethod())) {
3667
return null;
3768
}
3869

70+
// 校验 client_id
3971
String clientId = deviceClientAuthentication.getPrincipal().toString();
4072
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);
4173
if (registeredClient == null) {
4274
throwInvalidClient(OAuth2ParameterNames.CLIENT_ID);
4375
}
4476

77+
// 校验客户端是否支持 "none" 认证方法
4578
if (!registeredClient.getClientAuthenticationMethods().contains(
4679
deviceClientAuthentication.getClientAuthenticationMethod())) {
4780
throwInvalidClient("authentication_method");
@@ -51,19 +84,27 @@ public Authentication authenticate(Authentication authentication) throws Authent
5184
log.trace("Validated device client authentication parameters");
5285
}
5386

54-
if (log.isTraceEnabled()) {
55-
log.trace("Authenticated device client");
56-
}
57-
5887
return new DeviceClientAuthenticationToken(registeredClient,
5988
deviceClientAuthentication.getClientAuthenticationMethod(), null);
6089
}
6190

91+
/**
92+
* 确定是否支持指定的认证类型。
93+
*
94+
* @param authentication 认证类型。
95+
* @return 如果支持 {@link DeviceClientAuthenticationToken} 类型,返回 {@code true}。
96+
*/
6297
@Override
6398
public boolean supports(Class<?> authentication) {
6499
return DeviceClientAuthenticationToken.class.isAssignableFrom(authentication);
65100
}
66101

102+
/**
103+
* 抛出无效客户端异常,附带错误描述和 URI 链接。
104+
*
105+
* @param parameterName 失败的具体参数(如 {@code client_id} 或认证方法)。
106+
* @throws OAuth2AuthenticationException 包装的 OAuth2 错误。
107+
*/
67108
private static void throwInvalidClient(String parameterName) {
68109
OAuth2Error error = new OAuth2Error(
69110
OAuth2ErrorCodes.INVALID_CLIENT,
@@ -72,5 +113,4 @@ private static void throwInvalidClient(String parameterName) {
72113
);
73114
throw new OAuth2AuthenticationException(error);
74115
}
75-
76116
}

device-authorization-flow/device-authorization-server/src/main/java/com/relive/authentication/DeviceClientAuthenticationToken.java

+32-1
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,50 @@
88
import java.util.Map;
99

1010
/**
11+
* {@code DeviceClientAuthenticationToken} 是设备客户端身份验证的自定义令牌。
12+
*
13+
* <p>该类继承自 {@link OAuth2ClientAuthenticationToken},主要用于 OAuth 2.0 设备授权流程中的客户端认证。</p>
14+
*
15+
* <p>设备客户端身份验证的特点:</p>
16+
* <ul>
17+
* <li>客户端不需要使用客户端密钥(即无凭证认证)。</li>
18+
* <li>认证过程中必须提供 {@code client_id}。</li>
19+
* <li>可能包含额外参数 {@link Map},例如设备授权请求中的信息。</li>
20+
* </ul>
21+
*
22+
* <p>该类提供两种构造函数:</p>
23+
* <ul>
24+
* <li>一种基于 {@code clientId} 和认证方法(适用于请求阶段)。</li>
25+
* <li>一种基于已注册客户端 {@link RegisteredClient}(适用于验证通过后的阶段)。</li>
26+
* </ul>
27+
*
1128
* @author: ReLive27
1229
* @date: 2024/1/23 22:42
1330
*/
1431
public class DeviceClientAuthenticationToken extends OAuth2ClientAuthenticationToken {
1532

33+
/**
34+
* 构造函数。
35+
*
36+
* @param clientId 客户端 ID,表示发起请求的客户端。
37+
* @param clientAuthenticationMethod 客户端认证方法,通常为 {@link ClientAuthenticationMethod#NONE}。
38+
* @param credentials 凭据,可以为 {@code null}。
39+
* @param additionalParameters 额外参数,例如设备授权请求中的参数,允许为 {@code null}。
40+
*/
1641
public DeviceClientAuthenticationToken(String clientId, ClientAuthenticationMethod clientAuthenticationMethod,
1742
@Nullable Object credentials, @Nullable Map<String, Object> additionalParameters) {
1843
super(clientId, clientAuthenticationMethod, credentials, additionalParameters);
1944
}
2045

46+
/**
47+
* 构造函数,用于基于已注册客户端创建。
48+
*
49+
* @param registeredClient 已注册客户端,表示通过验证的客户端。
50+
* @param clientAuthenticationMethod 客户端认证方法,通常为 {@link ClientAuthenticationMethod#NONE}。
51+
* @param credentials 凭据,可以为 {@code null}。
52+
*/
2153
public DeviceClientAuthenticationToken(RegisteredClient registeredClient, ClientAuthenticationMethod clientAuthenticationMethod,
2254
@Nullable Object credentials) {
2355
super(registeredClient, clientAuthenticationMethod, credentials);
2456
}
25-
2657
}

0 commit comments

Comments
 (0)