Golang实现HTTP Basic Authorization协议

5年以前  |  阅读数:2035 次  |  编程语言:Golang 

关于HTTP,基本认证

在HTTP中,基本认证(Basic access authentication)是一种用来允许网页浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式。

优点

基本认证的一个优点是基本上所有流行的网页浏览器都支持基本认证。基本认证很少在可公开访问的互联网网站上使用,有时候会在小的私有系统中使用(如路由器网页管理接口)。后来的机制HTTP摘要认证是为替代基本认证而开发的,允许密钥以相对安全的方式在不安全的通道上传输。

程序员和系统管理员有时会在可信网络环境中使用基本认证,使用Telnet或其他明文网络协议工具手动地测试Web服务器。这是一个麻烦的过程,但是网络上传输的内容是人可读的,以便进行诊断。

缺点

虽然基本认证非常容易实现,但该方案创建在以下的假设的基础上,即:客户端和服务器主机之间的连接是安全可信的。特别是,如果没有使用SSL/TLS这样的传输层安全的协议,那么以明文传输的密钥和口令很容易被拦截。该方案也同样没有对服务器返回的信息提供保护。

现存的浏览器保存认证信息直到标签页或浏览器被关闭,或者用户清除历史记录。HTTP没有为服务器提供一种方法指示客户端丢弃这些被缓存的密钥。这意味着服务器端在用户不关闭浏览器的情况下,并没有一种有效的方法来让用户注销。

原理与过程

这一个典型的HTTP客户端和HTTP服务器的对话,服务器安装在同一台计算机上(localhost),包含以下步骤:

  • 客户端请求一个需要身份认证的页面,但是没有提供用户名和口令。这通常是用户在地址栏输入一个URL,或是打开了一个指向该页面的链接。
  • 服务端响应一个401应答码,并提供一个认证域。
  • 接到应答后,客户端显示该认证域(通常是所访问的计算机或系统的描述)给用户并提示输入用户名和口令。此时用户可以选择确定或取消。
  • 用户输入了用户名和口令后,客户端软件会在原先的请求上增加认证消息头(值是base64encode(username+":"+password)),然后重新发送再次尝试。
  • 如果服务器接受了该认证屏幕并返回了页面。如果用户凭据非法或无效,服务器可能再次返回401应答码,客户端可以再次提示用户输入口令。

注意:客户端有可能不需要用户交互,在第一次请求中就发送认证消息头。

package main

import (
    "encoding/base64"
    "fmt"
    "net/http"
    "strings"
)

type Auth func(string, string) bool
type handler http.HandlerFunc

func testAuth(r *http.Request, auth Auth) bool {
    header := r.Header.Get("Authorization")
    s := strings.SplitN(header, " ", 2)
    if len(s) != 2 || s[0] != "Basic" {
        return false
    }
    b, err := base64.StdEncoding.DecodeString(s[1])
    if err != nil {
        return false
    }
    pair := strings.SplitN(string(b), ":", 2)
    if len(pair) != 2 {
        return false
    }
    return auth(pair[0], pair[1])
}

func requireAuth(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("WWW-Authenticate",
        "Basic realm=\"private\"")
    w.WriteHeader(401)
    w.Write([]byte("401 Unauthorized\n"))
}

func wrapAuth(h handler, a Auth) handler {
    return func(w http.ResponseWriter, r *http.Request) {
        if testAuth(r, a) {
            h(w, r)
        } else {
            requireAuth(w, r)
        }
    }
}

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello secret world!")
}

func main() {
    checkPassword := func(_, password string) bool {
        return password == "supersecret"
    }
    handler1 := http.HanderFunc(hello)
    handler2 := wrapAuth(handler1, checkPassword)
    http.ListenAndServe(":5000", handler2)
}
 相关文章:
PHP分页显示制作详细讲解
SSH 登录失败:Host key verification failed
获取IMSI
将二进制数据转为16进制以便显示
获取IMEI
贪吃蛇
文件下载
双位运算符
发送邮件
PHP自定义函数获取搜索引擎来源关键字的方法
Java生成UUID
提取后缀名
年的日历图
在Zeus Web Server中安装PHP语言支持
让你成为最历害的git提交人
Yii2汉字转拼音类的实例代码
再谈PHP中单双引号的区别详解
指定应用ID以获取对应的应用名称
Python 2与Python 3版本和编码的对比
php封装的page分页类完整实例