jwt鉴权

This commit is contained in:
xiabin 2025-01-20 10:10:08 +08:00
parent 271dd8671f
commit a9403daab5
17 changed files with 166 additions and 71 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
.idea .idea
data/

View File

@ -11,7 +11,6 @@ type UserId {
} }
type SetAppUserRequest { type SetAppUserRequest {
Id uint64 `json:"id"`
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
} }
@ -19,6 +18,7 @@ type SetAppUserRequest {
@server ( @server (
group: app_user group: app_user
prefix: /v1/app_user prefix: /v1/app_user
jwt: Auth // 开启 jwt 认证
timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置,不需要则请删除此行 timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置,不需要则请删除此行
maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持 maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持
) )

View File

@ -7,12 +7,6 @@ type DouyinCode2UserIdRequest {
AnonymousCode string `form:"anonymousCode,optional"` AnonymousCode string `form:"anonymousCode,optional"`
} }
type GetEcpmRequest {
AppId string `form:"appId"`
UserId uint64 `form:"userId"`
}
@server( @server(
group: douyin group: douyin
prefix: /v1/douyin // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,不需要则请删除此行 prefix: /v1/douyin // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,不需要则请删除此行
@ -21,10 +15,24 @@ type GetEcpmRequest {
) )
service game_open_api-api { service game_open_api-api {
@handler douyinCode2UserId
get /code2userId (DouyinCode2UserIdRequest) returns (int64)
@handler getEcpm @handler douyinCode2UserId
get /getEcpm (GetEcpmRequest) returns (bool) get /code2userId (DouyinCode2UserIdRequest) returns (string)
}
@server(
group: douyin
prefix: /v1/douyin // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,不需要则请删除此行
jwt: Auth // 开启 jwt 认证
timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置,不需要则请删除此行
maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持
)
service game_open_api-api {
@handler getEcpm
get /getEcpm returns (bool)
} }

View File

@ -6,3 +6,7 @@ DB:
Cache: Cache:
- Host: 127.0.0.1:6379 - Host: 127.0.0.1:6379
type: node type: node
Auth:
AccessSecret: youtu123!
AccessExpire: 3600

View File

@ -4,14 +4,13 @@ syntax = "v1"
type SetUserGameScoreRequest { type SetUserGameScoreRequest {
AppId string `json:"appId"`
Score uint64 `json:"score"` Score uint64 `json:"score"`
UserId uint64 `json:"userId"`
} }
@server ( @server (
group: game group: game
prefix: /v1/game // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,不需要则请删除此行 prefix: /v1/game // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,不需要则请删除此行
jwt: Auth // 开启 jwt 认证
timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置,不需要则请删除此行 timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置,不需要则请删除此行
maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持 maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持
) )

View File

@ -12,4 +12,9 @@ type Config struct {
DataSource string DataSource string
} }
Cache cache.CacheConf Cache cache.CacheConf
Auth struct { // JWT 认证需要的密钥和过期时间配置
AccessSecret string
AccessExpire int64
}
} }

View File

@ -6,19 +6,13 @@ import (
"github.com/zeromicro/go-zero/rest/httpx" "github.com/zeromicro/go-zero/rest/httpx"
"youtu_server/game_open_api/internal/logic/douyin" "youtu_server/game_open_api/internal/logic/douyin"
"youtu_server/game_open_api/internal/svc" "youtu_server/game_open_api/internal/svc"
"youtu_server/game_open_api/internal/types"
) )
func GetEcpmHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { func GetEcpmHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var req types.GetEcpmRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := douyin.NewGetEcpmLogic(r.Context(), svcCtx) l := douyin.NewGetEcpmLogic(r.Context(), svcCtx)
resp, err := l.GetEcpm(&req) resp, err := l.GetEcpm()
if err != nil { if err != nil {
httpx.ErrorCtx(r.Context(), w, err) httpx.ErrorCtx(r.Context(), w, err)
} else { } else {

View File

@ -25,6 +25,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Handler: app_user.AppUserSetUserHandler(serverCtx), Handler: app_user.AppUserSetUserHandler(serverCtx),
}, },
}, },
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/v1/app_user"), rest.WithPrefix("/v1/app_user"),
rest.WithTimeout(3000*time.Millisecond), rest.WithTimeout(3000*time.Millisecond),
rest.WithMaxBytes(1048576), rest.WithMaxBytes(1048576),
@ -37,12 +38,21 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/code2userId", Path: "/code2userId",
Handler: douyin.DouyinCode2UserIdHandler(serverCtx), Handler: douyin.DouyinCode2UserIdHandler(serverCtx),
}, },
},
rest.WithPrefix("/v1/douyin"),
rest.WithTimeout(3000*time.Millisecond),
rest.WithMaxBytes(1048576),
)
server.AddRoutes(
[]rest.Route{
{ {
Method: http.MethodGet, Method: http.MethodGet,
Path: "/getEcpm", Path: "/getEcpm",
Handler: douyin.GetEcpmHandler(serverCtx), Handler: douyin.GetEcpmHandler(serverCtx),
}, },
}, },
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/v1/douyin"), rest.WithPrefix("/v1/douyin"),
rest.WithTimeout(3000*time.Millisecond), rest.WithTimeout(3000*time.Millisecond),
rest.WithMaxBytes(1048576), rest.WithMaxBytes(1048576),
@ -61,6 +71,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Handler: game.RankingSetScoreHandler(serverCtx), Handler: game.RankingSetScoreHandler(serverCtx),
}, },
}, },
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/v1/game"), rest.WithPrefix("/v1/game"),
rest.WithTimeout(3000*time.Millisecond), rest.WithTimeout(3000*time.Millisecond),
rest.WithMaxBytes(1048576), rest.WithMaxBytes(1048576),

View File

@ -25,9 +25,12 @@ func NewAppUserSetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ap
} }
func (l *AppUserSetUserLogic) AppUserSetUser(req *types.SetAppUserRequest) (resp *types.UserId, err error) { func (l *AppUserSetUserLogic) AppUserSetUser(req *types.SetAppUserRequest) (resp *types.UserId, err error) {
// todo: add your logic here and delete this line at, err := svc.GetCtxToken(l.ctx)
if err != nil {
return
}
err = l.svcCtx.AppUser.UpdateNicknameAvatar(l.ctx, &model.AppUser{ err = l.svcCtx.AppUser.UpdateNicknameAvatar(l.ctx, &model.AppUser{
Id: req.Id, Id: at.UserId,
Nickname: req.Nickname, Nickname: req.Nickname,
Avatar: req.Avatar, Avatar: req.Avatar,
}) })

View File

@ -3,6 +3,8 @@ package douyin
import ( import (
"context" "context"
"errors" "errors"
"github.com/golang-jwt/jwt/v4"
"time"
"youtu_server/game_open_api/internal/app_api_helper" "youtu_server/game_open_api/internal/app_api_helper"
"youtu_server/game_open_api/model" "youtu_server/game_open_api/model"
@ -27,7 +29,7 @@ func NewDouyinCode2UserIdLogic(ctx context.Context, svcCtx *svc.ServiceContext)
} }
} }
func (l *DouyinCode2UserIdLogic) DouyinCode2UserId(req *types.DouyinCode2UserIdRequest) (resp int64, err error) { func (l *DouyinCode2UserIdLogic) DouyinCode2UserId(req *types.DouyinCode2UserIdRequest) (resp string, err error) {
douyinCli, err := app_api_helper.DouyinCli.GetDouYinOpenApi(req.AppId) douyinCli, err := app_api_helper.DouyinCli.GetDouYinOpenApi(req.AppId)
if err != nil { if err != nil {
return return
@ -54,6 +56,29 @@ func (l *DouyinCode2UserIdLogic) DouyinCode2UserId(req *types.DouyinCode2UserIdR
Unionid: res.Unionid, Unionid: res.Unionid,
AnonymousOpenid: res.AnonymousOpenid, AnonymousOpenid: res.AnonymousOpenid,
} }
_, err = l.svcCtx.AppUser.Insert(l.ctx, aui) result, err := l.svcCtx.AppUser.Insert(l.ctx, aui)
if err != nil {
return
}
id, err := result.LastInsertId()
if err != nil {
return
}
aui.Id = uint64(id)
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + l.svcCtx.Config.Auth.AccessExpire
claims["iat"] = l.svcCtx.Config.Auth.AccessExpire
claims["payload"] = &svc.AccessToken{
AppId: accountId,
UserId: aui.Id,
AppIdStr: req.AppId,
OpenId: res.Openid,
}
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
resp, err = token.SignedString([]byte(l.svcCtx.Config.Auth.AccessSecret))
return return
} }

View File

@ -5,10 +5,8 @@ import (
"time" "time"
"youtu_server/game_open_api/internal/app_api_helper" "youtu_server/game_open_api/internal/app_api_helper"
"youtu_server/game_open_api/internal/svc"
"youtu_server/game_open_api/internal/types"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
"youtu_server/game_open_api/internal/svc"
) )
type GetEcpmLogic struct { type GetEcpmLogic struct {
@ -26,14 +24,15 @@ func NewGetEcpmLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetEcpmLo
} }
} }
func (l *GetEcpmLogic) GetEcpm(req *types.GetEcpmRequest) (result bool, errNeverNil error) { func (l *GetEcpmLogic) GetEcpm() (result bool, errNeverNil error) {
user, err := l.svcCtx.AppUser.FindOne(l.ctx, req.UserId)
at, err := svc.GetCtxToken(l.ctx)
if err != nil { if err != nil {
l.Logger.Error(err.Error()) errNeverNil = err
return return
} }
res, err := app_api_helper.DouyinCli.GetEcpmData(req.AppId, user.Openid, time.Now().Format(time.DateOnly)) res, err := app_api_helper.DouyinCli.GetEcpmData(at.AppIdStr, at.OpenId, time.Now().Format(time.DateOnly))
if err != nil { if err != nil {
l.Logger.Error(err.Error()) l.Logger.Error(err.Error())
return return
@ -45,7 +44,7 @@ func (l *GetEcpmLogic) GetEcpm(req *types.GetEcpmRequest) (result bool, errNever
return return
} }
if ecpm > float64(app_api_helper.DouyinCli.GetEcpmValue(req.AppId)) && len(res) > int(app_api_helper.DouyinCli.GetEcpmViewCount(req.AppId)) { if ecpm > float64(app_api_helper.DouyinCli.GetEcpmValue(at.AppIdStr)) && len(res) > int(app_api_helper.DouyinCli.GetEcpmViewCount(at.AppIdStr)) {
result = true result = true
} else { } else {
result = false result = false

View File

@ -25,13 +25,13 @@ func NewRankingSetScoreLogic(ctx context.Context, svcCtx *svc.ServiceContext) *R
} }
func (l *RankingSetScoreLogic) RankingSetScore(req *types.SetUserGameScoreRequest) error { func (l *RankingSetScoreLogic) RankingSetScore(req *types.SetUserGameScoreRequest) error {
appId, err := l.svcCtx.AppAccount.FindIdByAppId(l.ctx, req.AppId) at, err := svc.GetCtxToken(l.ctx)
if err != nil { if err != nil {
return err return err
} }
_, err = l.svcCtx.GameScore.Insert(l.ctx, &model.GameScore{ _, err = l.svcCtx.GameScore.Insert(l.ctx, &model.GameScore{
AppUserId: req.UserId, AppUserId: at.UserId,
AppAccount: appId, AppAccount: at.AppId,
Score: req.Score, Score: req.Score,
}) })

View File

@ -3,6 +3,8 @@ package wechat
import ( import (
"context" "context"
"errors" "errors"
"github.com/golang-jwt/jwt/v4"
"time"
"youtu_server/game_open_api/internal/app_api_helper" "youtu_server/game_open_api/internal/app_api_helper"
"youtu_server/game_open_api/model" "youtu_server/game_open_api/model"
@ -27,7 +29,7 @@ func NewWechatCode2UserIdLogic(ctx context.Context, svcCtx *svc.ServiceContext)
} }
} }
func (l *WechatCode2UserIdLogic) WechatCode2UserId(req *types.WechatCode2UserIdRequest) (resp uint64, err error) { func (l *WechatCode2UserIdLogic) WechatCode2UserId(req *types.WechatCode2UserIdRequest) (resp string, err error) {
wechatCli, err := app_api_helper.WechatCli.GetWechatOpenApi(req.AppId) wechatCli, err := app_api_helper.WechatCli.GetWechatOpenApi(req.AppId)
if err != nil { if err != nil {
return return
@ -56,7 +58,30 @@ func (l *WechatCode2UserIdLogic) WechatCode2UserId(req *types.WechatCode2UserIdR
//todo 校验唯一 //todo 校验唯一
_, err = l.svcCtx.AppUser.Insert(l.ctx, aui) result, err := l.svcCtx.AppUser.Insert(l.ctx, aui)
if err != nil {
return
}
id, err := result.LastInsertId()
if err != nil {
return
}
aui.Id = uint64(id)
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + l.svcCtx.Config.Auth.AccessExpire
claims["iat"] = l.svcCtx.Config.Auth.AccessExpire
claims["payload"] = &svc.AccessToken{
AppId: accountId,
UserId: aui.Id,
AppIdStr: req.AppId,
OpenId: res.OpenID,
}
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
resp, err = token.SignedString([]byte(l.svcCtx.Config.Auth.AccessSecret))
return return
} }

View File

@ -0,0 +1,52 @@
package svc
import (
"context"
"encoding/json"
"errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"youtu_server/game_open_api/internal/config"
"youtu_server/game_open_api/model"
)
type ServiceContext struct {
Config config.Config
AppUser model.AppUserModel
GameScore model.GameScoreModel
AppAccount model.AppAccountModel
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
AppUser: model.NewAppUserModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
GameScore: model.NewGameScoreModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
AppAccount: model.NewAppAccountModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
}
}
type AccessToken struct {
AppId uint64 `json:"appId"`
UserId uint64 `json:"userId"`
AppIdStr string `json:"appIdStr"`
OpenId string `json:"openId"`
}
func UnmarshalAccessToken(d any) (ac AccessToken, err error) {
m, ok := d.(map[string]interface{})
if !ok {
err = errors.New("invalid access token")
return
}
appId, _ := m["appId"].(json.Number).Int64()
ac.AppId = uint64(appId)
userId, _ := m["userId"].(json.Number).Int64()
ac.UserId = uint64(userId)
ac.AppIdStr = m["appIdStr"].(string)
ac.OpenId = m["openId"].(string)
return
}
func GetCtxToken(ctx context.Context) (ac AccessToken, err error) {
return UnmarshalAccessToken(ctx.Value("payload"))
}

View File

@ -1,23 +0,0 @@
package svc
import (
"github.com/zeromicro/go-zero/core/stores/sqlx"
"youtu_server/game_open_api/internal/config"
"youtu_server/game_open_api/model"
)
type ServiceContext struct {
Config config.Config
AppUser model.AppUserModel
GameScore model.GameScoreModel
AppAccount model.AppAccountModel
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
AppUser: model.NewAppUserModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
GameScore: model.NewGameScoreModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
AppAccount: model.NewAppAccountModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
}
}

View File

@ -9,21 +9,13 @@ type DouyinCode2UserIdRequest struct {
AnonymousCode string `form:"anonymousCode,optional"` AnonymousCode string `form:"anonymousCode,optional"`
} }
type GetEcpmRequest struct {
AppId string `form:"appId"`
UserId uint64 `form:"userId"`
}
type SetAppUserRequest struct { type SetAppUserRequest struct {
Id uint64 `json:"id"`
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
} }
type SetUserGameScoreRequest struct { type SetUserGameScoreRequest struct {
AppId string `json:"appId"` Score uint64 `json:"score"`
Score uint64 `json:"score"`
UserId uint64 `json:"userId"`
} }
type UserId struct { type UserId struct {

View File

@ -15,6 +15,6 @@ type WechatCode2UserIdRequest {
service game_open_api-api { service game_open_api-api {
@handler wechatCode2UserId @handler wechatCode2UserId
get /code2userId (WechatCode2UserIdRequest) returns (uint64 ) get /code2userId (WechatCode2UserIdRequest) returns (string )
} }