RSA算法 token生成与解析
本文演示两种方式,一种是把密钥文件放在配置文件中,一种是把密钥文件本身放入项目或者容器中。
下面两种的
区别
在于私钥公钥的初始化,init方法
,需要哪种取哪种。
一、通过文件读取
首先是密钥文件(privateKey),放在如config目录下。
-----BEGIN RSA PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMu4IDG1XU6a7bXo
4V1jSnbKk9Eum2WguAyq+maCRcP9JoHlE/HmhPOjl91aN5gDHw3pgB7QMMkPkuyY
0aG9UiIo7PbBgjXsNBErprKa8G7GKhDN4B3m8jxEi1NLtCk2H8AEf8H/deGFXCde
fjQx0NcTbJ8STfbsqrLhOq2xzAgMBAAECgYEAg1kZMNOd8IOFxqb7P2o4ZbUh
b1rciL8CS/CleBiAgOgkvtWDcZFOoYQV83sqoxFIIYEuwS88dTZcZb32U5EsdYEx
JvJwAAYnzpch/YAz0llvXSHzZwNfGGvs4qt0d74bFpPfveli82wSKMlykeajP2Ro
RQpOniTYOWrJ01UHdUECQQDt1KTj/Xs5BNmEZAkJVmGekQROADk+ztceAe9UMj/J
s5xECdXVwuFh2Rm62MMQNNoW2Pjz4Y5NqhjRu0MMZnlTAkEA20hZsgA78aqTO7s+
+y/CLgP3Cd7uG/5RkcmjBWq2eXkt6wmazZl0BMYb7vshblnMjFXJwuOmfBJl7rTr
1fg8YQJAEo4Jg0QObgdj1QFc9x6HJTDZLiC0VqMag1vRSTdWZK0fnutJhJDctp6S
dFJe/Y+yCCBLY/OP/50qrIo4k+oWwwJAIn8hTTVoOL6C5xSv9cgvnhmVlYHyp4i8
wFieQs3k4vtDVARwzANmExIvdssfGUMbQMCGOxihKkeirYjcyQ6CQQJAbsbpzCjD
wd9JCogmTu/xYqtL898ek7LeNkhgIY2KhYtlptxlHfzgLBUgiSTNTcD1YWtSSp6u
A5ImxrryDYPmfg==
-----END RSA PRIVATE KEY-----
处理私钥,生成token与解析
package main
import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"errors"
"github.com/golang-jwt/jwt/v4"
"io/ioutil"
"log"
"time"
)
var (
publicKey *rsa.PublicKey
privateKey *rsa.PrivateKey
)
func main() {
token, _ := createToken(privateKey)
println("-------------")
println(token)
println("-------------")
a1, _ := getSubFromToken(token)
println(a1)
}
func init() {
publicKeyByte, err := ioutil.ReadFile("公钥的路径/public.key")
if err != nil {
log.Println(err.Error())
}
publicKey, err = jwt.ParseRSAPublicKeyFromPEM(publicKeyByte)
privateKeyByte, err := ioutil.ReadFile("私钥的路径/private.key")
if err != nil {
log.Println(err.Error())
}
privateKey, _ = jwt.ParseRSAPrivateKeyFromPEM(privateKeyByte)
}
// createToken 生成一个RS256验证的Token
// Token里面包括的值,可以自己根据情况添加,
func createToken(privateKey *rsa.PrivateKey) (tokenStr string, err error) {
expireTime := time.Now().Add(7 * 24 * time.Hour)
var audi = jwt.ClaimStrings{
"zwmgc",
}
var expir = jwt.NewNumericDate(expireTime)
claim := jwt.RegisteredClaims{
Audience: audi,
ExpiresAt: expir, //过期时间
//IssuedAt: time.Now().Unix(),
Issuer: "my-sdk", // 签名颁发者
Subject: "23258bd202e451a988234c2145d754a", //签名主题
}
// jwt.SigningMethodHS256
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claim)
tokenStr, _ = token.SignedString(privateKey)
return
}
// getSubFromToken 获取Token的主题(也可以更改获取其他值)
// 参数tokenStr指的是 从客户端传来的待验证Token
// 验证Token过程中,如果Token生成过程中,指定了iat与exp参数值,将会自动根据时间戳进行时间验证
func getSubFromToken(tokenStr string) (sub string, err error) {
// 基于公钥验证Token合法性
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
// 基于JWT的第一部分中的alg字段值进行一次验证
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, errors.New("验证Token的加密类型错误")
}
return publicKey, nil
})
if err != nil {
return
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims["sub"].(string), nil
}
return "", errors.New("token无效或者无对应值")
}
这里使用到jwt.RegisteredClaims这个包来进行token生成,在最新的jwt包中,官方加了删除线,表示这个方法不建议再使用了。 这时候如果选择使用jwt.StandardClaims会发现一个问题,就是内部的audience字段从一个string变成了一个数组。那么如何兼容原来的string呢?可以使用下面这个
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
"aud": "your audience",
"exp": time.Now().Add(time.Hour * 24).Unix(), // Token过期时间,目前是24小时
"iss": "my-sdk", // 颁发者
"sub": credential.Subject, // 主题
})
二、 通过配置文件或者字符串
package main
import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"errors"
"github.com/golang-jwt/jwt/v4"
"io/ioutil"
"log"
"time"
)
var (
publicKey *rsa.PublicKey
privateKey *rsa.PrivateKey
)
func main() {
token, _ := createToken(privateKey)
println("-------------")
println(token)
println("-------------")
a1, _ := getSubFromToken(token)
println(a1)
}
func init() {
// 初始化 public Key
var publicKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLuCAxtV1Omu216OFdY0p2ypPR\n" +
"LptloLgMqvpmgkXD/SaB5RPx5oTzo5fdWjeYAx8N6YAe0DDJD5LsmNGhvVIiKOz2\n" +
"wYI17DQRK6aymvBuxioQzeAd5vI8RBH/B/3XhhVwnXn40MdDQxA3E\n" +
"2yfEk327Kqy4TqtscwIDAQAB"
publicKeyBinary := make([]byte, base64.StdEncoding.DecodedLen(len(publicKeyStr)))
publicKeyLen, _ := base64.StdEncoding.Decode(publicKeyBinary, []byte(publicKeyStr))
publicKeyBinary = publicKeyBinary[:publicKeyLen]
pubInterface, _ := x509.ParsePKIXPublicKey(publicKeyBinary)
publicKey = pubInterface.(*rsa.PublicKey)
// 初始化 private Key
var privateKeyStr = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMu4IDG1XU6a7bXo\n" +
"4V1jSnbKk9Eum2WguAyq+maCRcP9JoHlE/HmhPOjl91aN5gDHw3pgB7QMMkPkuyY\n" +
"0aG9UiIo7PbBgjXsNBErprKa8G7GKhDN4B3m8jxEi1NLtCk2H8AEf8H/deGFXCde\n" +
"fjQx0NDEDcTbJ8STfbsqrLhOq2YEAg1kZMNOd8IOFxqb7P2o4ZbUh\n" +
"b1rciL8CS/CleBiAgOgkvtWDcZFOoYQV83sqoxFIIYEuwS88dTZcZb32U5EsdYEx\n" +
"JvJwAAYnzpch/YAz0llvXSHzZwNfGGvs4qt0d74bFpPfveli82wSKMlykeajP2Ro\n" +
"RQpOniTYOWrJ01UHdUECQQDt1KTj/Xs5BNmEZAkJVmGekQROADk+ztceAe9UMj/J\n" +
"s5xECdXVwuFh2Rm62MMQNNoW2Pjz4Y5NqhjRu0MMZnlTAkEA20hZsgA78aqTO7s+\n" +
"+y/CLgP3Cd7uG/5RkcmjBWq2eXkt6wmazZl0BMYb7vshblnMjFXJwuOmfBJl7rTr\n" +
"1fg8YQJAEo4Jg0QObgdj1QFc9x6HJTDZLiC0VqMag1vRSTdWZK0fnutJhJDctp6S\n" +
"dFJe/Y+yCCBLY/OP/50qrIo4k+oWwwJAIn8hTTVoOL6C5xSv9cgvnhmVlYHyp4i8\n" +
"wFieQs3k4vtDVARwzANmExIvdssfGUMbQMCGOxihKkeirYjcyQ6CQQJAbsbpzCjD\n" +
"wd9JCogmTu/xYqtL898ek7LeNkhgIY2KhYtlptxlHfzgLBUgiSTNTcD1YWtSSp6u\n" +
"A5ImxrryDYPmfg=="
privateKeyBinary := make([]byte, base64.StdEncoding.DecodedLen(len(privateKeyStr)))
privateKeyLen, _ := base64.StdEncoding.Decode(privateKeyBinary, []byte(privateKeyStr))
privateKeyBinary = privateKeyBinary[:privateKeyLen]
privateInterface, _ := x509.ParsePKCS8PrivateKey(privateKeyBinary)
privateKey = privateInterface.(*rsa.PrivateKey)
}
// createToken 生成一个RS256验证的Token
// Token里面包括的值,可以自己根据情况添加,
func createToken(privateKey *rsa.PrivateKey) (tokenStr string, err error) {
expireTime := time.Now().Add(7 * 24 * time.Hour)
var audi = jwt.ClaimStrings{
"zwmgc",
}
var expir = jwt.NewNumericDate(expireTime)
claim := jwt.RegisteredClaims{
Audience: audi,
ExpiresAt: expir, //过期时间
//IssuedAt: time.Now().Unix(),
Issuer: "my-sdk", // 签名颁发者
Subject: "23258bd202e451a988234c2145d754a", //签名主题
}
// jwt.SigningMethodHS256
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claim)
tokenStr, _ = token.SignedString(privateKey)
return
}
// getSubFromToken 获取Token的主题(也可以更改获取其他值)
// 参数tokenStr指的是 从客户端传来的待验证Token
// 验证Token过程中,如果Token生成过程中,指定了iat与exp参数值,将会自动根据时间戳进行时间验证
func getSubFromToken(tokenStr string) (sub string, err error) {
// 基于公钥验证Token合法性
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
// 基于JWT的第一部分中的alg字段值进行一次验证
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, errors.New("验证Token的加密类型错误")
}
return publicKey, nil
})
if err != nil {
return
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims["sub"].(string), nil
}
return "", errors.New("token无效或者无对应值")
}