用户加密机制设计初步完成
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"OneAuth/libs/auth"
|
||||
"OneAuth/libs/oerr"
|
||||
"OneAuth/models"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func AddUser(tx *gorm.DB, appID uint, userID uint, roleID uint) error {
|
||||
au := &models.AppUser{}
|
||||
au.AppID = appID
|
||||
au.UserID = userID
|
||||
err := tx.Where(au).First(au).Error
|
||||
if err == nil {
|
||||
return oerr.ResourceDuplicated
|
||||
}
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = tx.Create(au).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = auth.BindUserRole(tx, userID, roleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Model(&models.App{}).Where("id = ?", appID).Update("user_count", gorm.Expr("user_count + ?", 1)).Error
|
||||
}
|
||||
return err
|
||||
}
|
||||
func EnableUser(tx *gorm.DB, appID uint, userID uint) error {
|
||||
au := &models.AppUser{}
|
||||
au.AppID = appID
|
||||
au.UserID = userID
|
||||
err := tx.Where(au).First(au).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Where(au).Update("disabled", false).Error
|
||||
}
|
||||
|
||||
func DisableUser(tx *gorm.DB, appID uint, userID uint) error {
|
||||
au := &models.AppUser{}
|
||||
au.AppID = appID
|
||||
au.UserID = userID
|
||||
return tx.Where(au).Update("disabled", true).Error
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"OneAuth/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 定义oa系统权限
|
||||
|
||||
type Resource = string
|
||||
|
||||
const (
|
||||
User Resource = "user"
|
||||
APP Resource = "app"
|
||||
Res Resource = "resource"
|
||||
Role Resource = "role"
|
||||
Auth Resource = "auth"
|
||||
)
|
||||
|
||||
func BindUserRole(tx *gorm.DB, userID uint, roleID uint) error {
|
||||
r := &models.Role{}
|
||||
r.ID = roleID
|
||||
err := tx.Where(r).First(r).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ur := &models.UserRole{}
|
||||
ur.RoleID = roleID
|
||||
if r.IsUnique {
|
||||
err = tx.Where(ur).Update("user_id", userID).Error
|
||||
} else {
|
||||
ur.UserID = userID
|
||||
err = tx.Where(ur).FirstOrCreate(ur).Error
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func BindUserAuth(tx *gorm.DB, userID uint, resID uint, level models.AuthLevel, ruid string) error {
|
||||
return bind(tx, userID, resID, level, ruid, false)
|
||||
}
|
||||
|
||||
func BindRoleAuth(tx *gorm.DB, roleID uint, resID uint, level models.AuthLevel, ruid string) error {
|
||||
return bind(tx, roleID, resID, level, ruid, true)
|
||||
}
|
||||
|
||||
func bind(tx *gorm.DB, id uint, resID uint, level models.AuthLevel, ruid string, isRole bool) error {
|
||||
r := &models.Resource{}
|
||||
r.ID = resID
|
||||
err := tx.Where(r).First(r).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
au := &models.Auth{
|
||||
AppID: r.AppID,
|
||||
ResourceID: resID,
|
||||
RID: r.Name,
|
||||
RUID: ruid,
|
||||
Level: level,
|
||||
}
|
||||
if isRole {
|
||||
au.RoleID = &id
|
||||
} else {
|
||||
au.UserID = &id
|
||||
}
|
||||
return tx.Where(au).FirstOrCreate(au).Error
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"OneAuth/libs/auth"
|
||||
"OneAuth/libs/oerr"
|
||||
"OneAuth/libs/tools"
|
||||
"github.com/json-iterator/go"
|
||||
@@ -17,7 +16,7 @@ var json = jsoniter.ConfigFastest
|
||||
|
||||
type ApiHandler struct {
|
||||
OneBD.BaseHandler
|
||||
auth.UserHandler
|
||||
UserHandler
|
||||
}
|
||||
|
||||
func (h *ApiHandler) Init(m OneBD.Meta) error {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package auth
|
||||
package base
|
||||
|
||||
import (
|
||||
"OneAuth/cfg"
|
||||
"OneAuth/libs/oerr"
|
||||
"OneAuth/libs/token"
|
||||
"OneAuth/models"
|
||||
"github.com/veypi/OneBD"
|
||||
"github.com/veypi/OneBD/rfc"
|
||||
)
|
||||
|
||||
type UserHandler struct {
|
||||
Payload *models.PayLoad
|
||||
Payload *token.PayLoad
|
||||
ignoreMethod map[rfc.Method]bool
|
||||
}
|
||||
|
||||
@@ -17,12 +17,16 @@ func (a *UserHandler) Init(m OneBD.Meta) error {
|
||||
if a.ignoreMethod != nil && a.ignoreMethod[m.Method()] {
|
||||
return nil
|
||||
}
|
||||
a.Payload = new(models.PayLoad)
|
||||
token := m.GetHeader("auth_token")
|
||||
if token == "" {
|
||||
return a.ParsePayload(m)
|
||||
}
|
||||
|
||||
func (a *UserHandler) ParsePayload(m OneBD.Meta) error {
|
||||
a.Payload = new(token.PayLoad)
|
||||
tokenStr := m.GetHeader("auth_token")
|
||||
if tokenStr == "" {
|
||||
return oerr.NotLogin
|
||||
}
|
||||
ok, err := models.ParseToken(token, cfg.CFG.Key, a.Payload)
|
||||
ok, err := token.ParseToken(tokenStr, a.Payload)
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package key
|
||||
|
||||
import "OneAuth/cfg"
|
||||
|
||||
func App(id uint) string {
|
||||
if id == cfg.CFG.APPID {
|
||||
return cfg.CFG.APPKey
|
||||
}
|
||||
// TODO
|
||||
return ""
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
package auth
|
||||
package key
|
||||
|
||||
import (
|
||||
"OneAuth/models"
|
||||
"OneAuth/cfg"
|
||||
"github.com/veypi/utils"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var keyCache = sync.Map{}
|
||||
|
||||
func GetUserKey(uid uint, app *models.App) string {
|
||||
if app.ID == 1 {
|
||||
func User(uid uint, appID uint) string {
|
||||
if appID == cfg.CFG.APPID {
|
||||
key, _ := keyCache.LoadOrStore(uid, utils.RandSeq(16))
|
||||
return key.(string)
|
||||
return cfg.CFG.APPKey + key.(string)
|
||||
}
|
||||
// TODO: 获取其他应用user_key
|
||||
return ""
|
||||
}
|
||||
|
||||
func RefreshUserKey(uid uint, app *models.App) string {
|
||||
if app.ID == 1 {
|
||||
func RefreshUser(uid uint, appID uint) string {
|
||||
if appID == cfg.CFG.APPID {
|
||||
key := utils.RandSeq(16)
|
||||
keyCache.Store(uid, key)
|
||||
return key
|
||||
@@ -0,0 +1,127 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"OneAuth/libs/key"
|
||||
"OneAuth/models"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
InvalidToken = errors.New("invalid token")
|
||||
ExpiredToken = errors.New("expired token")
|
||||
)
|
||||
|
||||
type simpleAuth struct {
|
||||
RID string `json:"rid"`
|
||||
// 具体某个资源的id
|
||||
RUID string `json:"ruid"`
|
||||
Level models.AuthLevel `json:"level"`
|
||||
}
|
||||
|
||||
// TODO:: roles 是否会造成token过大 ?
|
||||
type PayLoad struct {
|
||||
ID uint `json:"id"`
|
||||
AppID uint `json:"app_id"`
|
||||
Iat int64 `json:"iat"` //token time
|
||||
Exp int64 `json:"exp"`
|
||||
Auth map[uint]*simpleAuth `json:"auth"`
|
||||
}
|
||||
|
||||
// GetAuth resource_uuid 缺省或仅第一个有效 权限会被更高权限覆盖
|
||||
func (p *PayLoad) GetAuth(ResourceID string, ResourceUUID ...string) models.AuthLevel {
|
||||
res := models.AuthNone
|
||||
if p == nil || p.Auth == nil {
|
||||
return res
|
||||
}
|
||||
ruid := ""
|
||||
if len(ResourceUUID) > 0 {
|
||||
ruid = ResourceUUID[0]
|
||||
}
|
||||
for _, a := range p.Auth {
|
||||
if a.RID == ResourceID {
|
||||
if a.RUID != "" {
|
||||
if a.RUID == ruid {
|
||||
if a.Level > res {
|
||||
res = a.Level
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if a.Level > res {
|
||||
res = a.Level
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func GetToken(u *models.User, appID uint) (string, error) {
|
||||
header := map[string]string{
|
||||
"typ": "JWT",
|
||||
"alg": "HS256",
|
||||
}
|
||||
//header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}"
|
||||
now := time.Now().Unix()
|
||||
payload := PayLoad{
|
||||
ID: u.ID,
|
||||
AppID: appID,
|
||||
Iat: now,
|
||||
Exp: now + 60*60*24,
|
||||
Auth: map[uint]*simpleAuth{},
|
||||
}
|
||||
for _, a := range u.GetAuths() {
|
||||
if appID == a.AppID {
|
||||
payload.Auth[a.ID] = &simpleAuth{
|
||||
RID: a.RID,
|
||||
RUID: a.RUID,
|
||||
Level: a.Level,
|
||||
}
|
||||
}
|
||||
}
|
||||
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.User(payload.ID, payload.AppID)))
|
||||
hmacCipher.Write([]byte(A + "." + B))
|
||||
C := hmacCipher.Sum(nil)
|
||||
return A + "." + B + "." + base64.StdEncoding.EncodeToString(C), nil
|
||||
}
|
||||
|
||||
func ParseToken(token 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
|
||||
}
|
||||
tempPayload, err := base64.StdEncoding.DecodeString(B)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := json.Unmarshal(tempPayload, payload); err != nil {
|
||||
return false, err
|
||||
}
|
||||
hmacCipher := hmac.New(sha256.New, []byte(key.User(payload.ID, payload.AppID)))
|
||||
hmacCipher.Write([]byte(A + "." + B))
|
||||
tempC := hmacCipher.Sum(nil)
|
||||
if !hmac.Equal([]byte(C), []byte(base64.StdEncoding.EncodeToString(tempC))) {
|
||||
return false, nil
|
||||
}
|
||||
if time.Now().Unix() > payload.Exp {
|
||||
return false, ExpiredToken
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user