You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/models/user.go

160 lines
3.8 KiB
Go

3 years ago
package models
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"github.com/veypi/utils"
"github.com/veypi/utils/log"
"strings"
"time"
)
// User db user model
type User struct {
BaseModel
Username string `json:"username" gorm:"type:varchar(100);unique;not null"`
Nickname string `json:"nickname" gorm:"type:varchar(100)" json:",omitempty"`
Phone string `json:"phone" gorm:"type:varchar(20);unique;default:null" json:",omitempty"`
Email string `json:"email" gorm:"type:varchar(50);unique;default:null" json:",omitempty"`
CheckCode string `gorm:"type:varchar(64);not null" json:"-"`
RealCode string `gorm:"type:varchar(32);not null" json:"-"`
Position string `json:"position"`
// disabled 禁用
Status string `json:"status"`
Icon string `json:"icon"`
Roles []*Role `json:"roles" gorm:"many2many:user_role;"`
}
// TODO:: roles 是否会造成token过大 ?
type PayLoad struct {
ID uint `json:"id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Icon string `json:"icon"`
Iat int64 `json:"iat"` //token time
Exp int64 `json:"exp"`
Roles []uint `json:"roles"`
}
func (p *PayLoad) CheckAuth(name string, tags ...string) AuthLevel {
res := AuthNone
if p == nil || p.Roles == nil {
return res
}
for _, id := range p.Roles {
r := GlobalRoles[id]
if r == nil {
log.Warn().Msgf("not found role id: %d", id)
continue
}
t := r.CheckAuth(name, tags...)
if t > res {
res = t
}
}
return res
}
func (u *User) String() string {
return u.Username + ":" + u.Nickname
}
func (u *User) CheckAuth(name string, tags ...string) AuthLevel {
res := AuthNone
if u == nil || u.Roles == nil {
return res
}
for _, t := range u.Roles {
r := GlobalRoles[t.ID]
if r == nil {
log.Warn().Msgf("not found role id: %d", t.ID)
continue
}
t := r.CheckAuth(name, tags...)
if t > res {
res = t
}
}
return res
}
func (u *User) GetToken(key string) (string, error) {
header := map[string]string{
"typ": "JWT",
"alg": "HS256",
}
//header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}"
now := time.Now().Unix()
payload := PayLoad{
ID: u.ID,
Username: u.Username,
Nickname: u.Nickname,
Icon: u.Icon,
Iat: now,
Exp: now + 60*60*24,
}
for _, r := range u.Roles {
payload.Roles = append(payload.Roles, r.ID)
}
a, err := json.Marshal(header)
if err != nil {
return "", err
}
b, err := json.Marshal(payload)
if err != nil {
return "", err
}
A := base64.StdEncoding.EncodeToString(a)
B := base64.StdEncoding.EncodeToString(b)
hmacCipher := hmac.New(sha256.New, []byte(key))
hmacCipher.Write([]byte(A + "." + B))
C := hmacCipher.Sum(nil)
return A + "." + B + "." + base64.StdEncoding.EncodeToString(C), nil
}
var (
InvalidToken = errors.New("invalid token")
ExpiredToken = errors.New("expired token")
)
func ParseToken(token string, key string, payload *PayLoad) (bool, error) {
var A, B, C string
if seqs := strings.Split(token, "."); len(seqs) == 3 {
A, B, C = seqs[0], seqs[1], seqs[2]
} else {
return false, InvalidToken
}
hmacCipher := hmac.New(sha256.New, []byte(key))
hmacCipher.Write([]byte(A + "." + B))
tempC := hmacCipher.Sum(nil)
if !hmac.Equal([]byte(C), []byte(base64.StdEncoding.EncodeToString(tempC))) {
return false, nil
}
tempPayload, err := base64.StdEncoding.DecodeString(B)
if err != nil {
return false, err
}
if err := json.Unmarshal(tempPayload, payload); err != nil {
return false, err
}
if time.Now().Unix() > payload.Exp {
return false, ExpiredToken
}
return true, nil
}
func (u *User) UpdateAuth(ps string) (err error) {
u.RealCode = utils.RandSeq(32)
u.CheckCode, err = utils.AesEncrypt(u.RealCode, []byte(ps))
return err
}
func (u *User) CheckLogin(ps string) (bool, error) {
temp, err := utils.AesDecrypt(u.CheckCode, []byte(ps))
return temp == u.RealCode, err
}