HTTP diest认证
一、介绍:
可用于访问资源时的身份认证。当客户端未通过服务器的认证,服务器返回401,并传递WWW-Authenticate信息,客户端可通过WWW-Authenticate,生成response返回给服务器来实现认证。
二、实例:
1)客户端发起一次请求,报文如下:
引用
GET / HTTP/1.1
Host: 192.168.7.202:7547
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
2)服务器响应401,报文如下:
引用
HTTP/1.1 401 Unauthorized
Content-Length: 0
Connection: close
WWW-Authenticate: Digest realm="realm@easycwmp",qop="auth",nonce="c10c9897f05a9aee2e2c5fdebf03bb5b0001b1ef",
opaque="328458fab28345ae87ab3210a8513b14eff452a2"
3)客户端根据WWW-Authenticate生成response,报文如下:
引用
GET / HTTP/1.1
Host: 192.168.7.202:7547
Connection: keep-alive
Authorization: Digest username="povodo", realm="realm@easycwmp", qop=auth, nonce="c10c9897f05a9aee2e2c5fdebf03bb5b0001b1ef", opaque="328458fab28345ae87ab3210a8513b14eff452a2", uri="/", response="77d8c3e7ad338e86b8d039867651b65f", nc=00000001, cnonce="d5324153548c43d8"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
4)服务器响应:报文如下:
引用
HTTP/1.1 200 OK
Content-Length: 0
一次完整的认证完成了。
三、认证方法实现(JAVA):
加密算法:
//我用的是commons-codec-1.9.jar包
import org.apache.commons.codec.binary.Hex;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.SecureRandom;
public class Digests {
/**
* 加密遵循RFC2671规范
* 将相关参数加密生成一个MD5字符串,并返回
*/
public static String http_da_calc_HA1(String username, String realm, String password,
String nonce, String nc, String cnonce, String qop,
String method, String uri, String algorithm){
String HA1,HA2;
if ("MD5-sess".equals(algorithm)){
HA1 = HA1_MD5_sess(username,realm,password,nonce,cnonce);
} else{
HA1 = HA1_MD5(username,realm,password);
}
byte[] md5Byte = md5(HA1.getBytes());
HA1 = new String(Hex.encodeHex(md5Byte));
md5Byte = md5(HA2(method, uri).getBytes());
HA2 = new String(Hex.encodeHex(md5Byte));
String original = HA1 + ":" + (nonce +":"+ nc +":"+ cnonce +":"+ qop) + ":" + HA2;
md5Byte = md5(original.getBytes());
return new String(Hex.encodeHex(md5Byte));
}
/**
* algorithm值为MD5时规则
*/
private static String HA1_MD5(String username, String realm, String password){
return username+":"+realm+":"+password;
}
/**
* algorithm值为MD5-sess时规则
*/
private static String HA1_MD5_sess(String username, String realm, String password, String nonce, String cnonce){
// MD5(username:realm:password):nonce:cnonce
String s = HA1_MD5(username, realm, password);
byte[] md5Byte = md5(s.getBytes());
String smd5 = new String(Hex.encodeHex(md5Byte));
return smd5+":"+nonce+":"+cnonce;
}
private static String HA2(String method, String uri){
return method+":"+uri;
}
/**
* 对输入字符串进行md5散列.
*/
public static byte[] md5(byte[] input) {
return digest(input, MD5, null, 1);
}
/**
* 对字符串进行散列, 支持md5与sha1算法.
*/
private static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
try {
MessageDigest digest = MessageDigest.getInstance(algorithm);
if (salt != null) {
digest.update(salt);
}
byte[] result = digest.digest(input);
for (int i = 1; i < iterations; i++) {
digest.reset();
result = digest.digest(result);
}
return result;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
//测试
public static void main(String[] args) {
String s = http_da_calc_HA1("povodo", "realm@easycwmp", "povodo",
"c10c9897f05a9aee2e2c5fdebf03bb5b0001b1ef", "00000001", "d5324153548c43d8", "auth",
"GET", "/", "MD5");
System.out.println("加密后response为:"+s);
}
四、客户端请求(GET方式示例):
//我这里用的fastjson-1.1.41.jar
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.povodo.map.servlet.StringUtils;
import org.apache.commons.codec.binary.Hex;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class HttpRequestUtils {
static int nc = 0; //调用次数
/**
* 向指定URL发送GET方法的请求
* @param url 发送请求的URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @param username 验证所需的用户名
* @param password 验证所需的密码
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param, String username, String password) {
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try {
String wwwAuth = sendGet(url, param); //发起一次授权请求
if (wwwAuth.startsWith("WWW-Authenticate:")) {
wwwAuth = wwwAuth.replaceFirst("WWW-Authenticate:", "");
} else {
return wwwAuth;
}
nc ++;
String urlNameString = url + (StringUtils.isNotEmpty(param) ? "?" + param : "");
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
//授权信息
String authentication = getAuthorization(wwwAuth, realUrl.getPath(), username, password);
connection.setRequestProperty("Authorization", authentication);
// 建立实际的连接
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
nc = 0;
} catch (Exception e) {
nc = 0;
throw new RuntimeException(e);
} finally {
try {
if (in != null) in.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result.toString();
}
/**
* 生成授权信息
* @param authorization 上一次调用返回401的WWW-Authenticate数据
* @param username 用户名
* @param password 密码
* @return 授权后的数据, 应放在http头的Authorization里
* @throws IOException 异常
*/
private static String getAuthorization(String authorization, String uri, String username, String password) throws IOException {
uri = StringUtils.isEmpty(uri) ? "/" : uri;
String temp = authorization.replaceFirst("Digest", "").trim();
String json = "{\"" + temp.replaceAll("=", "\":").replaceAll(",", ",\"") + "}";
JSONObject jsonObject = JSON.parseObject(json);
String cnonce = new String(Hex.encodeHex(Digests.generateSalt(8))); //客户端随机数
String ncstr = ("00000000" + nc).substring(Integer.toString(nc).length()); //认证的次数,第一次是1,第二次是2...
String algorithm = jsonObject.getString("algorithm");
String response = Digests.http_da_calc_HA1(username, jsonObject.getString("realm"), password,
jsonObject.getString("nonce"), ncstr, cnonce, jsonObject.getString("qop"),
"GET", uri, algorithm);
//组成响应authorization
authorization = "Digest username=\"" + username + "\"," + temp;
authorization += ",uri=\"" + uri
+ "\",nc=\"" + ncstr
+ "\",cnonce=\"" + cnonce
+ "\",response=\"" + response+"\"";
return authorization;
}
/**
* 向指定URL发送GET方法的请求
*
* @param url 发送请求的URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param) {
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try {
String urlNameString = url + (StringUtils.isNotEmpty(param) ? "?" + param : "");
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
//返回401时需再次用用户名和密码请求
//此情况返回服务器的 WWW-Authenticate 信息
if (((HttpURLConnection) connection).getResponseCode() == 401) {
Map<String, List<String>> map = connection.getHeaderFields();
return "WWW-Authenticate:" + map.get("WWW-Authenticate").get(0);
}
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
} catch (Exception e) {
throw new RuntimeException("get请求发送失败",e);
}
// 使用finally块来关闭输入流
finally {
try {
if (in != null) in.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result.toString();
}
}
相关代码已上传附件。
如有错误,欢迎指正!
5、参考:
详解HTTP中的摘要认证机制
RFC 2017文档说明
分享到:
相关推荐
在RFC 2617中文版中,还对HTTP Digest Authentication的其他方面进行了详细的描述,例如,访问鉴别框架、基本鉴别方案和摘要报头的规范等。 访问鉴别框架是指在HTTP协议中对鉴别的框架结构,该框架包括客户端、...
“摘要”式认证( Digest authentication)是一个简单的认证机制,最初是为HTTP协议开发的,因而也常叫做HTTP摘要,在RFC2671中描述。其身份验证机制很简单,它采用杂凑式(hash)加密方法,以避免用明文传输用户的...
根据给定的文件标题、描述、标签以及部分内容,本文将详细介绍MD5加密算法在Java源文件中的实现原理及其代码逻辑。 ### MD5算法概述 MD5(Message-Digest Algorithm 5)是一种广泛使用的散列算法,它能将任意长度...
RFC3261是SIP的核心规范,它定义了SIP的语法、语义以及交互过程,是理解和实现SIP系统的基础。这份中文文档对于那些英语不是母语,但在开发SIP相关应用的人员来说,无疑提供了极大的便利。 SIP的基本概念包括: 1....
综上所述,本文档主要关注的是Java编程语言中MD5加密算法的应用,特别是通过实现KeyBean类来执行RFC 1321中的消息摘要算法。通过理解MD5算法的基本原理及其在Java中的实现方式,开发者可以更好地利用这种技术来确保...
- **HTTP鉴权**:使用HTTP Basic或Digest鉴权机制。 - **S/MIME**:使用加密和数字签名来保护消息内容。 - **TLS**:使用SSL/TLS来加密传输层通信。 #### 十七、SDP的Offer/Answer模式 SDP(Session Description ...
在Java中,可以利用`java.security.MessageDigest`类来实现MD5加密。 在提供的Java代码中,`StringUtil`类包含了一个`MD5Encode`方法,用于对字符串进行MD5加密。首先,它获取`MessageDigest`实例,指定算法为"MD5...
在给定的部分内容中,包含了一段Java代码示例,展示了如何使用Java实现MD5加密。 ```java public String EncoderByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException { // 获取MD5...
MD5算法的标准规范由RFC 1321文档定义,并提供了一组测试用例来验证实现的正确性。 #### MD5算法的关键特点 1. **唯一性**:对于不同的输入,其输出应该是不同的。 2. **不可逆性**:通过散列值很难推导出原始的...
根据给定的信息,“MD5Util”是指一个用于实现MD5加密功能的工具类。这类工具通常封装了对MD5算法的调用,并提供了简单的API供开发者使用。下面我们来详细探讨MD5Util可能包含的一些关键知识点: ##### 2.1 常用...
keyBean 类实现了RSA Data Security, Inc.在提交给IETF 的RFC1321中的keyBean message-digest算法。
这三个算法的描述和c语言源代码在Internet RFC 1321中有详细的描述(http://www.ietf.org/rfc/rfc1321.txt),这是一份最权威的文档,由Ronald L. Rivest在1992年8月向IETF提交。 希望能够给你的加密工作带来一些为...
在JSP中实现MD5加密方法涉及了多个方面的知识点,包括JSP基础、MD5加密技术原理以及Java编程技巧。下面将详细介绍这些知识点。 首先,JSP(Java Server Pages)是一种动态网页技术,允许开发者将Java代码嵌入到HTML...
简单的java加密算法有: ... Base64是网络上常见的用于传输8Bit字节代码的编码方式之一,大家可以查看RFC2045~RFC2049,上面有MIME的详细规范。Base64编码可用于在HTTP环境下传递较长的标识信息。例如,在Jav
Apache Commons Codec库并未直接提供SHA算法,但可以通过Java内置的java.security.MessageDigest类进行SHA加密。 五、Metaphone和Soundex: Metaphone和Soundex是两种音译编码算法,主要用于英文单词的相似性比较,...
4. **Cookie组件**:处理HTTP Cookie,支持RFC 6265规范。 5. **Redirect组件**:处理重定向逻辑,支持自动或手动重定向。 6. **Protocol组件**:定义了HTTP协议的实现,如HTTP/1.1和HTTP/2。 7. **Security组件**:...
4. **Cookie管理**:HttpClient 4.5支持Cookie规范,包括RFC6265,提供了`CookieStore`和`CookieSpecs`接口,方便处理和管理服务器返回的Cookie。 5. **认证与安全**:HttpClient支持多种认证机制,如Basic Auth、...
这个版本包含了对各种编码标准和算法的实现,使得开发者能够更方便地进行数据转换和信息加密。 Apache Commons Codec库的核心功能包括: 1. **Base64编码和解码**:Base64是一种用于将任意二进制数据转换为ASCII...