package token import ( "encoding/hex" "net/http" "oa/cfg" "oa/errs" "oa/libs/auth" M "oa/models" "time" "github.com/golang-jwt/jwt/v5" "github.com/veypi/OneBD/rest" "github.com/veypi/utils" "github.com/veypi/utils/logv" ) func useToken(r rest.Router) { r.Post("/salt", tokenSalt) r.Post("/", tokenPost) r.Get("/:token_id", tokenGet) r.Patch("/:token_id", tokenPatch) r.Delete("/:token_id", tokenDelete) r.Get("/", tokenList) } func tokenSalt(x *rest.X) (any, error) { opts := &M.TokenSalt{} err := x.Parse(opts) if err != nil { return nil, err } data := &M.User{} query := "username = ?" if opts.Typ == nil { } else if *opts.Typ == "email" { query = "email = ?" } else if *opts.Typ == "phone" { query = "phone = ?" } err = cfg.DB().Where(query, opts.Username).First(data).Error return map[string]string{"salt": data.Salt, "id": data.ID}, err } // for user login app func tokenPost(x *rest.X) (any, error) { opts := &M.TokenPost{} err := x.Parse(opts) if err != nil { return nil, err } aid := cfg.Config.ID if opts.AppID != nil { aid = *opts.AppID } data := &M.Token{} claim := &auth.Claims{} claim.IssuedAt = jwt.NewNumericDate(time.Now()) claim.Issuer = "oa" if opts.Refresh != nil { typ := "app" if opts.Typ != nil { typ = *opts.Typ } // for other app redirect refresh, err := auth.ParseJwt(*opts.Refresh) if err != nil { return nil, err } if refresh.ID == "" { return nil, errs.AuthInvalid } err = cfg.DB().Where("id = ?", refresh.ID).First(data).Error if err != nil { return nil, err } if typ == "app" { if refresh.AID == aid { // refresh token claim.AID = refresh.AID claim.UID = refresh.UID claim.Name = refresh.Name claim.Icon = refresh.Icon claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 10)) acList := make(auth.Access, 0, 10) logv.AssertError(cfg.DB().Table("accesses a"). Select("a.name, a.t_id, a.level"). Joins("INNER JOIN user_roles ur ON ur.role_id = a.role_id AND ur.user_id = ?", refresh.UID). Scan(&acList).Error) claim.Access = acList } else { // gen other app token } } else if typ == "ufs" { claim.AID = refresh.AID claim.UID = refresh.UID claim.Name = refresh.Name claim.Icon = refresh.Icon claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 10)) claim.Access = auth.Access{ {Name: "fs", TID: "/", Level: auth.Do}, } token := logv.AssertFuncErr(auth.GenJwt(claim)) cookie := &http.Cookie{ Name: "fstoken", // Cookie 的名称 Value: token, // Cookie 的值 Path: "/fs/u/", // Cookie 的路径,通常是根路径 MaxAge: 600, // Cookie 的最大年龄,单位是秒 HttpOnly: true, // 是否仅限 HTTP(S) 访问 Secure: false, // 是否通过安全连接传输 Cookie } http.SetCookie(x, cookie) return token, nil } } else if opts.Code != nil && aid == cfg.Config.ID && opts.Salt != nil && opts.UserID != nil { // for oa login user := &M.User{} err = cfg.DB().Where("id = ?", opts.UserID).Find(user).Error if err != nil { return nil, err } logv.Info().Str("user", user.ID).Msg("login") code := *opts.Code salt := logv.AssertFuncErr(hex.DecodeString(*opts.Salt)) key := logv.AssertFuncErr(hex.DecodeString(user.Code)) de, err := utils.AesDecrypt([]byte(code), key, salt) if err != nil || de != user.ID { return nil, errs.AuthFailed } data.UserID = *opts.UserID data.AppID = aid if opts.ExpiredAt != nil { data.ExpiredAt = *opts.ExpiredAt } else { data.ExpiredAt = time.Now().Add(time.Hour * 72) } if opts.OverPerm != nil { data.OverPerm = *opts.OverPerm } if opts.Device != nil { data.Device = *opts.Device } data.Ip = x.GetRemoteIp() logv.AssertError(cfg.DB().Create(data).Error) claim.ID = data.ID claim.AID = aid claim.UID = user.ID claim.Name = user.Username claim.Icon = user.Icon claim.ExpiresAt = jwt.NewNumericDate(data.ExpiredAt) if user.Nickname != "" { claim.Name = user.Nickname } } else { return nil, errs.ArgsInvalid } token := logv.AssertFuncErr(auth.GenJwt(claim)) return token, err } func tokenGet(x *rest.X) (any, error) { opts := &M.TokenGet{} err := x.Parse(opts) if err != nil { return nil, err } data := &M.Token{} err = cfg.DB().Where("id = ?", opts.ID).First(data).Error return data, err } func tokenPatch(x *rest.X) (any, error) { opts := &M.TokenPatch{} err := x.Parse(opts) if err != nil { return nil, err } data := &M.Token{} err = cfg.DB().Where("id = ?", opts.ID).First(data).Error if err != nil { return nil, err } optsMap := make(map[string]interface{}) if opts.ExpiredAt != nil { optsMap["expired_at"] = opts.ExpiredAt } if opts.OverPerm != nil { optsMap["over_perm"] = opts.OverPerm } err = cfg.DB().Model(data).Updates(optsMap).Error return data, err } func tokenDelete(x *rest.X) (any, error) { opts := &M.TokenDelete{} err := x.Parse(opts) if err != nil { return nil, err } data := &M.Token{} err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error return data, err } func tokenList(x *rest.X) (any, error) { opts := &M.TokenList{} err := x.Parse(opts) if err != nil { return nil, err } data := make([]*M.Token, 0, 10) query := cfg.DB() query = query.Where("user_id = ?", opts.UserID) query = query.Where("app_id = ?", opts.AppID) err = query.Find(&data).Error return data, err }