官方网站:https://jwt.io/
JWT(JSON Web Token)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

组成

一个JWT由3个部分组成:头部(header)、载荷(payload)、签名(signature)。
这三个部分又是由一个分隔符“.” 分割开的。

header

用户说明签名的加密算法等,大概如下:

  1. {
  2. 'alg': "HS256",
  3. 'typ': "JWT"
  4. }

payload

payload(载荷) 就是自定义的数据,结构是一个json或者说是map对象
目前有一个相对标准的payload格式

  • sub(subject): 该JWT所面向的用户,可以放登录的用户名等。
  • iss(issuer): 该JWT的签发者。
  • aud(audience): 该jwt接收者
  • iat(issued at): 该jwt的签发时间
  • exp(expires): 该jwt的过期时间,过期时间必须要大于签发时间
  • nbf(not before):生效时间,token在此时间之前不能被接收处理
  • jti(JWT ID)::JWT ID为web token提供唯一标识

当然你也可以不用这些字段,可以自己随意定义。
注意:在载荷里面不应该加入任何敏感的数据,因为它直接可以通过Base64就能进行解码了。

signature

签名过程:
1.头部和载荷各自base64加密后用.连接起来,然后就形成了xxx.xx的前两段token。
2.最后一段token的形成是,前两段加入一个密匙用HS256算法或者其他算法加密形成。
签名作用:
服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次签名,如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被篡改过了,应该拒绝这个Token,。
[

](https://blog.csdn.net/weixin_42030357/article/details/95629924)

优缺点

优点:

  • 可以减少请求数据库的次数,不需要每次数据接口请求都去访问数据库验证用户的有效性。
  • 可以设置过期时间,在payload中有一个字段叫exp。这个字段可以设置过期时间,如果服务端发现过期则需要中心登录或者验证身份。
  • 在荷载(payload)中其实是可以作为客户端服务器端的信息交换,但是一般不会用这样的操作
  • 服务器不保存session状态,更适合分布式的系统构建。每个请求不用通过hash打到固定的机器上。

    缺点:

  • 因为服务器不保存状态,jwt状态是游离状态那么服务器就不能主动的注销。在到期之前这个token始终有效。一般的解决方法则是使用redis记录token,每次请求判断下如果redis中不存在则过期或者不合法。

  • JWT中的秘钥一旦被泄漏出去,那么任何人都可以冒充别人请求数据了。

作用

Authorization (授权) :

一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。

Information Exchange (信息交换) :

对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

go jwt

  1. // token结构
  2. type Token struct {
  3. Raw string // 保存原始token解析的时候保存
  4. Method SigningMethod // 保存签名方法 目前库里有HMAC RSA ECDSA
  5. Header map[string]interface{} // jwt中的头部
  6. Claims Claims // jwt中第二部分荷载,Claims是一个借口
  7. Signature string // jwt中的第三部分 签名
  8. Valid bool // 记录token是否正确
  9. }
  10. type Claims interface {
  11. Valid() error
  12. }
  13. // 签名方法 所有的签名方法都会实现这个接口
  14. // 具体可以参考https://github.com/dgrijalva/jwt-go/blob/master/hmac.go
  15. type SigningMethod interface {
  16. // 验证token的签名,如果有限返回nil
  17. Verify(signingString, signature string, key interface{}) error
  18. // 签名方法 接受头部和荷载编码过后的字符串和签名秘钥
  19. // 在hmac中key必须是Key must be []byte
  20. // 在rsa中key 必须是*rsa.PrivateKey 对象
  21. Sign(signingString string, key interface{}) (string, error)
  22. // 返回加密方法的名字 比如'HS256'
  23. Alg() string
  24. }
  25. // 新建token
  26. func New(method SigningMethod) *Token {
  27. return NewWithClaims(method, MapClaims{})
  28. }
  29. func NewWithClaims(method SigningMethod, claims Claims) *Token {
  30. // 组成token
  31. return &Token{
  32. Header: map[string]interface{}{
  33. "typ": "JWT",
  34. "alg": method.Alg(),
  35. },
  36. Claims: claims,
  37. Method: method,
  38. }
  39. }

签名创建

  1. // 传入 key 返回token或者error
  2. func (t *Token) SignedString(key interface{}) (string, error) {
  3. var sig, sstr string
  4. var err error
  5. // 生成jwt的前两部分string
  6. if sstr, err = t.SigningString(); err != nil {
  7. return "", err
  8. }
  9. // 根据不同的签名method 生成签名字符串
  10. if sig, err = t.Method.Sign(sstr, key); err != nil {
  11. return "", err
  12. }
  13. return strings.Join([]string{sstr, sig}, "."), nil
  14. }
  15. // 生成jwt的头部和荷载的string
  16. func (t *Token) SigningString() (string, error) {
  17. var err error
  18. parts := make([]string, 2)
  19. // 创建一个字符串数组
  20. for i, _ := range parts {
  21. var jsonValue []byte
  22. if i == 0 {
  23. // 把header部分转成[]byte
  24. if jsonValue, err = json.Marshal(t.Header); err != nil {
  25. return "", err
  26. }
  27. } else {
  28. // 把荷载部分部转成[]byte
  29. if jsonValue, err = json.Marshal(t.Claims); err != nil {
  30. return "", err
  31. }
  32. }
  33. // 为签名编码
  34. parts[i] = EncodeSegment(jsonValue)
  35. }
  36. // 用'.'号拼接两部分然后返回
  37. return strings.Join(parts, "."), nil
  38. }

校验签名

  1. // 解析方法的回调函数 方法返回秘钥 可以根据不同的判断返回不同的秘钥
  2. type Keyfunc func(*Token) (interface{}, error)
  3. func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
  4. return new(Parser).Parse(tokenString, keyFunc)
  5. }
  6. func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
  7. return new(Parser).ParseWithClaims(tokenString, claims, keyFunc)
  8. }
  9. func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
  10. // 解析tokenstring 根据'.' 风格之后用base64反编码之后组成 token对象
  11. token, parts, err := p.ParseUnverified(tokenString, claims)
  12. if err != nil {
  13. return token, err
  14. }
  15. // 判断parse里的validmethods 是否为空 不为空则循环调用
  16. if p.ValidMethods != nil {
  17. var signingMethodValid = false
  18. var alg = token.Method.Alg()
  19. for _, m := range p.ValidMethods {
  20. if m == alg {
  21. signingMethodValid = true
  22. break
  23. }
  24. }
  25. if !signingMethodValid {
  26. // signing method is not in the listed set
  27. return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid)
  28. }
  29. }
  30. // 调用keyfunc 返回秘钥 方法从之前的调用注入的方法
  31. var key interface{}
  32. if keyFunc == nil {
  33. // keyFunc was not provided. short circuiting validation
  34. return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable)
  35. }
  36. if key, err = keyFunc(token); err != nil {
  37. // keyFunc returned an error
  38. if ve, ok := err.(*ValidationError); ok {
  39. return token, ve
  40. }
  41. return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
  42. }
  43. vErr := &ValidationError{}
  44. // 判断是否需要验证claims
  45. if !p.SkipClaimsValidation {
  46. // valid 方法中会判断 过期时间、签发人、生效时间 如果没有这3个字段则不判断
  47. if err := token.Claims.Valid(); err != nil {
  48. if e, ok := err.(*ValidationError); !ok {
  49. vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
  50. } else {
  51. vErr = e
  52. }
  53. }
  54. }
  55. // 验证jwt中第三部分 签名 调用的是签名方法定义的verify方法
  56. token.Signature = parts[2]
  57. if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
  58. vErr.Inner = err
  59. vErr.Errors |= ValidationErrorSignatureInvalid
  60. }
  61. // 设置valid字段
  62. if vErr.valid() {
  63. token.Valid = true
  64. return token, nil
  65. }
  66. return token, vErr
  67. }

参考

https://jwt.io/
https://github.com/monkeyk/MyOIDC/