合并
This commit is contained in:
146
utils/auth/confirmation_link.go
Normal file
146
utils/auth/confirmation_link.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type ConfirmationLink[T any] struct {
|
||||
Secret []byte
|
||||
DefaultQueryKey string // 默认key 是 token
|
||||
link *url.URL
|
||||
}
|
||||
|
||||
func NewConfirmationLink[T any](key []byte, UrlStr string) *ConfirmationLink[T] {
|
||||
u, err := url.Parse(UrlStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &ConfirmationLink[T]{
|
||||
Secret: key,
|
||||
DefaultQueryKey: "token",
|
||||
link: u,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate 序列化链接传入需求的obj
|
||||
func (cl *ConfirmationLink[T]) Generate(obj *T) (string, error) {
|
||||
|
||||
token, err := cl.Encrypt(obj)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cl.GenerateWithToken(token)
|
||||
}
|
||||
|
||||
// GenerateWithToken 序列化url带token
|
||||
func (cl *ConfirmationLink[T]) GenerateWithToken(token string) (string, error) {
|
||||
|
||||
q := cl.link.Query()
|
||||
if q.Has(cl.DefaultQueryKey) {
|
||||
q.Set(cl.DefaultQueryKey, token)
|
||||
} else {
|
||||
q.Add(cl.DefaultQueryKey, token)
|
||||
}
|
||||
|
||||
// 生成确认链接
|
||||
cl.link.RawQuery = q.Encode()
|
||||
|
||||
return cl.link.String(), nil
|
||||
}
|
||||
|
||||
func fusenMakeKey(keysting string) []byte {
|
||||
|
||||
key := []byte(keysting)
|
||||
|
||||
var result [32]byte
|
||||
|
||||
// If key length is more than 32, truncate it
|
||||
if len(key) > 32 {
|
||||
key = key[:32]
|
||||
}
|
||||
|
||||
// If key length is less than 32, replicate it until it reaches 32
|
||||
for len(key) < 32 {
|
||||
key = append(key, key...)
|
||||
}
|
||||
|
||||
// Only take the first 32 bytes
|
||||
key = key[:32]
|
||||
|
||||
// Swap the first 16 bytes with the last 16 bytes
|
||||
copy(result[:], key[16:])
|
||||
copy(result[16:], key[:16])
|
||||
|
||||
return result[:]
|
||||
}
|
||||
|
||||
func (cl *ConfirmationLink[T]) Encrypt(obj *T) (string, error) {
|
||||
|
||||
var buf = bytes.NewBuffer(nil)
|
||||
err := gob.NewEncoder(buf).Encode(obj)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(cl.Secret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nonce := make([]byte, 12)
|
||||
// if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ciphertext := aesgcm.Seal(nonce, nonce, buf.Bytes(), nil)
|
||||
|
||||
return base64.URLEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
func (cl *ConfirmationLink[T]) Decrypt(ciphertext string) (*T, error) {
|
||||
block, err := aes.NewCipher(cl.Secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ct, err := base64.URLEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ct) < 12 {
|
||||
return nil, fmt.Errorf("ciphertext too short")
|
||||
}
|
||||
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
plaintext, err := aesgcm.Open(nil, ct[:12], ct[12:], nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 解出golang的结构体
|
||||
var protected T
|
||||
var buf = bytes.NewBuffer(plaintext)
|
||||
err = gob.NewDecoder(buf).Decode(&protected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &protected, nil
|
||||
}
|
||||
80
utils/auth/confirmation_link_test.go
Normal file
80
utils/auth/confirmation_link_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
func BenchmarkConfirmationLink(b *testing.B) {
|
||||
|
||||
type Register struct {
|
||||
Id int64
|
||||
Password string
|
||||
platform string
|
||||
Expired time.Time
|
||||
}
|
||||
|
||||
key := "21321321"
|
||||
cl := NewConfirmationLink[Register](fusenMakeKey(key), "http://localhost:9900/api/auth/oauth2/register")
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
uri, _ := cl.Generate(&Register{Id: 39, Password: "21dsadsad", platform: "google", Expired: time.Now()})
|
||||
u, _ := url.Parse(uri)
|
||||
token := u.Query()["token"]
|
||||
cl.Decrypt(token[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirmationLink(t *testing.T) {
|
||||
|
||||
type Register struct {
|
||||
Id int64
|
||||
Password string
|
||||
platform string
|
||||
Expired time.Time
|
||||
}
|
||||
|
||||
key := "21321321"
|
||||
|
||||
cl := NewConfirmationLink[Register](fusenMakeKey(key), "http://localhost:9900/api/auth/oauth2/register")
|
||||
uri, _ := cl.Generate(&Register{Id: 39, Password: "21dsadsad", platform: "google", Expired: time.Now()})
|
||||
log.Println(uri)
|
||||
|
||||
u, _ := url.Parse(uri)
|
||||
token := u.Query()["token"]
|
||||
log.Println(cl.Decrypt(token[0]))
|
||||
}
|
||||
|
||||
const secret = "your-256-bit-secret"
|
||||
|
||||
func BenchmarkJWT(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
claims := &jwt.StandardClaims{
|
||||
ExpiresAt: time.Now().Unix() + 1020213021,
|
||||
Issuer: "test",
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
ss, err := token.SignedString([]byte(secret))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = jwt.Parse(ss, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, jwt.ErrSignatureInvalid
|
||||
}
|
||||
return []byte(secret), nil
|
||||
})
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,18 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
type RegisterToken struct {
|
||||
Id int64
|
||||
Password string
|
||||
Platform string
|
||||
Expired time.Time
|
||||
}
|
||||
|
||||
func ParseJwtTokenUint64SecretByRequest(r *http.Request, AccessSecret uint64) (jwt.MapClaims, error) {
|
||||
AuthKey := r.Header.Get("Authorization")
|
||||
if AuthKey == "" {
|
||||
@@ -93,37 +101,6 @@ func StringToHash(s string) uint64 {
|
||||
return intHash
|
||||
}
|
||||
|
||||
var secret = []byte("your-secret")
|
||||
|
||||
// func generateConfirmationLink(id, email, password, name string, platform string) (string, error) {
|
||||
// // 创建一个新的 JWT,并将用户的电子邮件设置为它的主题。
|
||||
// token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
// "email": email,
|
||||
// "password": password,
|
||||
// "id": id,
|
||||
// "platform": platform,
|
||||
// "exp": time.Now().Add(24 * time.Hour).Unix(), // Token expires after 24 hours
|
||||
// })
|
||||
|
||||
// // 签署 JWT。
|
||||
// tokenString, err := token.SignedString(secret)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
|
||||
// // 生成确认链接,这个链接包含 JWT。
|
||||
// link := url.URL{
|
||||
// Scheme: "http",
|
||||
// Host: "yourserver.com",
|
||||
// Path: "/confirm",
|
||||
// RawQuery: url.Values{
|
||||
// "token": []string{tokenString},
|
||||
// }.Encode(),
|
||||
// }
|
||||
|
||||
// return link.String(), nil
|
||||
// }
|
||||
|
||||
// func handleConfirm(w http.ResponseWriter, r *http.Request) {
|
||||
// // 从请求中获取 JWT。
|
||||
// tokenString := r.URL.Query().Get("token")
|
||||
|
||||
@@ -3,6 +3,7 @@ package auth
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -127,6 +128,30 @@ func GetBackendUserInfoFormMapClaims(claims jwt.MapClaims) (*BackendUserInfo, er
|
||||
return userinfo, nil
|
||||
}
|
||||
|
||||
// GenerateJwtTokenUint64 网站jwt token生成
|
||||
func GenerateJwtTokenUint64(AccessSecret uint64, accessExpire, nowSec int64, userid int64, guestid int64) (string, error) {
|
||||
claims := make(jwt.MapClaims)
|
||||
claims["exp"] = nowSec + accessExpire
|
||||
claims["iat"] = nowSec
|
||||
|
||||
if userid == 0 && guestid == 0 {
|
||||
err := errors.New("userid and guestid cannot be 0 at the same time")
|
||||
logx.Error(err)
|
||||
return "", err
|
||||
|
||||
}
|
||||
claims["user_id"] = userid
|
||||
claims["guest_id"] = guestid
|
||||
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
token.Claims = claims
|
||||
|
||||
key := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(key, AccessSecret)
|
||||
|
||||
return token.SignedString(key)
|
||||
}
|
||||
|
||||
// GenerateJwtToken 网站jwt token生成
|
||||
func GenerateJwtToken(accessSecret *string, accessExpire, nowSec int64, userid int64, guestid int64) (string, error) {
|
||||
claims := make(jwt.MapClaims)
|
||||
@@ -189,7 +214,9 @@ func getJwtClaims(AuthKey string, AccessSecret *string) (jwt.MapClaims, error) {
|
||||
}
|
||||
|
||||
func PasswordHash(pwd string) string {
|
||||
return base64.URLEncoding.EncodeToString(sha256.New().Sum([]byte(pwd)))
|
||||
h := sha256.New()
|
||||
h.Write([]byte(pwd))
|
||||
return base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func CheckValueRange[T comparable](v T, rangevalues ...T) bool {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -54,7 +57,18 @@ func TestGenBackendJwt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCase1(t *testing.T) {
|
||||
// log.Println(base64.URLEncoding.EncodeToString(sha256.New().Sum([]byte("fusen_backend_2023"))))
|
||||
|
||||
a := sha256.New()
|
||||
a.Write([]byte("fusen_backend_3021"))
|
||||
base64.URLEncoding.EncodeToString(a.Sum(nil))
|
||||
as := fmt.Sprintf("%x", a.Sum(nil))
|
||||
|
||||
log.Println(as, len(as), base64.URLEncoding.EncodeToString(a.Sum(nil)))
|
||||
|
||||
// b := sha256.New().Sum([]byte("fusen_backend_2022"))
|
||||
// bs := fmt.Sprintf("%x", b)
|
||||
// log.Println(bs)
|
||||
// log.Println(as == bs)
|
||||
|
||||
// log.Println(basic.CBCEncrypt("sdfsdfsadsasdasadas", "1232132131232132"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user