cookie、session、jwt都是用于HTTP用户认证的方式,最近在使用所以再次熟悉一下
Why?
HTTP是无状态的,无法获知用户的身份,所以就需要有一种手段可以实现能获知每个HTTP请求发起的用户是谁,还要能区分每个不同的用户。
Cookie
Cookie是保存用户信息的一种最简单的方式,cookie中可以保存用户的一些信息,存放在浏览器的本地存储中,并随着每次请求发送到服务器。
- 用户首次认证并登陆服务后,服务返回给用户的HTTP中带着一个服务器生成的cookie信息,浏览器会自动保存到本地,带用户下次再访问该网站时会自动带上该cookie;
- 不同的网站会发送不同的cookie,cookie和域名绑定,并能指定path;
- cookie有超时机制,如果超时需要重新认证再让服务方重新分配一个cookie;
- cookie可以指定secure=true属性,指定后只有https才能使用该cookie;
- cookei还可以指定httponly=true属性,可以防止js拿到cookie,只有服务器可以操作cookie
不足:
- 最大的原因是因为它存储在浏览器端,一些别有用心的人能够通过浏览器截获cookie(脚本、利用工具抓取等);
- Cookie在网络中很容易被劫持,这个问题需要用https解决;
- Cookie的失效时间是不受服务端控制的,用户可以修改;
- 同一站点能设置的cookie有限。
Session
Session是比cookie安全高的另一种会话保持技术,Session把信息存储的服务器端,在浏览器端只需要存储一个session id,服务端通过用户请求中带的session id自动获取用户信息。
session id虽然存储在cookie中,但所有用户信息都存放在服务端,通常公司对自己的用户信息都有更强的安全保护;
服务端可以存储用户大量信息,用户可以用一次登录就可以免密登录同一个公司的不同服务和网站;
session id通常会使用HMAC生成,并结合cookie使用,较为灵活,兼容性较好,防止篡改。
不足:
- 服务端存储session id需要承受更高的计算成本;
- 负载均衡需要考虑共享session机制;
- 查询session id是个查库操作,用户很多时会有性能问题;
- 无法跨域。
jwt
jwt是基于token验证机制的一种,目前受大多数网站和框架支持。jwt的原理是用户认证后发一个jwt token给用户,其中包含用户的信息,用户后续请求带上这个jwt,服务端就可以自动解析出用户信息。
jwt有三部分组成,都使用json格式:
Header
Payload
Signature
其中Header通常是:
{
"alg": "HS256",
"typ": "JWT"
}
Payload保存用户信息,比较随意,但通常是用户不铭感的信息:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Signature是根据jwt签名算法+服务端自定义的秘钥生成的hash信息:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
注意,header和payload部分都没有加密,所以不要存放铭感信息。
最后把三部分放在一起并使用BASE64_URL转义得到:
![87ea1909f57b6192f158e9236a65ef09.png](https://i-blog.csdnimg.cn/blog_migrate/af178d259e1103f2f66016e6c80578ac.jpeg)
golang版本实现如下:
1. header:结构固定,json.Mashal后再encode64
2. playload:和header一样,先json.Mashal后再base64UrlEncode
```
// Generate the signing string. This is the
// most expensive part of the whole deal. Unless you
// need this for something special, just go straight for
// the SignedString.
func (t *Token) SigningString() (string, error) {
var err error
parts := make([]string, 2)
for i, _ := range parts {
var jsonValue []byte
if i == 0 {
if jsonValue, err = json.Marshal(t.Header); err != nil {
return "", err
}
} else {
if jsonValue, err = json.Marshal(t.Claims); err != nil {
return "", err
}
}
parts[i] = EncodeSegment(jsonValue)
}
return strings.Join(parts, "."), nil
}
```
3. Signature:hash(playload,key)
```
// Implements the Sign method from SigningMethod for this signing method.
// Key must be []byte
func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) {
if keyBytes, ok := key.([]byte); ok {
if !m.Hash.Available() {
return "", ErrHashUnavailable
}
hasher := hmac.New(m.Hash.New, keyBytes)
hasher.Write([]byte(signingString))
return EncodeSegment(hasher.Sum(nil)), nil
}
return "", ErrInvalidKeyType
}
```
4. 最后,以上三部分join by .
jwt可以存放在cookie或者header中,推荐的做法是放到header,可以实现跨域:
Authorization: Bearer <token>
jwt解决了跨域、降低了服务端session开销问题,为分布式服务扫清了障碍,但也无法解决劫持问题,需要配合https才能实现更高的安全性。
引用:
https://jwt.io/introduction/jwt.io https://github.com/dgrijalva/jwt-gogithub.com