OkHttp本身并不直接支持Digest认证,可以通过拦截器(Interceptor)来手动实现Digest认证的逻辑。
1、自定义Digest认证拦截器
@Slf4j
public class DigestAuthInterceptor implements Interceptor {
@NotNull
@Override
@SneakyThrows
public Response intercept(@NotNull Chain chain) {
Request original = chain.request();
String ha1 = calculateHA1(USERNAME, REALM, PASSWORD);
String method = original.method();
String url = original.url().toString();
String ha2 = calculateHA2(method, url);
String nonce = generateNonce();
String cNonce = generateNonce();
String response = calculateResponse(ha1, nonce, NONCE_COUNT, cNonce, QOP, ha2);
//构建Authorization头信息
String authHeader = "Digest username=\"" + USERNAME + "\",realm=\"" + REALM + "\",nonce=\"" + nonce + "\",uri=\"" + url + "\",nc=" + NONCE_COUNT + ",cnonce=\"" + cNonce + "\",qop=" + QOP + ",response=\"" + response + "\"";
//创建带有Authorization的新请求
Request authRequest = original.newBuilder().header(AUTH_HEADER_KEY, authHeader).build();
return chain.proceed(authRequest);
}
private static final String AUTH_HEADER_KEY = "Authorization";
private static final String USERNAME = "name";
private static final String PASSWORD = "password";
private static final String REALM = "Digest";
private static final String QOP = "auth";
private static final String NONCE_COUNT = "00000001";
private static final String ALGORITHM = "MD5";
/**
* 计算HA1
*
* @param userName
* @param realm
* @param pwd
* @return
* @throws NoSuchAlgorithmException
*/
private String calculateHA1(String userName,String realm, String pwd) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(ALGORITHM);
md.update((userName + ":" + realm + ":" + pwd).getBytes(StandardCharsets.UTF_8));
return bytesToHex(md.digest());
}
/**
* 计算HA2
*
* @param method
* @param uri
* @return
* @throws NoSuchAlgorithmException
*/
private String calculateHA2(String method, String uri) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(ALGORITHM);
md.update((method + ":" + uri).getBytes(StandardCharsets.UTF_8));
return bytesToHex(md.digest());
}
/**
* 计算Response
*
* @param ha1
* @param ha2
* @param nonce
* @param nc
* @param cNonce
* @param qop
* @return
* @throws NoSuchAlgorithmException
*/
private String calculateResponse(String ha1, String nonce, String nc, String cNonce, String qop, String ha2) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(ALGORITHM);
md.update((ha1 + ":" + nonce + ":" + nc + ":" + cNonce + ":" + qop + ":" + ha2).getBytes(StandardCharsets.UTF_8));
return bytesToHex(md.digest());
}
/**
* 生成随机数
* @return
*/
private String generateNonce() {
SecureRandom random = new SecureRandom();
byte[] randomBytes = new byte[16];
random.nextBytes(randomBytes);
return bytesToHex(randomBytes);
}
/**
* 将字节数组转换为16进制字符串
*
* @param hash
* @return
*/
private String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
2、应用
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient
.Builder()
.addInterceptor(new DigestAuthInterceptor())
.build();
Request = request = new Request.Builder().url("http://example.com/protected-resource").build();
Response response = client.newCall(request).execute();
}