Java调用OKTA token端点获取access token返回401未授权问题求助
OKTA授权码兑换AccessToken 401错误修复方案
已定位的代码问题
- 拼接请求参数时使用了中文全角双引号,Java无法正确识别字符串
- 表单参数之间缺失分隔符
&,导致OKTA服务端无法正确解析请求参数 redirect_uri参数值缺失主机地址,且未保证和/authorize请求中的redirect_uri完全一致- 所有请求参数值未做URL编码,参数含特殊字符时会出现解析异常
- Basic认证的Base64编码未指定UTF-8字符集,不同环境默认字符集差异会导致认证信息错误
- 未处理错误响应流,4xx/5xx响应直接调用
getInputStream()会抛出IO异常,无法获取OKTA返回的具体错误信息
修复后的代码
import java.net.URLEncoder; import java.nio.charset.StandardCharsets; // 省略其他原有代码 String oktaURL = "https://xxx.oktapreview.com/oauth2/default/v1/token"; // 对每个参数值单独做URL编码,避免特殊字符问题 String encodedClientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name()); String encodedGrantType = URLEncoder.encode("authorization_code", StandardCharsets.UTF_8.name()); // 这里替换为你实际的redirect_uri,必须和/authorize接口传的完全一致 String redirectUri = "http://localhost:8192/app"; String encodedRedirectUri = URLEncoder.encode(redirectUri, StandardCharsets.UTF_8.name()); String encodedOktaCode = URLEncoder.encode(oktaCode, StandardCharsets.UTF_8.name()); // 参数之间用&分隔,使用半角英文引号 String urlParameters = "client_id=" + encodedClientId + "&grant_type=" + encodedGrantType + "&redirect_uri=" + encodedRedirectUri + "&code=" + encodedOktaCode; URL url1 = new URL(oktaURL); StringBuffer response = null; String output1; BufferedReader inputBuff = null; log.info("The url to get the access token:"+url1.toString()); if (url1.getProtocol() != null && url1.getProtocol().startsWith("https")){ HttpsURLConnection httpsClient = (HttpsURLConnection) url1.openConnection(); httpsClient.setRequestMethod("POST"); httpsClient.setRequestProperty("Accept","application/json"); // Base64编码指定UTF-8字符集 String authStr = clientId + ":" + clientSecret; String encodedAuth = Base64.getEncoder().encodeToString(authStr.getBytes(StandardCharsets.UTF_8)); httpsClient.setRequestProperty("Authorization", "Basic " + encodedAuth); httpsClient.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); httpsClient.setInstanceFollowRedirects(false); log.info ("Send the POST request"); // Send post request httpsClient.setDoOutput(true); try (DataOutputStream opStream = new DataOutputStream(httpsClient.getOutputStream())) { opStream.writeBytes(urlParameters); opStream.flush(); } // 先判断响应码,错误响应读取errorStream int responseCode = httpsClient.getResponseCode(); InputStream inputStream; if (responseCode >= 200 && responseCode < 300) { inputStream = httpsClient.getInputStream(); } else { inputStream = httpsClient.getErrorStream(); // 打印错误信息方便排查 log.error("Request failed with response code: {}", responseCode); } inputBuff = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); log.info("Read from the input stream"); response = new StringBuffer(); while ((output1 = inputBuff.readLine()) != null) { response.append(output1); } inputBuff.close(); } if (response != null) { String theString = response.toString(); log.info("Response content: {}", theString); }
额外校验项
如果修改代码后仍然报错,可按以下顺序排查:
- 确认client_secret值正确,未出现拼写错误、前后多余空格的情况
- 确认授权码
oktaCode未被重复使用,OKTA授权码只能使用一次,有效期只有几分钟 - 确认OKTA应用配置中已开启
authorization_code授权模式,且redirect_uri已加入白名单
内容的提问来源于stack exchange,提问作者AAPJ
相关产品推荐
相关产品推荐

