Java调用Duo Admin API添加Authorization和Date头后401错误如何解决
错误根因
- 认证方式不符合Duo规范:Duo Admin API不使用普通的账号密码Basic认证,要求基于HMAC-SHA1的签名认证,你直接对用户名密码做Base64生成的Authorization头不被识别
- 请求路径缺失:创建用户的接口路径为
/admin/v1/users,你当前只请求了API根域名,没有指定接口路径 - 请求体格式错误:Duo Admin API接收
application/x-www-form-urlencoded格式的参数,你传递的JSON格式参数无法被解析
修复后可运行代码
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.util.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class DuoAdminAPI { // 替换为你自己Duo后台的对应值 private static final String IKEY = "你的Integration Key"; private static final String SKEY = "你的Secret Key"; private static final String API_HOST = "api-e9770554.duosecurity.com"; private static final String HMAC_ALGORITHM = "HmacSHA1"; // 字节数组转十六进制字符串工具 private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } // 生成Duo要求的签名 private static String generateDuoSignature(String date, String method, String path, String params) throws Exception { String canonicalString = String.join("\n", date, method.toUpperCase(), API_HOST.toLowerCase(), path, params ); Mac mac = Mac.getInstance(HMAC_ALGORITHM); mac.init(new SecretKeySpec(SKEY.getBytes(), HMAC_ALGORITHM)); return bytesToHex(mac.doFinal(canonicalString.getBytes())); } public static void POSTRequest() throws Exception { String dateTime = OffsetDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME); String path = "/admin/v1/users"; // 表单格式参数,创建用户必填参数可根据需求调整 String postParams = "username=testuser&email=test@example.com&realname=测试用户"; String signature = generateDuoSignature(dateTime, "POST", path, postParams); // 生成符合要求的Authorization头 String authString = IKEY + ":" + signature; String basicAuth = "Basic " + Base64.getEncoder().encodeToString(authString.getBytes()); URL obj = new URL("https://" + API_HOST + path); HttpURLConnection postConnection = (HttpURLConnection) obj.openConnection(); postConnection.setRequestMethod("POST"); postConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); postConnection.setRequestProperty("Authorization", basicAuth); postConnection.setRequestProperty("Date", dateTime); postConnection.setDoOutput(true); OutputStream os = postConnection.getOutputStream(); os.write(postParams.getBytes()); os.flush(); os.close(); int responseCode = postConnection.getResponseCode(); System.out.println("POST响应码: " + responseCode); System.out.println("POST响应消息: " + postConnection.getResponseMessage()); BufferedReader in = new BufferedReader(new InputStreamReader( responseCode >= 200 && responseCode <300 ? postConnection.getInputStream() : postConnection.getErrorStream() )); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); System.out.println("响应内容: " + response.toString()); } public static void main(String[] args) throws Exception { POSTRequest(); } }
注意事项
- 运行前务必替换
IKEY、SKEY为你在Duo Admin后台创建集成时拿到的实际密钥,不要直接使用示例值 - 确认你的Duo Admin集成已经开启了用户写入权限,否则依然会报权限错误
- 确保运行代码的服务器本地时间和标准时间误差不超过5分钟,否则会出现签名验证失败的问题
- 如果需要传递更多用户参数,可以调整postParams里的键值对,参数名要和接口要求完全一致
内容的提问来源于stack exchange,提问作者Jhonnysins
相关产品推荐
相关产品推荐

