what is jwt
- Json Web Token
- 是一个常用语HTTP的客户端和服务端间进行身份认证和鉴权的标准规范,使用JWT可以允许我们在用户和服务器之间传递安全可靠的信息。
Cookie Session Jwt
- Cookie 存在客户端,后续请求会携带
- Session 存在服务器端,配合Cookie使用
- Jwt 存在客户端, 随请求头 Authorization 发给服务端做校验
Authorization: Bearer <token>
JWT Token组成部分
image.png- 不要在header和payload中放置敏感信息,除非加密
Do note that for signed tokens this information, though protected against tampering, is readable by anyone. Do not put secret information in the payload or header elements of a JWT unless it is encrypted.
- header 、payload 通过base64_decode可以解析成明文
- signature 是sha256算法和密钥加密生成的
- 服务端验证token的过程是解析token的明文部分header和payload,再做一次签名比对结果和signature是否一致
- https://jwt.io/ demo
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
php -r " echo base64_decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9');"
输出:
php -r " echo base64_decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9');"
{"alg":"HS256","typ":"JWT"}
php -r " echo base64_decode('eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ');"
输出:
{"sub":"1234567890","name":"John Doe","iat":1516239022}
// SignedString creates and returns a complete, signed JWT.
// The token is signed using the SigningMethod specified in the token.
func (t *Token) SignedString(key interface{}) (string, error) {
var sig, sstr string
var err error
if sstr, err = t.SigningString(); err != nil {
return "", err
}
if sig, err = t.Method.Sign(sstr, key); err != nil {
return "", err
}
return strings.Join([]string{sstr, sig}, "."), nil
}
// Sign implements token signing for the SigningMethod.
// Key must be []byte 生成 token中的 signature
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
}
// 解析 token
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
token, parts, err := p.ParseUnverified(tokenString, claims)
if err != nil {
return token, err
}
// Verify signing method is in the required set
if p.ValidMethods != nil {
var signingMethodValid = false
var alg = token.Method.Alg()
for _, m := range p.ValidMethods {
if m == alg {
signingMethodValid = true
break
}
}
if !signingMethodValid {
// signing method is not in the listed set
return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid)
}
}
// Lookup key
var key interface{}
if keyFunc == nil {
// keyFunc was not provided. short circuiting validation
return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable)
}
if key, err = keyFunc(token); err != nil {
// keyFunc returned an error
if ve, ok := err.(*ValidationError); ok {
return token, ve
}
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
}
vErr := &ValidationError{}
// Validate Claims
if !p.SkipClaimsValidation {
if err := token.Claims.Valid(); err != nil {
// If the Claims Valid returned an error, check if it is a validation error,
// If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set
if e, ok := err.(*ValidationError); !ok {
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
} else {
vErr = e
}
}
}
// Perform validation
token.Signature = parts[2]
// 解析完token 重新做一次签名,比对签名是否一致
if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
vErr.Inner = err
vErr.Errors |= ValidationErrorSignatureInvalid
}
if vErr.valid() {
token.Valid = true
return token, nil
}
return token, vErr
}
工作原理
image.png image.png- token 可以存于浏览器的localStorage
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage
https://juejin.cn/post/7149380173027573767
why use jwt
- 无状态和可扩展性
- 安全: 防止CSRF攻击;token过期重新认证
网友评论