这是我参与「第五届青训营 」伴学笔记创作活动的第 17 天。
1. 加密和解密
加密技术包括两个基本的两个元素:加密算法、密钥。
在现代密码学种,加密算法都是公开的,而密钥是保密的。
1.1. 对称加密
对称加密的特点有:
- 加密和解密时使用的密钥相同
- 加密简单快捷
- 适用于一对一的场景
常见算法:
- DES: 密钥位数是 56
- 3DES: 密钥位数是 112
- IDEA: 密钥位数是 128
- AES: 密钥位数是 128 或 192 或 256
1.2. 非对称加密
非对称加密的特点有:
- 加密和解密使用不同的密钥
- 公钥用于加密,私钥用于解密
- 适用于一对多的场景
常见算法:
- RSA
- DSA
- ECC
- DH
1.3. Hash
哈希不是加密算法,其算法是单向的,不可逆的。
- Hash 函数将任意长的报文
M映射为定长的 Hash 码h。 - Hash 函数的目的就是要产生文件、报文或其他数据块的指纹,Hash 码也称报文摘要。
- Hash 函数可提供保密性、报文认证以及数字签名功能。
1.4. 数字签名
数字签名的作用是证明当事人的身份和数据真实性的一种方式。
- 签名者事后不能抵赖自己的签名。
- 任何其他人不能伪造签名。
- 如果当事的双方关于签名发生争执,能够在公正的仲裁者面前通过验证签名来确认其真伪。
利用非对称加密算法,如 RSA,可以同时实现数字签名。
使用私钥生成数字签名,使用公钥解密验证。
2. Token
Token 需要具备的特性:
- 随机性, 不可预测, 防猜测
- 失效时间, 可以无感获取 (refresh)
- 包含信息, 可以代表用户身份
- 防篡改, 保证接收到的 Token 是服务端曾经发放过的
- 服务端必须保存些什么, 以便主动失效掉 Token
2.1. jwt-go
安装 JWT 库:使用 go get 命令安装 JWT 库:
go get -u github.com/dgrijalva/jwt-go@v3.2.0
2.1.1. 创建 token
-
创建一个
claims结构体, 用来存储 token 的载荷 (payload) 信息.
claims需要组合继承jwt.StandardClaims, 其中包括失效时间 (ExpiresAt), Token 颁布者 (Issuer)等信息.type claims struct { jwt.StandardClaims UserId int }// Structured version of Claims Section, as referenced at // https://tools.ietf.org/html/rfc7519#section-4.1 // See examples for how to use this with your own claim types type StandardClaims struct { Audience string `json:"aud,omitempty"` ExpiresAt int64 `json:"exp,omitempty"` Id string `json:"jti,omitempty"` IssuedAt int64 `json:"iat,omitempty"` Issuer string `json:"iss,omitempty"` NotBefore int64 `json:"nbf,omitempty"` Subject string `json:"sub,omitempty"` } -
调用
jwt.NewWithClaims初始化 Token 生成器并选择算法.func NewWithClaims(method SigningMethod, claims Claims) *Token除了
claims还需要传入一个参数,SigningMethod表示签名算法, 可选择的算法有jwt.SigningMethodHMACjwt.SigningMethodRSAPSSjwt.SigningMethodRSASigningMethodECDSA这些算法里面各自还包含了 3 种哈希算法, 分别初始化了一些算法实例. 例如SigningMethodHS256就是使用了HMAC-SHA256算法:
type SigningMethodHMAC struct { Name string Hash crypto.Hash } // Specific instances for HS256 and company var ( SigningMethodHS256 *SigningMethodHMAC SigningMethodHS384 *SigningMethodHMAC SigningMethodHS512 *SigningMethodHMAC ErrSignatureInvalid = errors.New("signature is invalid") ) // Implements the RSAPSS family of signing methods signing methods type SigningMethodRSAPSS struct { *SigningMethodRSA Options *rsa.PSSOptions } // Specific instances for RS/PS and company var ( SigningMethodPS256 *SigningMethodRSAPSS SigningMethodPS384 *SigningMethodRSAPSS SigningMethodPS512 *SigningMethodRSAPSS ) -
调用
jwt.SignedString函数生成 Token.
并使用一个密钥 (key) 进行数字签名, 用于防止篡改, 保证接收到的 Token 是服务端曾经发放过的.// Get the complete, signed token func (t *Token) SignedString(key interface{}) (string, error)
完整代码:
type claims struct {
jwt.StandardClaims
UserId int
}
func generateToken(userId int) (string, error) {
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims{
StandardClaims: jwt.StandardClaims{
Issuer: conf.Hostname,
ExpiresAt: time.Now().Add(72 * time.Hour).Unix(),
},
UserId: userId,
}).SignedString([]byte("密钥,请保密"))
if err != nil {
return "", err
}
return token, nil
}
2.1.2. 校验 Token
使用 jwt.ParseWithClaims 函数验证 Token, 还可以获取 claims 中的信息.
claims 用于反射获取类型, keyFunc 是获取密钥的函数,
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error)
完整代码:
func verifyToken(token string) int {
_token, err := jwt.ParseWithClaims(token, &claims{}, func(token *jwt.Token) (interface{}, error) {
return conf.SecretKey, nil
})
if err != nil {
return 0
}
_claims, ok := _token.Claims.(*claims)
if !ok || !_token.Valid {
return 0
}
return _claims.UserId
}