paascloud/paascloud-master · error · UnapprovedClientAuthenticationException
clientSecret不匹配:
Error message
clientSecret不匹配:
What it means
Thrown by PcAuthenticationSuccessHandler.onAuthenticationSuccess when the stored ClientDetails secret does not equal the clientSecret decoded from the Authorization header. Message is "clientSecret不匹配:" plus the clientId, signaling a failed OAuth2 client credential check after successful form login.
Solutions
- Regenerate the Authorization header using the exact current clientId:clientSecret stored in uac_oauth_client_details
- Check whether stored secrets are encrypted (PasswordEncoder) and that comparison logic matches the storage scheme
- Trim whitespace/newlines from the secret in config and when base64-encoding
- Sync frontend client credentials with the environment's DB (dev/prod secrets differ)
Example fix
// before String basic = clientId + ":" + oldSecret; // after String basic = clientId + ":" + currentSecretFromConfig.trim(); String header = "Basic " + Base64.getEncoder().encodeToString(basic.getBytes(StandardCharsets.UTF_8));
Defensive patterns
Strategy: validation
Validate before calling
String expected = jdbc.queryForObject("SELECT client_secret FROM uac_oauth_client_details WHERE client_id=?", String.class, clientId);
if (!Objects.equals(expected, mySecret.trim())) { /* refresh credentials before calling login */ } Try / catch
try { return authClient.login(user, pwd); } catch (UnapprovedClientAuthenticationException e) { if (e.getMessage().contains("clientSecret不匹配")) { refreshClientCredentials(); } throw e; } Prevention
- Rotate client secrets on both DB and client config in one coordinated change
- Trim secrets and use a single encoding (UTF-8) when base64-encoding the header
- Know whether secrets are stored encrypted/BCrypt and compare with the matching checker
- Keep dev/prod client credentials in separate config files to avoid cross-env secrets
When it happens
Trigger: The base64 clientSecret in the Authorization header differs from the client_secret stored for that clientId in the client details store (plain comparison via StringUtils.equals, so encoding differences also trigger it).
Common situations: Client rotated its secret in the DB but the frontend still ships the old one; secret stored encrypted/BCrypt in DB while compared plainly (or vice versa); copy-paste with whitespace; different secret per environment.
Related errors
AI-assisted analysis of paascloud/paascloud-master@781281a950 (2026-09-10).
Data as JSON: /api/errors/176a63fa1513f8b1.
Report an issue: GitHub.
Appendix: source
Thrown at paascloud-provider/paascloud-provider-uac/src/main/java/com/paascloud/provider/security/PcAuthenticationSuccessHandler.java:70
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header == null || !header.startsWith(BEARER_TOKEN_TYPE)) {
throw new UnapprovedClientAuthenticationException("请求头中无client信息");
}
String[] tokens = RequestUtil.extractAndDecodeHeader(header);
assert tokens.length == 2;
String clientId = tokens[0];
String clientSecret = tokens[1];
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
if (clientDetails == null) {
throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在:" + clientId);
} else if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) {
throw new UnapprovedClientAuthenticationException("clientSecret不匹配:" + clientId);
}
TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), "custom");
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
SecurityUser principal = (SecurityUser) authentication.getPrincipal();
uacUserService.handlerLoginData(token, principal, request);
log.info("用户【 {} 】记录登录日志", principal.getUsername());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write((objectMapper.writeValueAsString(WrapMapper.ok(token))));
}View on GitHub (pinned to 781281a950)