HTTP基本认证和摘要认证

         今天试了下HTTP认证的资料. 主要是基本认证与摘要认证.
其中基本认证是指 Base64(user:pwd)后,放在Http头的Authorization中发送给服务端来作认证.
用Base64纯只是防君子不防小人的做法。所以只适合用在一些不那么要求安全性的场合。
         不过如果是做WebAPI不是网页,且web服务器是自己可以控制的,其实没必要那么死板。 
我就试了把Base64(AES(user:pwd))后,按基本认证的方式传参数。然后Web服务再依次解码得
到user与pwd做认证,只要两边都约定好,是没什么问题的。
     
至于摘要认证就要复杂点,我把维基百科的上东西拿过来做下笔记:
1. Client  ------->  WebServer
   点链接或发请求
   /
    GET /dir/index.html HTTP/1.0
Host: localhost
   

2. Client  <-------  WebServer
   返加401 "Unauthorized" 响应码
       认证域(realm)
       服务端密码随机数(nonce)
     ///
     HTTP/1.0 401 Unauthorized
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:26:47 GMT
WWW-Authenticate: Digest realm="testrealm@host.com",
                       qop="auth,auth-int",
                       nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                       opaque="5ccc069c403ebaf9f0171e9517f40e41"
Content-Type: text/html
Content-Length: 311
///
3. Client  ------->  WebServer
     返回同样的header,但多加一个认证头包括了响应代码
    //
    GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
                    realm="testrealm@host.com",
                    nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                    uri="/dir/index.html",
                    qop=auth,
                    nc=00000001,
                    cnonce="0a4f113b",
                    response="6629fae49393a05397450978507c4ef1",
                    opaque="5ccc069c403ebaf9f0171e9517f40e41"


4. Client  <-------  WebServer
///
HTTP/1.0 200 OK
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:27:03 GMT
Content-Type: text/html
Content-Length: 7984
///

客户端在后续提交请求时,
   重复用服务器密码随机数(nonce)
   每次产生新的客户端密码随机数(cnonce)
   计数器(nc)++
然后依 保护质量(qop)类型,确定response 值

服务端收到后,依qop类型,用同样的算法,算出值与客户端带过来的response值对比。

nonce过期处理:
当服务端密码随机数nonce过期时,
返回 401,并在认证头中添加stale=TRUE

// ":"作为各个参数的分割符
HA1 : md5("username:realm:pwd")
HA2 : md5("GET:/xx/xx/index.html")

qop:为"auth"或"auth-int"
Response = md5("HA1:nonce:nc:cnonce:qop:HA2")
qop:未指定
Response = md5("HA1:nonce:HA2")


         上面两种认证方式其实都是通过Authenticate传参数,只不过定义不同罢了。要是自己写Web服务,两边约定好。

想怎么定义都行,当然其它web服务器就不认得了。

  我把测试基本认证和加了层AES加密的基本认证的部分实现贴一下:

 启动Web服务

func main() {
	fmt.Println("HTTP认证 服务端 ")

	http.HandleFunc("/", lib.MakeHandleFunc(lib.HTTPGET, lib.HandleTest))

	log.Fatal(http.ListenAndServe(":8080", nil))
}
在这作认证

/**
HTTP身份认证测试
   转Handler函数
Author: XiongChuanLiang
Date:2015-10-28
*/
package lib

import (
	"log"
	"net/http"
)

type HandleFuncType func(http.ResponseWriter, *http.Request)

const (
	HTTPGET  = "GET"
	HTTPPOST = "POST"
)

func MakeHandleFunc(fMethod string, f HandleFuncType) HandleFuncType {
	return func(w http.ResponseWriter, r *http.Request) {

		defer func() {
			if x := recover(); x != nil {
				log.Printf("[%v] panic: %v", r.RemoteAddr, x)
			}
		}()

		if r.Method != fMethod {
			http.Error(w, "调用方法出错.", http.StatusMethodNotAllowed)
		} else {
			//if BasicAuth(r) {
			if AesBasicAuth(r) {
				f(w, r)
			} else {
				http.Error(w, "Auth失败.", http.StatusBadRequest)
			}
		}
	}
}

具体的两种认证方式

// 标准的Http身份认证
func BasicAuth(r *http.Request) bool {

	auth := strings.SplitN(r.Header["Authorization"][0], " ", 2)

	log.Println("[BasicAuth] auth:", auth)
	if len(auth) != 2 || auth[0] != "Basic" {
		log.Println("[BasicAuth] [ERROR] Authorization参数传过来的格式不对!")
		return false
	}
	payload, _ := base64.StdEncoding.DecodeString(auth[1])
	fmt.Println("[BasicAuth] Auth payload:", string(payload))

	userInfo := strings.SplitN(string(payload), ":", 2)
	if len(userInfo) != 2 {
		log.Println("[BasicAuth] [ERROR] Authorization参数少了!")
		return false
	}

	//检验身份
	return Validate(userInfo[0], userInfo[1])
}

// 如果只是设Web Api可用,不适应于网页.
// Client:将user:password用AES加密后,再用Base64发送过来
// Server:Base64解密,AES解密
func AesBasicAuth(r *http.Request) bool {
	auth := strings.SplitN(r.Header["Authorization"][0], " ", 2)
	log.Println("[AesBasicAuth] auth:", auth)

	if len(auth) != 2 || auth[0] != "Basic" {
		log.Println("[AesBasicAuth] [ERROR] Authorization参数传过来的格式不对!")
		return false
	}

	
	payload := strings.TrimSpace(auth[1])
	uDec, _ := base64.URLEncoding.DecodeString(payload)
	origData2, err2 := util.AesDecrypt(uDec, util.AesKey16)
	if err2 != nil {
		panic(err2)
	}
	log.Println("[AesBasicAuth] origData:", string(origData2))
	

	loginInfo := strings.SplitN(string(origData2), ":", 2)
	if len(loginInfo) != 2 {
		log.Println("[AesBasicAuth] [ERROR] Authorization参数少了!")
		return false
	}

	//检验身份
	return Validate(loginInfo[0], loginInfo[1])
}

客户端的测试代码:

 

/**
HTTP身份认证测试
   客户端
Author: XiongChuanLiang
Date:2015-10-28

*/

package main

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"

	"../util"
)

var (
	User string = "xcl"
	Pwd  string = "pwd"

	TestUrl string = "http://localhost:8080/test?cmdtype=testauth&p1=xcl" //get
)

func main() {
	fmt.Println("HTTP认证 客户端")

	testBaseAuth()
	//testAesBaseAuth()
}

// 标准的Http身份认证
func testBaseAuth() {
	client := &http.Client{}
	body := bytes.NewBuffer([]byte("Hello"))

	req, _ := http.NewRequest("GET", TestUrl, body)
	req.Header.Set("Content-Type", "application/json;charset=utf-8")
	req.SetBasicAuth(User, Pwd)

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("[testBaseAuth] err:", err)
		return
	}
	defer resp.Body.Close()

	data, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("[testBaseAuth] resp.Body:", string(data))
}

// 如果只是设Web Api可用,不适应于网页.
// Client:将user:password用AES加密后,再用Base64发送过来
// Server:Base64解密,AES解密
func testAesBaseAuth() {
	client := &http.Client{}
	body := bytes.NewBuffer([]byte("Hello"))

	req, _ := http.NewRequest("GET", TestUrl, body)
	req.Header.Set("Content-Type", "application/json;charset=utf-8")

	//
	f1 := func() string {
		auth := fmt.Sprintf("%s:%s", User, Pwd)
		//test
		//uEnc := base64.URLEncoding.EncodeToString([]byte(auth))
		if aesData, err := util.AesEncrypt([]byte(auth), util.AesKey16); err != nil {
			panic(err)
		} else {
			return base64.URLEncoding.EncodeToString(aesData)
		}
	}

	//自己构建Authorization
	//Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", f1()))
	//

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("[testAesBaseAuth] err:", err)
		return
	}
	defer resp.Body.Close()

	data, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("[testAesBaseAuth] resp.Body:", string(data))
}
  上面是测试的部份代码,纯只是验证下而已,实际中估计很少有人这么做。


BLOG:http://blog.csdn.net/xcl168



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值