feat(jwt): add jwt helpers
This commit is contained in:
parent
fbbfdf8df6
commit
a99e990270
27
data/private
Normal file
27
data/private
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAl6nmCqsNninrds4Y0MXJkonE/a8/ak+tIWn8/af0nR/r+wlx
|
||||
ISfAMkoAaGuhpj39v6um+iQlnBjZoo/G8+HuanAv7wxERr9l995nUNBm+EelY0KL
|
||||
mG4Xh6b4qEYY2bFbHYsKiDEvxDBiaALdRLeplELw/pyoWJp6ALusbDSDJ1AG9FZ7
|
||||
JYq0xNUgcydYN9PNao1pfe4A8ZkxBktJyoLK8CtjjAAZ54WwpzQx3abmVh506fS5
|
||||
3lrXYIOJaW0dxcI5tT5PrKJkkE4UP551HMW/V0GVtWfHebtCutW9Jq5/uBtQW3Kg
|
||||
sdNdvm+DZVrME8TqKWMQ6utz9u8DQ+YF2/DB4wIDAQABAoIBADMFPdUy7BbcJVFH
|
||||
TgPVtdTtMe7hqKS7/xMxk6FFgj3lgj5mU7+Cnt6MFI0MQEorqpRzS231AQ39MiHE
|
||||
2noq9EisSwPRDZr7QnNbR0hhg3Jcr9+vgEScLKA+5IG/axa42l0a7EUavuXyHPi+
|
||||
le9LFepBhs8wplWASjC68etI0yJC/i5p5W8RNiBOTMrRB73LAgLYy0TcZ6OPMAor
|
||||
PXyNLdTtDe5CXc4KbL3D1u8VARJ8b8+Ck7ObBg5p9qnr+VcTZLG13Af2/aEh9zDm
|
||||
prU0i5obBNYsy/OFG+IZfnK0Pm56ZpkiOFSDI6f8d00BwVF0OBC4dftAgCACi62x
|
||||
QDD7evECgYEAx2kARY5Nnk/98hAygiUpI8wdOlF6rCCjPUZNfJbhwqj2rame9Q4Y
|
||||
nvBhqGX1SJBD5ps5zjlSL6ilwr+j3cyiAHQl3szgvdy9C9J48RkWjME6LsS8x2Yl
|
||||
aDtomzZ2a4+iOzcYzaxVX3nVn4RoS+gsMiX7jDEVocyYCc5s2Hkt9GsCgYEAwrQm
|
||||
hb7rX/yEQw3Ds4TbjKxIDaqsA1yRikE8hQPZ8p8SZmZIhtWjVSDvBgyCHJO1iOim
|
||||
AW/72f2+kyrUXuffyzXZW7s/3KW1AHNlOKJCraBMRVtzvUt1oHL8ZrYYXbKDW6Eq
|
||||
Zbae+s5n2kU8UrtaXs1qh4EhYnTO+M5NdgxXBmkCgYAk6gcm2ST9PYmhGeZ/uSlY
|
||||
exyeAx9WZeRSH4WQns3EH0sq8s9+RdHA+nbZmaZCfJJVSj71Mh9Iu0uUNa28DXmf
|
||||
4+Bu0jZ4bzh/y8KfvykxfUOsDLd1oi8ikHzY3sglOT2rAJQS3uge+IrXMMet5Zjo
|
||||
36clWKDMhvdOOWxk1mnvaQKBgCbAnHo6SbbNF7YQ40azxs7061JtCdeRcRZHbbg7
|
||||
0AFOT+c5rG3Jz7x91ZUqoCr360XYqFHY7BOzQV8hQyuwkwZrLVvopQlRofj4/siK
|
||||
4yKTqRqU3TBr+Hl66Wm4DJl5klOGfF3KP1JECr+S0DLXP2FnGTDnLrHd9ePni9tX
|
||||
EWshAoGAIMMr4+eYD2MIZkST6Nwbw+0BNTfiIW18CQUUwPeEHG1+CA5KT9tL5Zvz
|
||||
vJ/4aFNoFPZ04IBmNmGO5LLRGvMsIJs85niqRcGIPrQq948vQ2QCQ5/W9GwxE4lu
|
||||
x+yGW+XUExRP4FtTISWDMb0ZQERLjo6oBiKJkGYowzapIHCnP5Q=
|
||||
-----END RSA PRIVATE KEY-----
|
9
data/public
Normal file
9
data/public
Normal file
@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl6nmCqsNninrds4Y0MXJ
|
||||
konE/a8/ak+tIWn8/af0nR/r+wlxISfAMkoAaGuhpj39v6um+iQlnBjZoo/G8+Hu
|
||||
anAv7wxERr9l995nUNBm+EelY0KLmG4Xh6b4qEYY2bFbHYsKiDEvxDBiaALdRLep
|
||||
lELw/pyoWJp6ALusbDSDJ1AG9FZ7JYq0xNUgcydYN9PNao1pfe4A8ZkxBktJyoLK
|
||||
8CtjjAAZ54WwpzQx3abmVh506fS53lrXYIOJaW0dxcI5tT5PrKJkkE4UP551HMW/
|
||||
V0GVtWfHebtCutW9Jq5/uBtQW3KgsdNdvm+DZVrME8TqKWMQ6utz9u8DQ+YF2/DB
|
||||
4wIDAQAB
|
||||
-----END PUBLIC KEY-----
|
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
||||
module git.rinsvent.ru/rinsvent/go-jwt
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/ua-parser/uap-go v0.0.0-20250213224047-9c035f085b90 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
17
go.sum
Normal file
17
go.sum
Normal file
@ -0,0 +1,17 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/ua-parser/uap-go v0.0.0-20250213224047-9c035f085b90 h1:rB0J+hLNltG1Qv+UF+MkdFz89XMps5BOAFJN4xWjc+s=
|
||||
github.com/ua-parser/uap-go v0.0.0-20250213224047-9c035f085b90/go.mod h1:BUbeWZiieNxAuuADTBNb3/aeje6on3DhU3rpWsQSB1E=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
200
jwt.go
Normal file
200
jwt.go
Normal file
@ -0,0 +1,200 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RefreshTokenClaims struct {
|
||||
JWT
|
||||
}
|
||||
|
||||
func CreateRefreshTokenByAccess(accessClaims JWT, ttl time.Duration) RefreshTokenClaims {
|
||||
return RefreshTokenClaims{
|
||||
JWT: JWT{
|
||||
Type: "refresh",
|
||||
Ttl: ttl,
|
||||
SessionId: accessClaims.SessionId,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ID: accessClaims.ID,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ParseRefreshToken(token string, publicKey *rsa.PublicKey) (*RefreshTokenClaims, error) {
|
||||
refreshDecodedClaims, err := Decode(token, &RefreshTokenClaims{}, publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refreshTokenClaims, ok := refreshDecodedClaims.(*RefreshTokenClaims)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid refresh token claims")
|
||||
}
|
||||
return refreshTokenClaims, nil
|
||||
}
|
||||
|
||||
type JWT struct {
|
||||
Type string `json:"t"`
|
||||
Ttl time.Duration `json:"td"`
|
||||
SessionId string `json:"si,omitempty"`
|
||||
AuthorizationInfo string `json:"ai,omitempty"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func (j *JWT) WithTtl(ttl time.Duration) *JWT {
|
||||
j.Ttl = ttl
|
||||
j.ExpiresAt = jwt.NewNumericDate(time.Now().Add(ttl))
|
||||
j.IssuedAt = jwt.NewNumericDate(time.Now())
|
||||
j.NotBefore = jwt.NewNumericDate(time.Now())
|
||||
return j
|
||||
}
|
||||
|
||||
func (j *JWT) WithId(id string) *JWT {
|
||||
j.ID = id
|
||||
return j
|
||||
}
|
||||
|
||||
func (j *JWT) WithSessionId(sessionId string) *JWT {
|
||||
j.SessionId = sessionId
|
||||
return j
|
||||
}
|
||||
|
||||
func (j *JWT) WithAuthorizationInfo(tai TokenAuthorizationInfo, secret string) *JWT {
|
||||
ciphertext, err := tai.WithSecret(secret).Encode()
|
||||
if err != nil {
|
||||
return j
|
||||
}
|
||||
j.AuthorizationInfo = ciphertext
|
||||
return j
|
||||
}
|
||||
|
||||
func (j *JWT) GetAuthorizationInfo(secret string) *TokenAuthorizationInfo {
|
||||
tai, err := DecodeTokenAuthorizationInfo(j.AuthorizationInfo, secret)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return tai
|
||||
}
|
||||
|
||||
func (j *JWT) IsRefreshToken() bool {
|
||||
return j.Type == "refresh"
|
||||
}
|
||||
|
||||
func Encode(j interface{}, privateKey *rsa.PrivateKey) (string, error) {
|
||||
payload, ok := j.(jwt.Claims)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("invalid jwt claims")
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, payload)
|
||||
ss, err := token.SignedString(privateKey)
|
||||
return ss, err
|
||||
}
|
||||
|
||||
func Decode(token string, data jwt.Claims, publicKey *rsa.PublicKey, options ...jwt.ParserOption) (jwt.Claims, error) {
|
||||
t, err := jwt.ParseWithClaims(token, data, func(token *jwt.Token) (interface{}, error) {
|
||||
return publicKey, nil
|
||||
}, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if claims, ok := t.Claims.(jwt.Claims); ok {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown claims type, cannot proceed")
|
||||
}
|
||||
|
||||
func ReadPublicKey(path string) (*rsa.PublicKey, error) {
|
||||
b, err := readPublicKey(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading file: %w", err)
|
||||
}
|
||||
|
||||
decrypted, err := decodePEMBlock(b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decoding PEM block failed: %w", err)
|
||||
}
|
||||
|
||||
parsedKey, err := x509.ParsePKIXPublicKey(decrypted)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing decrypted public key failed: %w", err)
|
||||
}
|
||||
|
||||
publicKey, ok := parsedKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("parsing decrypted public key failed: %w", err)
|
||||
}
|
||||
|
||||
return publicKey, nil
|
||||
}
|
||||
|
||||
func readFile(path string) ([]byte, error) {
|
||||
file, err := os.Open(filepath.Clean(path))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
b, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading file: %w", err)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func readPublicKey(path string) ([]byte, error) {
|
||||
b, err := readFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading file: %w", err)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func ReadPrivateKey(path string) (*rsa.PrivateKey, error) {
|
||||
b, err := readFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading file: %w", err)
|
||||
}
|
||||
|
||||
decrypted, err := decodePEMBlock(b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode PEM block: %w", err)
|
||||
}
|
||||
|
||||
parsedKey, err := x509.ParsePKCS1PrivateKey(decrypted)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing decrypted private key: %w", err)
|
||||
}
|
||||
|
||||
return parsedKey, nil
|
||||
}
|
||||
|
||||
func decodePEMBlock(block []byte) ([]byte, error) {
|
||||
decodedKey, _ := pem.Decode(block)
|
||||
if decodedKey == nil {
|
||||
return nil, fmt.Errorf("decoding PEM block failed")
|
||||
}
|
||||
var (
|
||||
decrypted = decodedKey.Bytes
|
||||
err error
|
||||
)
|
||||
//nolint:staticcheck,nolintlint
|
||||
if x509.IsEncryptedPEMBlock(decodedKey) {
|
||||
//nolint:staticcheck,nolintlint
|
||||
if decrypted, err = x509.DecryptPEMBlock(decodedKey, []byte("")); err != nil {
|
||||
return nil, fmt.Errorf("decrypting PEM key failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return decrypted, nil
|
||||
}
|
40
jwt_test.go
Normal file
40
jwt_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AccessTokenClaims struct {
|
||||
UserId string `json:"id"`
|
||||
FirstName string `json:"fn"`
|
||||
LastName string `json:"ln"`
|
||||
JWT
|
||||
}
|
||||
|
||||
func TestJWT(t *testing.T) {
|
||||
dir := "./data/"
|
||||
|
||||
accessClaims := AccessTokenClaims{
|
||||
UserId: "123",
|
||||
FirstName: "Igor",
|
||||
LastName: "Sypachev",
|
||||
}
|
||||
accessClaims.
|
||||
WithId("sadfswdf").
|
||||
WithTtl(20 * time.Minute).
|
||||
WithSessionId("wergwergw")
|
||||
|
||||
privateKey, _ := ReadPrivateKey(filepath.Join(filepath.Dir(dir), "private"))
|
||||
token, err := Encode(accessClaims, privateKey)
|
||||
assert.Equal(t, true, err == nil)
|
||||
|
||||
publicKey, _ := ReadPublicKey(filepath.Join(filepath.Dir(dir), "public"))
|
||||
decodedClaims, _ := Decode(token, &AccessTokenClaims{}, publicKey)
|
||||
f, _ := decodedClaims.(*AccessTokenClaims)
|
||||
assert.Equal(t, f.UserId, "123")
|
||||
assert.Equal(t, f.FirstName, "Igor")
|
||||
assert.Equal(t, f.LastName, "Sypachev")
|
||||
}
|
140
token_authorization_info.go
Normal file
140
token_authorization_info.go
Normal file
@ -0,0 +1,140 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ua-parser/uap-go/uaparser"
|
||||
"net"
|
||||
)
|
||||
|
||||
type TokenAuthorizationInfo struct {
|
||||
Ip net.IP `json:"ip,omitempty"`
|
||||
UserAgent string `json:"ua,omitempty"`
|
||||
DeviceId string `json:"di,omitempty"`
|
||||
CookieData string `json:"cd,omitempty"`
|
||||
secret string
|
||||
}
|
||||
|
||||
func (tai *TokenAuthorizationInfo) WithSecret(secret string) *TokenAuthorizationInfo {
|
||||
tai.secret = secret
|
||||
return tai
|
||||
}
|
||||
|
||||
func (tai *TokenAuthorizationInfo) GetUserAgentInfo() *uaparser.Client {
|
||||
parser := uaparser.NewFromSaved()
|
||||
return parser.Parse(tai.UserAgent)
|
||||
}
|
||||
|
||||
func (tai *TokenAuthorizationInfo) Encode() (string, error) {
|
||||
payload, err := json.Marshal(tai)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ciphertext, err := GetAESEncrypted(string(payload), tai.secret[0:32], tai.secret[0:16])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func DecodeTokenAuthorizationInfo(ciphertext string, secret string) (*TokenAuthorizationInfo, error) {
|
||||
payload, err := GetAESDecrypted(ciphertext, secret[0:32], secret[0:16])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tai := TokenAuthorizationInfo{}
|
||||
if err := json.Unmarshal(payload, &tai); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tai, nil
|
||||
}
|
||||
|
||||
func (tai *TokenAuthorizationInfo) Equal(requestAuthorizationInfo TokenAuthorizationInfo) bool {
|
||||
// todo проработать логику проверки данных клиента. Возможно добавим finger print / http only cookie. Защита от потери токенов
|
||||
if tai.Ip.String() == requestAuthorizationInfo.Ip.String() {
|
||||
return true
|
||||
}
|
||||
|
||||
uaClient := tai.GetUserAgentInfo()
|
||||
requestUaClient := tai.GetUserAgentInfo()
|
||||
if uaClient.UserAgent.Family == requestUaClient.UserAgent.Family &&
|
||||
uaClient.UserAgent.Major == requestUaClient.UserAgent.Major &&
|
||||
uaClient.Os.Family == requestUaClient.Os.Family &&
|
||||
uaClient.Os.Major == requestUaClient.Os.Major &&
|
||||
uaClient.Device.Family == requestUaClient.Device.Family &&
|
||||
uaClient.Device.Model == requestUaClient.Device.Model &&
|
||||
uaClient.Device.Brand == requestUaClient.Device.Brand {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func GetAESDecrypted(encrypted string, key string, iv string) ([]byte, error) {
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher([]byte(key))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ciphertext)%aes.BlockSize != 0 {
|
||||
return nil, fmt.Errorf("block size cant be zero")
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, []byte(iv))
|
||||
mode.CryptBlocks(ciphertext, ciphertext)
|
||||
ciphertext = PKCS5UnPadding(ciphertext)
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// PKCS5UnPadding pads a certain blob of data with necessary data to be used in AES block cipher
|
||||
func PKCS5UnPadding(src []byte) []byte {
|
||||
length := len(src)
|
||||
unpadding := int(src[length-1])
|
||||
|
||||
return src[:(length - unpadding)]
|
||||
}
|
||||
|
||||
// GetAESEncrypted encrypts given text in AES 256 CBC
|
||||
func GetAESEncrypted(plaintext string, key string, iv string) (string, error) {
|
||||
var plainTextBlock []byte
|
||||
length := len(plaintext)
|
||||
|
||||
if length%16 != 0 {
|
||||
extendBlock := 16 - (length % 16)
|
||||
plainTextBlock = make([]byte, length+extendBlock)
|
||||
copy(plainTextBlock[length:], bytes.Repeat([]byte{uint8(extendBlock)}, extendBlock))
|
||||
} else {
|
||||
plainTextBlock = make([]byte, length)
|
||||
}
|
||||
|
||||
copy(plainTextBlock, plaintext)
|
||||
block, err := aes.NewCipher([]byte(key))
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ciphertext := make([]byte, len(plainTextBlock))
|
||||
mode := cipher.NewCBCEncrypter(block, []byte(iv))
|
||||
mode.CryptBlocks(ciphertext, plainTextBlock)
|
||||
|
||||
str := base64.StdEncoding.EncodeToString(ciphertext)
|
||||
|
||||
return str, nil
|
||||
}
|
42
token_authorization_info_test.go
Normal file
42
token_authorization_info_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTAI(t *testing.T) {
|
||||
ip := net.ParseIP("192.186.4.33")
|
||||
userAgent := "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
|
||||
deviceId := "6a22eeeb-966c-47fa-bff8-f83dc7929d84"
|
||||
cookieData := "asdfasd"
|
||||
|
||||
tai := TokenAuthorizationInfo{
|
||||
Ip: ip,
|
||||
UserAgent: userAgent,
|
||||
DeviceId: deviceId,
|
||||
CookieData: cookieData,
|
||||
}
|
||||
|
||||
actual, err := tai.WithSecret("1234567890123456789012345678901234567890").Encode()
|
||||
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t,
|
||||
"BbFhu340tDQl9y8siWFlc7s1TpjaHpWWG9tlOGXGOLheBj+cOiF4HKUaBFou10WX8y/feoz6tz/9IPgiUTwbEuXetGIO1KdoygmYiRhxlBYqv0sRa55EjNnPS1DrM7KHOu4fyV57+dvfc4dR669lnuTwhQFE6Q51pq5FtLTnm02HisPGVl1G3JukKAjPRNWCwdZhOylGPuQCav1Egihcz2ZZ3RRDOwUu3SsKEZJJig56XAd1J5MMHzovEgg6B4J4",
|
||||
actual,
|
||||
)
|
||||
|
||||
tai2, err2 := DecodeTokenAuthorizationInfo(
|
||||
"BbFhu340tDQl9y8siWFlc7s1TpjaHpWWG9tlOGXGOLheBj+cOiF4HKUaBFou10WX8y/feoz6tz/9IPgiUTwbEuXetGIO1KdoygmYiRhxlBYqv0sRa55EjNnPS1DrM7KHOu4fyV57+dvfc4dR669lnuTwhQFE6Q51pq5FtLTnm02HisPGVl1G3JukKAjPRNWCwdZhOylGPuQCav1Egihcz2ZZ3RRDOwUu3SsKEZJJig56XAd1J5MMHzovEgg6B4J4",
|
||||
"1234567890123456789012345678901234567890",
|
||||
)
|
||||
|
||||
assert.Equal(t, nil, err2)
|
||||
assert.Equal(t, ip, tai2.Ip)
|
||||
assert.Equal(t, userAgent, tai2.UserAgent)
|
||||
assert.Equal(t, deviceId, tai2.DeviceId)
|
||||
assert.Equal(t, cookieData, tai2.CookieData)
|
||||
|
||||
assert.Equal(t, true, tai.Equal(*tai2))
|
||||
}
|
Loading…
Reference in New Issue
Block a user