OAuth2授权架构:PKCE流程详解
OAuth2授权架构:PKCE流程详解
OAuth2授权码流程
OAuth2授权码流程是最安全的授权方式之一,适用于服务端应用和单页应用。
// OAuth2授权端点
@RestController
@RequestMapping("/oauth2")
public class OAuth2AuthorizationEndpoint {
@GetMapping("/authorize")
public ResponseEntity<Void> authorize(
@RequestParam("response_type") String responseType,
@RequestParam("client_id") String clientId,
@RequestParam("redirect_uri") String redirectUri,
@RequestParam("scope") String scope,
@RequestParam(value = "state", required = false) String state,
@RequestParam(value = "code_challenge", required = false) String codeChallenge,
@RequestParam(value = "code_challenge_method", required = false) String codeChallengeMethod) {
// 验证客户端
Client client = clientService.validate(clientId, redirectUri);
// 生成授权码
AuthorizationCode code = authorizationService.generateCode(
client, scope, state, codeChallenge, codeChallengeMethod
);
// 重定向回客户端
String redirectUrl = redirectUri +
"?code=" + code.getValue() +
(state != null ? "&state=" + state : "");
return ResponseEntity.status(302)
.header("Location", redirectUrl)
.build();
}
}
PKCE增强安全
PKCE(Proof Key for Code Exchange)防止授权码被拦截攻击,特别适用于公共客户端。
// PKCE实现
@Component
public class PKCEHandler {
public CodeChallenge generateCodeChallenge(String codeVerifier) {
// SHA256哈希
String challenge = Base64.getUrlEncoder().withoutPadding()
.digestToString(MessageDigest.getInstance("SHA-256")
.digest(codeVerifier.getBytes()));
return CodeChallenge.builder()
.verifier(codeVerifier)
.challenge(challenge)
.method("S256")
.build();
}
public boolean verifyCodeExchange(String codeVerifier, String codeChallenge,
String codeChallengeMethod) {
if ("S256".equals(codeChallengeMethod)) {
String computed = generateCodeChallenge(codeVerifier).getChallenge();
return MessageDigest.isEqual(computed.getBytes(), codeChallenge.getBytes());
} else if ("plain".equals(codeChallengeMethod)) {
return codeVerifier.equals(codeChallenge);
}
return false;
}
}
令牌交换与刷新
// 令牌端点
@RestController
@RequestMapping("/oauth2")
public class TokenEndpoint {
@PostMapping("/token")
public ResponseEntity<TokenResponse> token(
@RequestParam("grant_type") String grantType,
@RequestParam(value = "code", required = false) String code,
@RequestParam(value = "redirect_uri", required = false) String redirectUri,
@RequestParam(value = "client_id", required = false) String clientId,
@RequestParam(value = "client_secret", required = false) String clientSecret,
@RequestParam(value = "refresh_token", required = false) String refreshToken,
@RequestParam(value = "code_verifier", required = false) String codeVerifier) {
if ("authorization_code".equals(grantType)) {
// 验证授权码
AuthorizationCode authCode = codeService.validate(code, clientId, redirectUri);
// PKCE验证
if (authCode.getCodeChallenge() != null) {
if (!pkceHandler.verifyCodeExchange(
codeVerifier,
authCode.getCodeChallenge(),
authCode.getCodeChallengeMethod())) {
throw new InvalidGrantException("PKCE验证失败");
}
}
// 生成令牌
TokenSet tokens = tokenService.generateTokens(authCode);
return ResponseEntity.ok(TokenResponse.fromTokenSet(tokens));
} else if ("refresh_token".equals(grantType)) {
// 刷新令牌
TokenSet newTokens = tokenService.refresh(refreshToken, clientId);
return ResponseEntity.ok(TokenResponse.fromTokenSet(newTokens));
}
throw new UnsupportedGrantTypeException("不支持的授权类型");
}
}
安全最佳实践
# OAuth2安全配置
oauth2:
security:
# 强制使用PKCE
require_pkce: true
allowed_challenge_methods:
- S256
# 令牌配置
access_token:
lifetime: 3600
algorithm: RS256
issuer: "https://auth.example.com"
refresh_token:
lifetime: 2592000
rotation: true
# 客户端认证
client_authentication:
methods:
- client_secret_basic
- client_secret_post
- private_key_jwt
# 作用域限制
scopes:
allowed:
- openid
- profile
- email
default: openid
OAuth2授权码流程配合PKCE提供了安全可靠的授权机制,是现代应用授权的标准解决方案。