{"record":{"id":"9954a51af321eca0","repo":"wuyouzhuguli/SpringAll","slug":"client","errorCode":null,"errorMessage":"请求头中无client信息","messagePattern":"请求头中无client信息","errorType":"exception","errorClass":"UnapprovedClientAuthenticationException","httpStatus":null,"severity":"error","filePath":"64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/handler/MyAuthenticationSucessHandler.java","lineNumber":39,"sourceCode":"import java.util.Base64;\nimport java.util.HashMap;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private ClientDetailsService clientDetailsService;\n    @Autowired\n    private AuthorizationServerTokenServices authorizationServerTokenServices;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {\n        // 1. 从请求头中获取 ClientId\n        String header = request.getHeader(\"Authorization\");\n        if (header == null || !header.startsWith(\"Basic \")) {\n            throw new UnapprovedClientAuthenticationException(\"请求头中无client信息\");\n        }\n\n        String[] tokens = this.extractAndDecodeHeader(header, request);\n        String clientId = tokens[0];\n        String clientSecret = tokens[1];\n\n        TokenRequest tokenRequest = null;\n\n        // 2. 通过 ClientDetailsService 获取 ClientDetails\n        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);\n\n        // 3. 校验 ClientId和 ClientSecret的正确性\n        if (clientDetails == null) {\n            throw new UnapprovedClientAuthenticationException(\"clientId:\" + clientId + \"对应的信息不存在\");\n        } else if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) {\n            throw new UnapprovedClientAuthenticationException(\"clientSecret不正确\");\n        } else {\n            // 4. 通过 TokenRequest构造器生成 TokenRequest","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/wuyouzhuguli/SpringAll/blob/614d2578d9495acf53cc02f2dee9c6131cc5e51a/64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/handler/MyAuthenticationSucessHandler.java#L21-L57","documentation":"UnapprovedClientAuthenticationException thrown in MyAuthenticationSucessHandler.onAuthenticationSuccess when the request has no 'Authorization' header or the header does not start with 'Basic '. This handler mints an OAuth2 access token after a successful form/mobile login by treating the caller as an OAuth2 client, so it requires HTTP Basic client credentials (base64(clientId:clientSecret)).","triggerScenarios":"A successful login request that lacks the Authorization header, or sends a Bearer token instead of Basic; the header is present but not prefixed with 'Basic ' (case/spacing sensitive, note the trailing space).","commonSituations":"SPA/mobile client logged in without embedding client credentials; developer used a Bearer token for the login call; the gateway stripped the Authorization header; header capitalization or missing space after 'Basic'.","solutions":["Send 'Authorization: Basic <base64(clientId:clientSecret)>' on the login request that triggers this success handler.","Confirm the header literal: must start with 'Basic ' (capital B, one trailing space) before the base64 payload.","Do not send a Bearer token for the login itself; the Basic client header is separate from the resulting access token.","Ensure no proxy/gateway strips Authorization on the way in."],"exampleFix":"// before\n// fetch('/authentication/login', { method:'POST', body: form })\n\n// after\nconst basic = btoa('clientId:clientSecret');\nfetch('/authentication/login', {\n  method: 'POST',\n  headers: { 'Authorization': 'Basic ' + basic },\n  body: form\n});","handlingStrategy":"validation","validationCode":"// Always attach the Basic client header for the login that triggers this handler.\nfunction authHeader(clientId, secret) {\n  return 'Basic ' + btoa(`${clientId}:${secret}`);\n}\nfetch('/authentication/form', { method:'POST', headers:{ Authorization: authHeader(CID, CSECRET) }, body: form });","typeGuard":null,"tryCatchPattern":"try { await login(); }\ncatch (e) {\n  if (/请求头中无client信息/.test(e.message)) attachBasicClientHeader();\n  else handleError(e);\n}","preventionTips":["Centralize client-credential header construction in one client helper.","Ensure no proxy/gateway strips Authorization.","Send Basic for the login request; do not confuse it with the resulting Bearer token."],"tags":["oauth2","spring-security","authentication","client","http-headers"],"backgroundTag":null,"analyzedSha":"614d2578d9495acf53cc02f2dee9c6131cc5e51a","analyzedAt":"2026-08-14T04:40:03.488Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}