feat(app): 添加用户退出登录功能
- 在 app.go 中新增 LogoutUser 方法及对应请求响应结构体 - 更新 app.json API 文档,移除旧 /logout 接口,新增 /user/logout 接口 - 在 app.proto 中定义 LogoutUserRequest 和 UserToken 消息类型 - 生成新的 app.pb.go 和 app_grpc.pb.go 文件以支持新接口 - 配置文件 app.yaml 中增加 RPC 调用的超时和重试设置 - 实现 gRPC 客户端和服务端对 LogoutUser 接口的支持
This commit is contained in:
parent
65ff8ddb62
commit
c3406ce964
49
api/app.json
49
api/app.json
|
|
@ -92,34 +92,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/logout": {
|
||||
"post": {
|
||||
"description": "登出",
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "登出",
|
||||
"operationId": "Logout",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/IDReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "BaseDataInfo",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/BaseDataInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/register": {
|
||||
"post": {
|
||||
"description": "Register | 注册",
|
||||
|
|
@ -206,11 +178,10 @@
|
|||
},
|
||||
"/user/info": {
|
||||
"post": {
|
||||
"description": "@ Get UserInfo detail By Token | 获取用户信息",
|
||||
"description": "Get UserInfo detail By Token | 获取用户信息",
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "@ Get UserInfo detail By Token | 获取用户信息",
|
||||
"operationId": "GetUserInfoByToken",
|
||||
"responses": {
|
||||
"200": {
|
||||
|
|
@ -250,6 +221,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/user/logout": {
|
||||
"post": {
|
||||
"description": "Logout | 退出登录 不传参数,默认使用请求头的token",
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Logout | 退出登录 不传参数,默认使用请求头的token",
|
||||
"operationId": "Logout",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "BaseMsgResp",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/BaseMsgResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/oauthAuthorize": {
|
||||
"post": {
|
||||
"description": "OauthAuthorize | 第三方登录接口",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ type (
|
|||
CaptchaCode string `json:"captchaCode,optional"`
|
||||
// 过期时间
|
||||
ExpireTime uint32 `json:"expireTime,optional"`
|
||||
|
||||
}
|
||||
RegisterReq {
|
||||
// 账户注册类型:手机或邮箱
|
||||
|
|
@ -31,89 +30,83 @@ type (
|
|||
Value string `json:"value,optional"`
|
||||
// 昵称
|
||||
NickName string `json:"nickName,optional"`
|
||||
// 性别
|
||||
Sex uint8 `json:"sex,optional"`
|
||||
//生日
|
||||
Birthday uint32 `json:"birthday,optional"`
|
||||
// 密码加密后的值
|
||||
PasswordHash *string `json:"passwordHash,optional"`
|
||||
//验证码
|
||||
Captcha string `json:"captcha,optional"`
|
||||
// 验证码ID
|
||||
CaptchaId string `json:"captchaId,optional"`
|
||||
//VerificationType 类型 1 手机 2 邮箱
|
||||
VerificationType *uint32 `json:"verificationType,optional"`
|
||||
// Gender male or female or other , 男,女,其他
|
||||
Gender *string `json:"gender,optional"`
|
||||
// 注册来源 app,pc
|
||||
RegisterSource *string `json:"registerSource,optional"`
|
||||
// 性别
|
||||
Sex uint8 `json:"sex,optional"`
|
||||
//生日
|
||||
Birthday uint32 `json:"birthday,optional"`
|
||||
// 密码加密后的值
|
||||
PasswordHash *string `json:"passwordHash,optional"`
|
||||
//验证码
|
||||
Captcha string `json:"captcha,optional"`
|
||||
// 验证码ID
|
||||
CaptchaId string `json:"captchaId,optional"`
|
||||
//VerificationType 类型 1 手机 2 邮箱
|
||||
VerificationType *uint32 `json:"verificationType,optional"`
|
||||
// Gender male or female or other , 男,女,其他
|
||||
Gender *string `json:"gender,optional"`
|
||||
// 注册来源 app,pc
|
||||
RegisterSource *string `json:"registerSource,optional"`
|
||||
}
|
||||
|
||||
|
||||
RegisterResp{
|
||||
// token信息
|
||||
AuthToken *AuthToken `json:"authToken,optional"`
|
||||
User *UserInfo `json:"userInfo,optional"`
|
||||
RegisterResp {
|
||||
// token信息
|
||||
AuthToken *AuthToken `json:"authToken,optional"`
|
||||
User *UserInfo `json:"userInfo,optional"`
|
||||
}
|
||||
|
||||
AuthToken{
|
||||
// access_token
|
||||
AccessToken string `json:"accessToken,optional"`
|
||||
// refresh_token
|
||||
RefreshToken string `json:"refreshToken,optional"`
|
||||
// token_type 类型
|
||||
TokenType string `json:"tokenType,optional"`
|
||||
//access_token_expires
|
||||
AccessTokenExpires int64 `json:"accessTokenExpires,optional"`
|
||||
//refresh_token_expires
|
||||
RefreshTokenExpires int64 `json:"refreshTokenExpires,optional"`
|
||||
AuthToken {
|
||||
// access_token
|
||||
AccessToken string `json:"accessToken,optional"`
|
||||
// refresh_token
|
||||
RefreshToken string `json:"refreshToken,optional"`
|
||||
// token_type 类型
|
||||
TokenType string `json:"tokenType,optional"`
|
||||
//access_token_expires
|
||||
AccessTokenExpires int64 `json:"accessTokenExpires,optional"`
|
||||
//refresh_token_expires
|
||||
RefreshTokenExpires int64 `json:"refreshTokenExpires,optional"`
|
||||
}
|
||||
|
||||
UserInfo{
|
||||
BaseIDInfo
|
||||
// 昵称
|
||||
NickName string `json:"nickName,optional"`
|
||||
//生日
|
||||
Birthday *int64 `json:"birthday,optional"`
|
||||
// 头像
|
||||
Avatar *string `json:"avatar,optional"`
|
||||
// Gender male or female or other , 男,女,其他
|
||||
Gender *string `json:"gender,optional"`
|
||||
// 账户注册类型:
|
||||
// 1 手机 2 邮箱
|
||||
RegisterType *uint32 `json:"registerType,optional"`
|
||||
// 手机号
|
||||
Mobile *string `json:"mobile,optional"`
|
||||
// 用户名
|
||||
Username *string `json:"username,optional"`
|
||||
//邮箱
|
||||
Email *string `json:"email,optional"`
|
||||
}
|
||||
|
||||
LoginReq{
|
||||
// UserName or Email or Mobile
|
||||
UserName string `json:"userName,optional"`
|
||||
// Password
|
||||
Password string `json:"password"`
|
||||
// ClientIP
|
||||
ClientIP string `json:"clientIp"`
|
||||
// 登录方式
|
||||
LoginType string `json:"loginType"`
|
||||
// 登录系统
|
||||
LoginPlatform string `json:"loginPlatform"`
|
||||
}
|
||||
LoginResp{
|
||||
BaseMsgResp
|
||||
AuthToken *AuthToken
|
||||
UserInfo *UserInfo
|
||||
}
|
||||
UserInfoResp{
|
||||
UserInfo {
|
||||
BaseIDInfo
|
||||
// UserInfo information | UserInfo信息数据
|
||||
Data UserInfo `json:"data"`
|
||||
// 昵称
|
||||
NickName string `json:"nickName,optional"`
|
||||
//生日
|
||||
Birthday *int64 `json:"birthday,optional"`
|
||||
// 头像
|
||||
Avatar *string `json:"avatar,optional"`
|
||||
// Gender male or female or other , 男,女,其他
|
||||
Gender *string `json:"gender,optional"`
|
||||
// 账户注册类型:
|
||||
// 1 手机 2 邮箱
|
||||
RegisterType *uint32 `json:"registerType,optional"`
|
||||
// 手机号
|
||||
Mobile *string `json:"mobile,optional"`
|
||||
// 用户名
|
||||
Username *string `json:"username,optional"`
|
||||
//邮箱
|
||||
Email *string `json:"email,optional"`
|
||||
}
|
||||
|
||||
UserListReq{
|
||||
LoginReq {
|
||||
// UserName or Email or Mobile
|
||||
UserName string `json:"userName,optional"`
|
||||
// Password
|
||||
Password string `json:"password"`
|
||||
// ClientIP
|
||||
ClientIP string `json:"clientIp"`
|
||||
// 登录方式
|
||||
LoginType string `json:"loginType"`
|
||||
// 登录系统
|
||||
LoginPlatform string `json:"loginPlatform"`
|
||||
}
|
||||
LoginResp {
|
||||
BaseMsgResp
|
||||
AuthToken *AuthToken
|
||||
UserInfo *UserInfo
|
||||
}
|
||||
UserInfoResp {
|
||||
BaseIDInfo
|
||||
// UserInfo information | UserInfo信息数据
|
||||
Data UserInfo `json:"data"`
|
||||
}
|
||||
UserListReq {
|
||||
// Page
|
||||
PageInfo
|
||||
// UserName or Email or Mobile
|
||||
|
|
@ -125,89 +118,88 @@ type (
|
|||
// Status 1: normal 2: ban | 状态 1 正常 2 禁用
|
||||
Status *string `json:"status,optional"`
|
||||
}
|
||||
|
||||
// The response data of user list | User信息列表数据
|
||||
// The response data of user list | User信息列表数据
|
||||
UserListResp {
|
||||
BaseDataInfo
|
||||
// The device list data | USer信息列表数据
|
||||
Data UserListInfo `json:"data"`
|
||||
}
|
||||
|
||||
// The device list data | User信息列表数据
|
||||
UserListInfo {
|
||||
BaseListInfo
|
||||
// The device list data | User信息列表数据
|
||||
Data []UserInfo `json:"data"`
|
||||
}
|
||||
OauthAuthorizeReq {
|
||||
UserListInfo {
|
||||
BaseListInfo
|
||||
// The device list data | User信息列表数据
|
||||
Data []UserInfo `json:"data"`
|
||||
}
|
||||
OauthAuthorizeReq {}
|
||||
|
||||
|
||||
OauthAuthorizeResp {
|
||||
BaseMsgResp
|
||||
}
|
||||
PassWordResetReq {
|
||||
// UserName or Email or Mobile
|
||||
UserName string `json:"userName,optional"`
|
||||
// Password
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
OauthAuthorizeResp {
|
||||
BaseMsgResp
|
||||
}
|
||||
PassWordResetReq {
|
||||
// UserName or Email or Mobile
|
||||
UserName string `json:"userName,optional"`
|
||||
// Password
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
@server (
|
||||
group: user_public
|
||||
group: user_public
|
||||
)
|
||||
|
||||
service App {
|
||||
// GetVerifyCode | 获取验证码
|
||||
@handler getVerifyCode
|
||||
post /get/verifyCode (VerifyCodeReq) returns (VerifyCodeResp)
|
||||
|
||||
// Register | 注册
|
||||
@handler register
|
||||
post /register (RegisterReq) returns (RegisterResp)
|
||||
// Login | 登录
|
||||
@handler login
|
||||
post /login (LoginReq) returns (LoginResp)
|
||||
}
|
||||
post /register (RegisterReq) returns (RegisterResp)
|
||||
|
||||
// Login | 登录
|
||||
@handler login
|
||||
post /login (LoginReq) returns (LoginResp)
|
||||
}
|
||||
|
||||
@server (
|
||||
group: user
|
||||
middleware: Authority
|
||||
)
|
||||
|
||||
service App {
|
||||
// Get userInfo detail by ID | 获取用户信息
|
||||
// Get userInfo detail by ID | 获取用户信息
|
||||
@handler getUserInfoById
|
||||
post /user (IDReq) returns (UserInfo)
|
||||
|
||||
// Get UserInfo detail By Token | 获取用户信息
|
||||
@handler getUserInfoByToken
|
||||
post /user/info () returns (UserInfo)
|
||||
|
||||
post /user/info returns (UserInfo)
|
||||
|
||||
//List userInfo | 获取用户列表
|
||||
@handler listUserInfo
|
||||
post /user/list (UserListReq) returns (UserListResp)
|
||||
|
||||
//Update userInfo | 更新用户信息
|
||||
@handler updateUserInfo
|
||||
post /user/update (UserInfo) returns (BaseDataInfo)
|
||||
post /user/update (UserInfo) returns (BaseMsgResp)
|
||||
|
||||
// Logout | 退出登录
|
||||
// Logout | 退出登录 不传参数,默认使用请求头的token
|
||||
@handler logout
|
||||
post /user/logout (IDReq) returns (BaseDataInfo)
|
||||
post /user/logout () returns (BaseMsgResp)
|
||||
|
||||
// PassWordReset | 修改密码
|
||||
@handler passWordReset
|
||||
post /user/passWordReset (PassWordResetReq) returns (BaseDataInfo)
|
||||
// PassWordReset | 修改密码
|
||||
@handler passWordReset
|
||||
post /user/passWordReset (PassWordResetReq) returns (BaseDataInfo)
|
||||
|
||||
// CheckLogin | 检测登录状态
|
||||
@handler checkLogin
|
||||
post /user/checkLogin (IDReq) returns (BaseDataInfo)
|
||||
// CheckLogin | 检测登录状态
|
||||
@handler checkLogin
|
||||
post /user/checkLogin (IDReq) returns (BaseDataInfo)
|
||||
|
||||
//OauthAuthorize | 第三方登录接口
|
||||
@handler oauthAuthorize
|
||||
post /user/oauthAuthorize (OauthAuthorizeReq) returns (OauthAuthorizeResp)
|
||||
//OauthAuthorize | 第三方登录接口
|
||||
@handler oauthAuthorize
|
||||
post /user/oauthAuthorize (OauthAuthorizeReq) returns (OauthAuthorizeResp)
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,7 @@ AppRpc:
|
|||
Hosts:
|
||||
- 192.168.201.58:2379 # 共享etcd地址
|
||||
Key: app.rpc
|
||||
Enabled: true
|
||||
Enabled: true
|
||||
NonBlock: true
|
||||
Timeout: 3000
|
||||
RetryCount: 1 # 重试次数,如果为1,则最多调用2次(包括第一次)
|
||||
|
|
@ -11,9 +11,8 @@ import (
|
|||
|
||||
// swagger:route post /user/info user GetUserInfoByToken
|
||||
//
|
||||
//@ Get UserInfo detail By Token | 获取用户信息
|
||||
//
|
||||
//@ Get UserInfo detail By Token | 获取用户信息
|
||||
// Get UserInfo detail By Token | 获取用户信息
|
||||
//
|
||||
// Responses:
|
||||
// 200: UserInfo
|
||||
|
|
|
|||
|
|
@ -7,34 +7,21 @@ import (
|
|||
|
||||
"mingyang-admin-app-api/internal/logic/user"
|
||||
"mingyang-admin-app-api/internal/svc"
|
||||
"mingyang-admin-app-api/internal/types"
|
||||
)
|
||||
|
||||
// swagger:route post /logout user Logout
|
||||
// swagger:route post /user/logout user Logout
|
||||
//
|
||||
//登出
|
||||
// Logout | 退出登录 不传参数,默认使用请求头的token
|
||||
//
|
||||
//登出
|
||||
//
|
||||
// Parameters:
|
||||
// + name: body
|
||||
// require: true
|
||||
// in: body
|
||||
// type: IDReq
|
||||
// Logout | 退出登录 不传参数,默认使用请求头的token
|
||||
//
|
||||
// Responses:
|
||||
// 200: BaseDataInfo
|
||||
// 200: BaseMsgResp
|
||||
|
||||
func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.IDReq
|
||||
if err := httpx.Parse(r, &req, true); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := user.NewLogoutLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Logout(&req)
|
||||
resp, err := l.Logout()
|
||||
if err != nil {
|
||||
err = svcCtx.Trans.TransError(r.Context(), err)
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/saas-mingyang/mingyang-admin-common/i18n"
|
||||
"mingyang-admin-app-api/internal/svc"
|
||||
"mingyang-admin-app-api/internal/types"
|
||||
"mingyang-admin-app-rpc/types/app"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
|
@ -23,8 +30,96 @@ func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogi
|
|||
}
|
||||
}
|
||||
|
||||
func (l *LogoutLogic) Logout(req *types.IDReq) (resp *types.BaseDataInfo, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
// Logout 在API层添加调用栈信息
|
||||
func (l *LogoutLogic) Logout() (resp *types.BaseMsgResp, err error) {
|
||||
// 打印调用栈
|
||||
buf := make([]byte, 1024)
|
||||
n := runtime.Stack(buf, false)
|
||||
l.Logger.Infof("Logout call stack:\n%s", buf[:n])
|
||||
|
||||
return
|
||||
token := l.getTokenFromContext()
|
||||
l.Infof("Starting Logout RPC call at: %v", time.Now())
|
||||
|
||||
// 记录goroutine ID
|
||||
goID := getGoroutineID()
|
||||
l.Logger.Infof("Goroutine ID: %d", goID)
|
||||
|
||||
_, err = l.svcCtx.AppRpc.LogoutUser(l.ctx, &app.UserToken{
|
||||
AccessToken: token,
|
||||
UserId: l.getUserIDFromContext(),
|
||||
})
|
||||
|
||||
l.Logger.Infof("Finished Logout RPC call at: %v", time.Now())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.BaseMsgResp{Msg: l.svcCtx.Trans.Trans(l.ctx, i18n.Success)}, nil
|
||||
}
|
||||
|
||||
// 获取goroutine ID
|
||||
func getGoroutineID() uint64 {
|
||||
b := make([]byte, 64)
|
||||
b = b[:runtime.Stack(b, false)]
|
||||
b = bytes.TrimPrefix(b, []byte("goroutine "))
|
||||
b = b[:bytes.IndexByte(b, ' ')]
|
||||
n, _ := strconv.ParseUint(string(b), 10, 64)
|
||||
return n
|
||||
}
|
||||
|
||||
// 从上下文中获取 Token
|
||||
func (l *LogoutLogic) getTokenFromContext() string {
|
||||
// 尝试不同的 key 从上下文中获取 Token
|
||||
keys := []string{"token", "access_token", "jwt_token", "auth_token"}
|
||||
|
||||
for _, key := range keys {
|
||||
if token, ok := l.ctx.Value(key).(string); ok && token != "" {
|
||||
return l.cleanToken(token)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 清理 Token 字符串,移除 "Bearer " 前缀
|
||||
func (l *LogoutLogic) cleanToken(token string) string {
|
||||
if token == "" {
|
||||
return ""
|
||||
}
|
||||
token = strings.TrimSpace(token)
|
||||
// 移除 "Bearer " 前缀
|
||||
if strings.HasPrefix(strings.ToLower(token), "bearer ") {
|
||||
token = token[7:]
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
// 从 Authorization Header 中提取 Token
|
||||
func (l *LogoutLogic) extractTokenFromAuthHeader(ctx context.Context) string {
|
||||
// 尝试从请求上下文获取 Request 对象
|
||||
if req, ok := ctx.Value("http_request").(*http.Request); ok {
|
||||
authHeader := req.Header.Get("Authorization")
|
||||
if authHeader != "" {
|
||||
return l.cleanToken(authHeader)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 从上下文中获取用户ID
|
||||
func (l *LogoutLogic) getUserIDFromContext() uint64 {
|
||||
// 在AuthMiddleware中,我们将用户ID放入了context
|
||||
if userID, ok := l.ctx.Value("userId").(uint64); ok {
|
||||
return userID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 获取 Token 过期时间
|
||||
func (l *LogoutLogic) getTokenExpireTime(token string) (time.Time, error) {
|
||||
// 使用 JWTManager 验证并获取 Claims
|
||||
claims, err := l.svcCtx.AppRpc.AuthToken(l.ctx, &app.AuthReq{Token: token})
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return claims.Claims.ExpiresAt.Timestamp.AsTime(), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/saas-mingyang/mingyang-admin-common/config"
|
||||
jwt2 "github.com/saas-mingyang/mingyang-admin-common/utils/jwt"
|
||||
"github.com/zeromicro/go-zero/rest/enum"
|
||||
"mingyang-admin-app-api/internal/types"
|
||||
"mingyang-admin-app-rpc/appclient"
|
||||
"mingyang-admin-app-rpc/types/app"
|
||||
|
|
@ -16,12 +19,6 @@ import (
|
|||
)
|
||||
|
||||
// 定义上下文键类型
|
||||
type contextKey string
|
||||
|
||||
const (
|
||||
UserIDKey contextKey = "user_id"
|
||||
UserInfoKey contextKey = "user_info"
|
||||
)
|
||||
|
||||
type AuthorityMiddleware struct {
|
||||
Rds redis.UniversalClient
|
||||
|
|
@ -61,36 +58,21 @@ func (m *AuthorityMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
|||
writeError(w, 401, "Authorization header is required")
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 验证 Bearer Token 格式
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
writeError(w, 401, "Invalid authorization format, must be 'Bearer <token>'")
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 提取 Token
|
||||
tokenString := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer"))
|
||||
if tokenString == "" {
|
||||
writeError(w, 401, "Token cannot be empty")
|
||||
return
|
||||
}
|
||||
|
||||
// 4. 检查 Redis 缓存(可选)
|
||||
fromToken := jwt2.StripBearerPrefixFromToken(r.Header.Get("Authorization"))
|
||||
if m.Rds != nil {
|
||||
cacheKey := fmt.Sprintf("token:%s", tokenString)
|
||||
cacheKey := config.RedisTokenPrefix + fromToken
|
||||
fmt.Printf("cacheKey: %s\n", cacheKey)
|
||||
cachedUserID, err := m.Rds.Get(r.Context(), cacheKey).Result()
|
||||
if err == nil && cachedUserID != "" {
|
||||
// 从缓存中获取到用户ID
|
||||
ctx := context.WithValue(r.Context(), UserIDKey, cachedUserID)
|
||||
ctx := context.WithValue(r.Context(), enum.UserIdRpcCtxKey, cachedUserID)
|
||||
r = r.WithContext(ctx)
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 调用 RPC 验证 Token
|
||||
fmt.Printf("Validating token: %s\n", tokenString)
|
||||
token, err := m.AppRpc.AuthToken(r.Context(), &app.AuthReq{Token: tokenString})
|
||||
token, err := m.AppRpc.AuthToken(r.Context(), &app.AuthReq{Token: fromToken})
|
||||
if err != nil {
|
||||
fmt.Printf("Error validating token: %v\n", err)
|
||||
// 根据错误类型返回不同的错误信息
|
||||
|
|
@ -120,15 +102,30 @@ func (m *AuthorityMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
|||
|
||||
// 7. 缓存到 Redis(可选)
|
||||
if m.Rds != nil {
|
||||
cacheKey := fmt.Sprintf("token:%s", tokenString)
|
||||
cacheKey := fmt.Sprintf("token:%s", fromToken)
|
||||
// 设置缓存,过期时间30分钟
|
||||
m.Rds.Set(r.Context(), cacheKey, id, 30*time.Minute)
|
||||
}
|
||||
|
||||
// 8. 设置到上下文
|
||||
// 创建新的上下文,包含 Token 和用户信息
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, UserIDKey, id)
|
||||
ctx = context.WithValue(ctx, UserInfoKey, token)
|
||||
ctx = context.WithValue(ctx, "token", fromToken)
|
||||
ctx = context.WithValue(ctx, "userId", token.UserId)
|
||||
ctx = context.WithValue(ctx, "tokenClaims", token)
|
||||
|
||||
// 获取客户端 IP
|
||||
clientIP := getClientIP(r)
|
||||
ctx = context.WithValue(ctx, "client_ip", clientIP)
|
||||
|
||||
// 获取 User-Agent
|
||||
userAgent := r.UserAgent()
|
||||
ctx = context.WithValue(ctx, "user_agent", userAgent)
|
||||
|
||||
// 将新上下文设置到请求中
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
// 调用下一个处理器
|
||||
next(w, r)
|
||||
|
||||
// 9. 记录请求日志(可选)
|
||||
fmt.Printf("[%s] %s - UserID: %d - Duration: %v\n",
|
||||
|
|
@ -136,9 +133,25 @@ func (m *AuthorityMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
|||
r.URL.Path,
|
||||
id,
|
||||
time.Since(startTime))
|
||||
|
||||
// 10. 继续处理请求
|
||||
r = r.WithContext(ctx)
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取客户端 IP
|
||||
func getClientIP(r *http.Request) string {
|
||||
// 尝试从 X-Forwarded-For 获取
|
||||
if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" {
|
||||
// 可能有多个 IP,取第一个
|
||||
ips := strings.Split(forwarded, ",")
|
||||
if len(ips) > 0 {
|
||||
return strings.TrimSpace(ips[0])
|
||||
}
|
||||
}
|
||||
|
||||
// 从 RemoteAddr 获取
|
||||
ip := r.RemoteAddr
|
||||
if colonIndex := strings.LastIndex(ip, ":"); colonIndex != -1 {
|
||||
ip = ip[:colonIndex]
|
||||
}
|
||||
|
||||
return ip
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,10 @@ message LoginResponse {
|
|||
AuthToken auth_token = 2;
|
||||
}
|
||||
|
||||
message LogoutUserRequest {
|
||||
string access_token = 1;
|
||||
}
|
||||
|
||||
// NumericDate 使用 timestamp 表示 JWT 中的时间字段
|
||||
message NumericDate {
|
||||
google.protobuf.Timestamp timestamp = 1;
|
||||
|
|
@ -191,6 +195,11 @@ message UserInfo {
|
|||
optional int64 updated_at = 24;
|
||||
}
|
||||
|
||||
message UserToken {
|
||||
string access_token = 1;
|
||||
uint64 user_id = 2;
|
||||
}
|
||||
|
||||
message VerifyCodeReq {
|
||||
VerifyCodeType type = 1;
|
||||
AccountType account_type = 2;
|
||||
|
|
@ -219,6 +228,9 @@ service App {
|
|||
// 分页查询用户信息
|
||||
// group: user
|
||||
rpc ListUsers(PageUserRequest) returns (PageUserResponse);
|
||||
// 用户退出登录
|
||||
// group: user
|
||||
rpc LogoutUser(UserToken) returns (LogoutUserRequest);
|
||||
// group: base
|
||||
rpc InitDatabase(Empty) returns (BaseResp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ type (
|
|||
IDsReq = app.IDsReq
|
||||
LoginRequest = app.LoginRequest
|
||||
LoginResponse = app.LoginResponse
|
||||
LogoutUserRequest = app.LogoutUserRequest
|
||||
NumericDate = app.NumericDate
|
||||
PageInfoReq = app.PageInfoReq
|
||||
PageUserRequest = app.PageUserRequest
|
||||
|
|
@ -36,6 +37,7 @@ type (
|
|||
UUIDReq = app.UUIDReq
|
||||
UUIDsReq = app.UUIDsReq
|
||||
UserInfo = app.UserInfo
|
||||
UserToken = app.UserToken
|
||||
VerifyCodeReq = app.VerifyCodeReq
|
||||
VerifyCodeResp = app.VerifyCodeResp
|
||||
|
||||
|
|
@ -50,6 +52,8 @@ type (
|
|||
LoginUser(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error)
|
||||
// 分页查询用户信息
|
||||
ListUsers(ctx context.Context, in *PageUserRequest, opts ...grpc.CallOption) (*PageUserResponse, error)
|
||||
// 用户退出登录
|
||||
LogoutUser(ctx context.Context, in *UserToken, opts ...grpc.CallOption) (*LogoutUserRequest, error)
|
||||
InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error)
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +98,12 @@ func (m *defaultApp) ListUsers(ctx context.Context, in *PageUserRequest, opts ..
|
|||
return client.ListUsers(ctx, in, opts...)
|
||||
}
|
||||
|
||||
// 用户退出登录
|
||||
func (m *defaultApp) LogoutUser(ctx context.Context, in *UserToken, opts ...grpc.CallOption) (*LogoutUserRequest, error) {
|
||||
client := app.NewAppClient(m.cli.Conn())
|
||||
return client.LogoutUser(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultApp) InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error) {
|
||||
client := app.NewAppClient(m.cli.Conn())
|
||||
return client.InitDatabase(ctx, in, opts...)
|
||||
|
|
|
|||
|
|
@ -84,8 +84,13 @@ message PageUserResponse {
|
|||
uint64 total = 1;
|
||||
repeated UserInfo data = 2;
|
||||
}
|
||||
|
||||
|
||||
message UserToken {
|
||||
string access_token = 1;
|
||||
uint64 user_id = 2;
|
||||
}
|
||||
message LogoutUserRequest {
|
||||
string access_token = 1;
|
||||
}
|
||||
|
||||
|
||||
// App 服务定义
|
||||
|
|
@ -99,4 +104,7 @@ service App {
|
|||
// 分页查询用户信息
|
||||
// group: user
|
||||
rpc ListUsers(PageUserRequest) returns (PageUserResponse);
|
||||
// 用户退出登录
|
||||
// group: user
|
||||
rpc LogoutUser(UserToken) returns (LogoutUserRequest);
|
||||
}
|
||||
|
|
@ -1,111 +1,100 @@
|
|||
package jwt_manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"mingyang-admin-app-rpc/internal/config"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type JWTManager struct {
|
||||
accessTokenSecret []byte
|
||||
refreshTokenSecret []byte
|
||||
accessTokenExpiry time.Duration
|
||||
refreshTokenExpiry time.Duration
|
||||
issuer string
|
||||
}
|
||||
// TokenType 令牌类型
|
||||
type TokenType string
|
||||
|
||||
const (
|
||||
AccessToken TokenType = "access"
|
||||
RefreshToken TokenType = "refresh"
|
||||
)
|
||||
|
||||
// JWTConfig JWT 配置
|
||||
|
||||
// Claims JWT Claims 结构体
|
||||
type Claims struct {
|
||||
UserID uint64 `json:"user_id"`
|
||||
Type string `json:"type"` // "access" or "refresh"
|
||||
UserID uint64 `json:"user_id"`
|
||||
Type TokenType `json:"type"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func NewJWTManager(config *config.JWTConfig) *JWTManager {
|
||||
// TokenPair 令牌对
|
||||
type TokenPair struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
AccessTokenExpiresAt time.Time `json:"access_token_expires_at"`
|
||||
RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
// JWTManager JWT 管理器
|
||||
type JWTManager struct {
|
||||
accessSecret []byte
|
||||
refreshSecret []byte
|
||||
accessExpiry time.Duration
|
||||
refreshExpiry time.Duration
|
||||
issuer string
|
||||
redis redis.UniversalClient
|
||||
prefix string // Redis 键前缀
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewJWTManager 创建新的 JWT 管理器
|
||||
func NewJWTManager(config *config.JWTConfig, redisClient redis.UniversalClient) *JWTManager {
|
||||
if config.AccessTokenSecret == "" || config.RefreshTokenSecret == "" {
|
||||
panic("JWT 配置错误")
|
||||
}
|
||||
return &JWTManager{
|
||||
accessTokenSecret: []byte(config.AccessTokenSecret),
|
||||
refreshTokenSecret: []byte(config.RefreshTokenSecret),
|
||||
accessTokenExpiry: config.AccessTokenExpiry,
|
||||
refreshTokenExpiry: config.RefreshTokenExpiry,
|
||||
issuer: config.Issuer,
|
||||
accessSecret: []byte(config.AccessTokenSecret),
|
||||
refreshSecret: []byte(config.RefreshTokenSecret),
|
||||
accessExpiry: config.AccessTokenExpiry,
|
||||
refreshExpiry: config.RefreshTokenExpiry,
|
||||
issuer: config.Issuer,
|
||||
redis: redisClient,
|
||||
prefix: "jwt:",
|
||||
ctx: context.Background(),
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateAccessToken 生成访问令牌
|
||||
func (m *JWTManager) GenerateAccessToken(userID uint64) (string, *Claims, error) {
|
||||
return m.generateToken(userID, "access", m.accessTokenExpiry, m.accessTokenSecret)
|
||||
// WithContext 设置上下文
|
||||
func (m *JWTManager) WithContext(ctx context.Context) *JWTManager {
|
||||
m.ctx = ctx
|
||||
return m
|
||||
}
|
||||
|
||||
// GenerateRefreshToken 生成刷新令牌
|
||||
func (m *JWTManager) GenerateRefreshToken(userID uint64) (string, *Claims, error) {
|
||||
return m.generateToken(userID, "refresh", m.refreshTokenExpiry, m.refreshTokenSecret)
|
||||
}
|
||||
// ==================== Token 生成方法 ====================
|
||||
|
||||
func (m *JWTManager) generateToken(userID uint64, tokenType string, expiry time.Duration, secret []byte) (string, *Claims, error) {
|
||||
fmt.Printf("userID: %d, tokenType: %s, expiry: %s, secret: %s\n", userID, tokenType, expiry, secret)
|
||||
claims := &Claims{
|
||||
UserID: userID,
|
||||
Type: tokenType,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiry)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: m.issuer,
|
||||
Subject: fmt.Sprint(userID),
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString(secret)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return tokenString, claims, nil
|
||||
}
|
||||
|
||||
// VerifyAccessToken 验证访问令牌
|
||||
func (m *JWTManager) VerifyAccessToken(tokenString string) (*Claims, error) {
|
||||
return m.verifyToken(tokenString, m.accessTokenSecret, "access")
|
||||
}
|
||||
|
||||
// VerifyRefreshToken 验证刷新令牌
|
||||
func (m *JWTManager) VerifyRefreshToken(tokenString string) (*Claims, error) {
|
||||
return m.verifyToken(tokenString, m.refreshTokenSecret, "refresh")
|
||||
}
|
||||
|
||||
func (m *JWTManager) verifyToken(tokenString string, secret []byte, expectedType string) (*Claims, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return secret, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
|
||||
if claims.Type != expectedType {
|
||||
return nil, errors.New("invalid token type")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
|
||||
// GenerateTokenPair 生成令牌对
|
||||
// GenerateTokenPair 生成访问令牌和刷新令牌对
|
||||
func (m *JWTManager) GenerateTokenPair(userID uint64) (*TokenPair, error) {
|
||||
accessToken, accessClaims, err := m.GenerateAccessToken(userID)
|
||||
// 生成访问令牌
|
||||
accessToken, accessClaims, err := m.generateToken(userID, AccessToken, m.accessSecret, m.accessExpiry)
|
||||
fmt.Printf("accessToken: %v\n", accessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("生成访问令牌失败: %w", err)
|
||||
}
|
||||
|
||||
refreshToken, refreshClaims, err := m.GenerateRefreshToken(userID)
|
||||
// 生成刷新令牌
|
||||
refreshToken, refreshClaims, err := m.generateToken(userID, RefreshToken, m.refreshSecret, m.refreshExpiry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("生成刷新令牌失败: %w", err)
|
||||
}
|
||||
|
||||
// 存储令牌到 Redis(用于追踪和管理)
|
||||
if m.redis != nil {
|
||||
m.storeToken(userID, accessToken, accessClaims.ExpiresAt.Time)
|
||||
m.storeToken(userID, refreshToken, refreshClaims.ExpiresAt.Time)
|
||||
}
|
||||
|
||||
return &TokenPair{
|
||||
|
|
@ -117,10 +106,484 @@ func (m *JWTManager) GenerateTokenPair(userID uint64) (*TokenPair, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
type TokenPair struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
AccessTokenExpiresAt time.Time `json:"access_token_expires_at"`
|
||||
RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"`
|
||||
TokenType string `json:"token_type"`
|
||||
// GenerateAccessToken 生成访问令牌
|
||||
func (m *JWTManager) GenerateAccessToken(userID uint64) (string, *Claims, error) {
|
||||
token, claims, err := m.generateToken(userID, AccessToken, m.accessSecret, m.accessExpiry)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// 存储到 Redis
|
||||
if m.redis != nil {
|
||||
m.storeToken(userID, token, claims.ExpiresAt.Time)
|
||||
}
|
||||
|
||||
return token, claims, nil
|
||||
}
|
||||
|
||||
// GenerateRefreshToken 生成刷新令牌
|
||||
func (m *JWTManager) GenerateRefreshToken(userID uint64) (string, *Claims, error) {
|
||||
token, claims, err := m.generateToken(userID, RefreshToken, m.refreshSecret, m.refreshExpiry)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// 存储到 Redis
|
||||
if m.redis != nil {
|
||||
m.storeToken(userID, token, claims.ExpiresAt.Time)
|
||||
}
|
||||
|
||||
return token, claims, nil
|
||||
}
|
||||
|
||||
// generateToken 内部方法:生成令牌
|
||||
func (m *JWTManager) generateToken(userID uint64, tokenType TokenType, secret []byte, expiry time.Duration) (string, *Claims, error) {
|
||||
now := time.Now()
|
||||
expireAt := now.Add(expiry)
|
||||
|
||||
claims := &Claims{
|
||||
UserID: userID,
|
||||
Type: tokenType,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(expireAt),
|
||||
IssuedAt: jwt.NewNumericDate(now),
|
||||
Issuer: m.issuer,
|
||||
Subject: fmt.Sprintf("%d", userID),
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString(secret)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("签名令牌失败: %w", err)
|
||||
}
|
||||
|
||||
return tokenString, claims, nil
|
||||
}
|
||||
|
||||
// ==================== Token 验证方法 ====================
|
||||
|
||||
// VerifyAccessToken 验证访问令牌(包含黑名单检查)
|
||||
func (m *JWTManager) VerifyAccessToken(tokenString string) (*Claims, error) {
|
||||
return m.verifyToken(tokenString, AccessToken, m.accessSecret)
|
||||
}
|
||||
|
||||
// VerifyRefreshToken 验证刷新令牌(包含黑名单检查)
|
||||
func (m *JWTManager) VerifyRefreshToken(tokenString string) (*Claims, error) {
|
||||
return m.verifyToken(tokenString, RefreshToken, m.refreshSecret)
|
||||
}
|
||||
|
||||
// verifyToken 内部方法:验证令牌
|
||||
func (m *JWTManager) verifyToken(tokenString string, expectedType TokenType, secret []byte) (*Claims, error) {
|
||||
// 1. 检查令牌是否在黑名单中
|
||||
if m.redis != nil {
|
||||
blacklisted, err := m.isTokenBlacklisted(tokenString, expectedType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查黑名单失败: %w", err)
|
||||
}
|
||||
if blacklisted {
|
||||
return nil, errors.New("令牌已失效,请重新登录")
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 验证 JWT
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("不支持的签名方法: %v", token.Header["alg"])
|
||||
}
|
||||
return secret, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析令牌失败: %w", err)
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
|
||||
// 3. 检查令牌类型
|
||||
if claims.Type != expectedType {
|
||||
return nil, errors.New("令牌类型不匹配")
|
||||
}
|
||||
|
||||
// 4. 检查令牌是否过期
|
||||
if claims.ExpiresAt.Time.Before(time.Now()) {
|
||||
return nil, errors.New("令牌已过期")
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("无效的令牌")
|
||||
}
|
||||
|
||||
// ==================== 黑名单管理 ====================
|
||||
|
||||
// BlacklistToken 将令牌加入黑名单
|
||||
func (m *JWTManager) BlacklistToken(tokenString string) error {
|
||||
if m.redis == nil {
|
||||
return errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
// 尝试解析令牌
|
||||
claims, err := m.parseTokenWithoutBlacklistCheck(tokenString)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法识别令牌: %w", err)
|
||||
}
|
||||
|
||||
return m.addToBlacklist(tokenString, claims.Type, claims.ExpiresAt.Time)
|
||||
}
|
||||
|
||||
// BlacklistAccessToken 将访问令牌加入黑名单
|
||||
func (m *JWTManager) BlacklistAccessToken(tokenString string) error {
|
||||
if m.redis == nil {
|
||||
return errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
claims, err := m.VerifyAccessToken(tokenString)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无效的访问令牌: %w", err)
|
||||
}
|
||||
|
||||
return m.addToBlacklist(tokenString, AccessToken, claims.ExpiresAt.Time)
|
||||
}
|
||||
|
||||
// BlacklistRefreshToken 将刷新令牌加入黑名单
|
||||
func (m *JWTManager) BlacklistRefreshToken(tokenString string) error {
|
||||
if m.redis == nil {
|
||||
return errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
claims, err := m.VerifyRefreshToken(tokenString)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无效的刷新令牌: %w", err)
|
||||
}
|
||||
|
||||
return m.addToBlacklist(tokenString, RefreshToken, claims.ExpiresAt.Time)
|
||||
}
|
||||
|
||||
// addToBlacklist 内部方法:将令牌加入黑名单
|
||||
func (m *JWTManager) addToBlacklist(tokenString string, tokenType TokenType, expireTime time.Time) error {
|
||||
remaining := time.Until(expireTime)
|
||||
if remaining <= 0 {
|
||||
// 令牌已过期,不需要加入黑名单
|
||||
return nil
|
||||
}
|
||||
|
||||
tokenHash := m.hashToken(tokenString)
|
||||
blacklistKey := m.getBlacklistKey(tokenHash, tokenType)
|
||||
|
||||
// 设置黑名单,过期时间等于令牌剩余有效期
|
||||
err := m.redis.Set(m.ctx, blacklistKey, "1", remaining).Err()
|
||||
if err != nil {
|
||||
return fmt.Errorf("设置黑名单失败: %w", err)
|
||||
}
|
||||
|
||||
// 记录用户与令牌的关联(用于按用户清理)
|
||||
if claims, err := m.parseTokenWithoutBlacklistCheck(tokenString); err == nil {
|
||||
userTokenKey := m.getUserTokenKey(claims.UserID, tokenHash)
|
||||
m.redis.Set(m.ctx, userTokenKey, tokenString, remaining)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsTokenBlacklisted 检查令牌是否在黑名单中
|
||||
func (m *JWTManager) IsTokenBlacklisted(tokenString string) (bool, error) {
|
||||
if m.redis == nil {
|
||||
return false, errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
// 检查所有类型的黑名单
|
||||
tokenHash := m.hashToken(tokenString)
|
||||
|
||||
// 检查访问令牌黑名单
|
||||
accessBlacklistKey := m.getBlacklistKey(tokenHash, AccessToken)
|
||||
exists, err := m.redis.Exists(m.ctx, accessBlacklistKey).Result()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("检查访问令牌黑名单失败: %w", err)
|
||||
}
|
||||
if exists > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 检查刷新令牌黑名单
|
||||
refreshBlacklistKey := m.getBlacklistKey(tokenHash, RefreshToken)
|
||||
exists, err = m.redis.Exists(m.ctx, refreshBlacklistKey).Result()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("检查刷新令牌黑名单失败: %w", err)
|
||||
}
|
||||
|
||||
return exists > 0, nil
|
||||
}
|
||||
|
||||
// isTokenBlacklisted 内部方法:检查令牌是否在黑名单中
|
||||
func (m *JWTManager) isTokenBlacklisted(tokenString string, tokenType TokenType) (bool, error) {
|
||||
if m.redis == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
tokenHash := m.hashToken(tokenString)
|
||||
blacklistKey := m.getBlacklistKey(tokenHash, tokenType)
|
||||
|
||||
exists, err := m.redis.Exists(m.ctx, blacklistKey).Result()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return exists > 0, nil
|
||||
}
|
||||
|
||||
// RemoveFromBlacklist 从黑名单中移除令牌
|
||||
func (m *JWTManager) RemoveFromBlacklist(tokenString string) error {
|
||||
if m.redis == nil {
|
||||
return errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
tokenHash := m.hashToken(tokenString)
|
||||
|
||||
// 尝试移除两种类型的黑名单
|
||||
accessBlacklistKey := m.getBlacklistKey(tokenHash, AccessToken)
|
||||
refreshBlacklistKey := m.getBlacklistKey(tokenHash, RefreshToken)
|
||||
|
||||
err := m.redis.Del(m.ctx, accessBlacklistKey, refreshBlacklistKey).Err()
|
||||
return err
|
||||
}
|
||||
|
||||
// RevokeUserTokens 撤销用户所有令牌
|
||||
func (m *JWTManager) RevokeUserTokens(userID uint64) error {
|
||||
if m.redis == nil {
|
||||
return errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
// 获取用户的所有令牌
|
||||
tokens, err := m.GetUserTokens(userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取用户令牌失败: %w", err)
|
||||
}
|
||||
|
||||
// 将所有令牌加入黑名单
|
||||
for _, token := range tokens {
|
||||
m.BlacklistToken(token)
|
||||
}
|
||||
|
||||
// 清理用户令牌集合
|
||||
userTokenSetKey := m.getUserTokenSetKey(userID)
|
||||
err = m.redis.Del(m.ctx, userTokenSetKey).Err()
|
||||
if err != nil {
|
||||
return fmt.Errorf("清理用户令牌集合失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ==================== 用户令牌管理 ====================
|
||||
|
||||
// storeToken 存储用户令牌
|
||||
func (m *JWTManager) storeToken(userID uint64, tokenString string, expireTime time.Time) {
|
||||
if m.redis == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 将令牌添加到用户的令牌集合
|
||||
userTokenSetKey := m.getUserTokenSetKey(userID)
|
||||
err := m.redis.SAdd(m.ctx, userTokenSetKey, tokenString).Err()
|
||||
if err != nil {
|
||||
// 记录错误但继续执行
|
||||
fmt.Printf("存储用户令牌失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 设置集合的过期时间(比令牌晚 1 小时)
|
||||
expireAt := expireTime.Add(time.Hour)
|
||||
err = m.redis.ExpireAt(m.ctx, userTokenSetKey, expireAt).Err()
|
||||
if err != nil {
|
||||
fmt.Printf("设置集合过期时间失败: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserTokens 获取用户的所有令牌
|
||||
func (m *JWTManager) GetUserTokens(userID uint64) ([]string, error) {
|
||||
if m.redis == nil {
|
||||
return nil, errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
userTokenSetKey := m.getUserTokenSetKey(userID)
|
||||
return m.redis.SMembers(m.ctx, userTokenSetKey).Result()
|
||||
}
|
||||
|
||||
// RemoveUserToken 移除用户的特定令牌
|
||||
func (m *JWTManager) RemoveUserToken(userID uint64, tokenString string) error {
|
||||
if m.redis == nil {
|
||||
return errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
userTokenSetKey := m.getUserTokenSetKey(userID)
|
||||
err := m.redis.SRem(m.ctx, userTokenSetKey, tokenString).Err()
|
||||
return err
|
||||
}
|
||||
|
||||
// ==================== 令牌刷新 ====================
|
||||
|
||||
// RefreshTokens 使用刷新令牌获取新的令牌对
|
||||
func (m *JWTManager) RefreshTokens(refreshToken string) (*TokenPair, error) {
|
||||
// 验证刷新令牌
|
||||
claims, err := m.VerifyRefreshToken(refreshToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("刷新令牌验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 将旧的刷新令牌加入黑名单
|
||||
if err := m.BlacklistRefreshToken(refreshToken); err != nil {
|
||||
// 记录日志但继续执行
|
||||
fmt.Printf("警告:将旧刷新令牌加入黑名单失败: %v\n", err)
|
||||
}
|
||||
|
||||
// 生成新的令牌对
|
||||
return m.GenerateTokenPair(claims.UserID)
|
||||
}
|
||||
|
||||
// ==================== 辅助方法 ====================
|
||||
|
||||
// parseTokenWithoutBlacklistCheck 解析令牌但不检查黑名单
|
||||
func (m *JWTManager) parseTokenWithoutBlacklistCheck(tokenString string) (*Claims, error) {
|
||||
// 尝试作为访问令牌解析
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return m.accessSecret, nil
|
||||
})
|
||||
|
||||
if err == nil && token.Valid {
|
||||
if claims, ok := token.Claims.(*Claims); ok {
|
||||
return claims, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试作为刷新令牌解析
|
||||
token, err = jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return m.refreshSecret, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("无法解析令牌")
|
||||
}
|
||||
|
||||
// hashToken 生成令牌的哈希值
|
||||
func (m *JWTManager) hashToken(token string) string {
|
||||
hash := md5.Sum([]byte(token))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// getBlacklistKey 获取黑名单键名
|
||||
func (m *JWTManager) getBlacklistKey(tokenHash string, tokenType TokenType) string {
|
||||
return fmt.Sprintf("%sblacklist:%s:%s", m.prefix, tokenType, tokenHash)
|
||||
}
|
||||
|
||||
// getUserTokenSetKey 获取用户令牌集合键名
|
||||
func (m *JWTManager) getUserTokenSetKey(userID uint64) string {
|
||||
return fmt.Sprintf("%suser:tokens:%d", m.prefix, userID)
|
||||
}
|
||||
|
||||
// getUserTokenKey 获取用户令牌键名
|
||||
func (m *JWTManager) getUserTokenKey(userID uint64, tokenHash string) string {
|
||||
return fmt.Sprintf("%suser:%d:token:%s", m.prefix, userID, tokenHash)
|
||||
}
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
// GetTokenInfo 获取令牌信息
|
||||
func (m *JWTManager) GetTokenInfo(tokenString string) (map[string]interface{}, error) {
|
||||
claims, err := m.parseTokenWithoutBlacklistCheck(tokenString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := map[string]interface{}{
|
||||
"user_id": claims.UserID,
|
||||
"token_type": claims.Type,
|
||||
"issued_at": claims.IssuedAt.Time.Format(time.RFC3339),
|
||||
"expires_at": claims.ExpiresAt.Time.Format(time.RFC3339),
|
||||
"issuer": claims.Issuer,
|
||||
"subject": claims.Subject,
|
||||
"remaining": time.Until(claims.ExpiresAt.Time).String(),
|
||||
}
|
||||
|
||||
// 检查是否在黑名单中
|
||||
if m.redis != nil {
|
||||
blacklisted, _ := m.IsTokenBlacklisted(tokenString)
|
||||
info["blacklisted"] = blacklisted
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// ValidateAndExtract 验证令牌并提取用户ID
|
||||
func (m *JWTManager) ValidateAndExtract(tokenString string) (uint64, error) {
|
||||
claims, err := m.VerifyAccessToken(tokenString)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return claims.UserID, nil
|
||||
}
|
||||
|
||||
// Cleanup 清理过期的令牌数据
|
||||
func (m *JWTManager) Cleanup() error {
|
||||
if m.redis == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Redis 会自动清理过期的键
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBlacklistStats 获取黑名单统计信息
|
||||
func (m *JWTManager) GetBlacklistStats() (map[string]interface{}, error) {
|
||||
if m.redis == nil {
|
||||
return nil, errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
stats := make(map[string]interface{})
|
||||
|
||||
// 获取访问令牌黑名单数量
|
||||
accessPattern := fmt.Sprintf("%sblacklist:access:*", m.prefix)
|
||||
accessKeys, err := m.redis.Keys(m.ctx, accessPattern).Result()
|
||||
if err == nil {
|
||||
stats["access_token_blacklist_count"] = len(accessKeys)
|
||||
} else {
|
||||
stats["access_token_blacklist_count"] = 0
|
||||
}
|
||||
|
||||
// 获取刷新令牌黑名单数量
|
||||
refreshPattern := fmt.Sprintf("%sblacklist:refresh:*", m.prefix)
|
||||
refreshKeys, err := m.redis.Keys(m.ctx, refreshPattern).Result()
|
||||
if err == nil {
|
||||
stats["refresh_token_blacklist_count"] = len(refreshKeys)
|
||||
} else {
|
||||
stats["refresh_token_blacklist_count"] = 0
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// Ping 检查 Redis 连接
|
||||
func (m *JWTManager) Ping() error {
|
||||
if m.redis == nil {
|
||||
return errors.New("redis 客户端未初始化")
|
||||
}
|
||||
|
||||
_, err := m.redis.Ping(m.ctx).Result()
|
||||
return err
|
||||
}
|
||||
|
||||
// Close 关闭 Redis 连接
|
||||
func (m *JWTManager) Close() error {
|
||||
if m.redis == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.redis.Close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func NewAuthTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *TokenLo
|
|||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf),
|
||||
jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf, svcCtx.Redis),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ func (l *TokenLogic) AuthToken(in *app.AuthReq) (*app.AuthInfoResp, error) {
|
|||
}
|
||||
return &app.AuthInfoResp{
|
||||
UserId: token.UserID,
|
||||
Type: token.Type,
|
||||
Type: string(token.Type),
|
||||
Claims: &app.RegisteredClaims{
|
||||
IssuedAt: &app.NumericDate{
|
||||
Timestamp: ×tamppb.Timestamp{
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func NewLoginUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginUs
|
|||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
jwt: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf),
|
||||
jwt: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf, nil),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mingyang-admin-app-rpc/internal/jwt_manager"
|
||||
"mingyang-admin-app-rpc/internal/util/dberrorhandler"
|
||||
"time"
|
||||
|
||||
"mingyang-admin-app-rpc/internal/svc"
|
||||
"mingyang-admin-app-rpc/types/app"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type LogoutUserLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
jwtManager *jwt_manager.JWTManager
|
||||
}
|
||||
|
||||
func NewLogoutUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutUserLogic {
|
||||
return &LogoutUserLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf, svcCtx.Redis),
|
||||
}
|
||||
}
|
||||
|
||||
// LogoutUser 用户退出登录
|
||||
func (l *LogoutUserLogic) LogoutUser(in *app.UserToken) (*app.LogoutUserRequest, error) {
|
||||
// 记录开始时间
|
||||
start := time.Now()
|
||||
l.Logger.Info("LogoutUser started token =", in.AccessToken)
|
||||
accessToken := in.GetAccessToken()
|
||||
_, err := l.jwtManager.VerifyAccessToken(accessToken)
|
||||
if err != nil {
|
||||
l.Logger.Error("Verify access token failed", "error", err, "time", time.Since(start))
|
||||
return nil, dberrorhandler.DefaultEntError(l.Logger, err, in)
|
||||
}
|
||||
err = l.jwtManager.BlacklistToken(accessToken)
|
||||
if err != nil {
|
||||
l.Logger.Error("Blacklist token failed", "error", err, "time", time.Since(start))
|
||||
return nil, dberrorhandler.DefaultEntError(l.Logger, err, in)
|
||||
}
|
||||
return &app.LogoutUserRequest{
|
||||
AccessToken: accessToken,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ func NewRegisterUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Regi
|
|||
return &RegisterUserLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf),
|
||||
jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf, svcCtx.Redis),
|
||||
Logger: logx.WithContext(ctx),
|
||||
cacheRepo: cacherepo.NewCacheRepository(ctx, svcCtx),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ func (s *AppServer) ListUsers(ctx context.Context, in *app.PageUserRequest) (*ap
|
|||
return l.ListUsers(in)
|
||||
}
|
||||
|
||||
// 用户退出登录
|
||||
func (s *AppServer) LogoutUser(ctx context.Context, in *app.UserToken) (*app.LogoutUserRequest, error) {
|
||||
l := user.NewLogoutUserLogic(ctx, s.svcCtx)
|
||||
return l.LogoutUser(in)
|
||||
}
|
||||
|
||||
func (s *AppServer) InitDatabase(ctx context.Context, in *app.Empty) (*app.BaseResp, error) {
|
||||
l := base.NewInitDatabaseLogic(ctx, s.svcCtx)
|
||||
return l.InitDatabase(in)
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
package service
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.10
|
||||
// protoc v3.21.11
|
||||
// protoc v3.19.4
|
||||
// source: app.proto
|
||||
|
||||
package app
|
||||
|
|
@ -821,6 +821,50 @@ func (x *LoginResponse) GetAuthToken() *AuthToken {
|
|||
return nil
|
||||
}
|
||||
|
||||
type LogoutUserRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *LogoutUserRequest) Reset() {
|
||||
*x = LogoutUserRequest{}
|
||||
mi := &file_app_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *LogoutUserRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LogoutUserRequest) ProtoMessage() {}
|
||||
|
||||
func (x *LogoutUserRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LogoutUserRequest.ProtoReflect.Descriptor instead.
|
||||
func (*LogoutUserRequest) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *LogoutUserRequest) GetAccessToken() string {
|
||||
if x != nil {
|
||||
return x.AccessToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// NumericDate 使用 timestamp 表示 JWT 中的时间字段
|
||||
type NumericDate struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
|
|
@ -831,7 +875,7 @@ type NumericDate struct {
|
|||
|
||||
func (x *NumericDate) Reset() {
|
||||
*x = NumericDate{}
|
||||
mi := &file_app_proto_msgTypes[13]
|
||||
mi := &file_app_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -843,7 +887,7 @@ func (x *NumericDate) String() string {
|
|||
func (*NumericDate) ProtoMessage() {}
|
||||
|
||||
func (x *NumericDate) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[13]
|
||||
mi := &file_app_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -856,7 +900,7 @@ func (x *NumericDate) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use NumericDate.ProtoReflect.Descriptor instead.
|
||||
func (*NumericDate) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{13}
|
||||
return file_app_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *NumericDate) GetTimestamp() *timestamppb.Timestamp {
|
||||
|
|
@ -876,7 +920,7 @@ type PageInfoReq struct {
|
|||
|
||||
func (x *PageInfoReq) Reset() {
|
||||
*x = PageInfoReq{}
|
||||
mi := &file_app_proto_msgTypes[14]
|
||||
mi := &file_app_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -888,7 +932,7 @@ func (x *PageInfoReq) String() string {
|
|||
func (*PageInfoReq) ProtoMessage() {}
|
||||
|
||||
func (x *PageInfoReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[14]
|
||||
mi := &file_app_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -901,7 +945,7 @@ func (x *PageInfoReq) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use PageInfoReq.ProtoReflect.Descriptor instead.
|
||||
func (*PageInfoReq) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{14}
|
||||
return file_app_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *PageInfoReq) GetPage() uint64 {
|
||||
|
|
@ -933,7 +977,7 @@ type PageUserRequest struct {
|
|||
|
||||
func (x *PageUserRequest) Reset() {
|
||||
*x = PageUserRequest{}
|
||||
mi := &file_app_proto_msgTypes[15]
|
||||
mi := &file_app_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -945,7 +989,7 @@ func (x *PageUserRequest) String() string {
|
|||
func (*PageUserRequest) ProtoMessage() {}
|
||||
|
||||
func (x *PageUserRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[15]
|
||||
mi := &file_app_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -958,7 +1002,7 @@ func (x *PageUserRequest) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use PageUserRequest.ProtoReflect.Descriptor instead.
|
||||
func (*PageUserRequest) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{15}
|
||||
return file_app_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *PageUserRequest) GetPage() uint64 {
|
||||
|
|
@ -1020,7 +1064,7 @@ type PageUserResponse struct {
|
|||
|
||||
func (x *PageUserResponse) Reset() {
|
||||
*x = PageUserResponse{}
|
||||
mi := &file_app_proto_msgTypes[16]
|
||||
mi := &file_app_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1032,7 +1076,7 @@ func (x *PageUserResponse) String() string {
|
|||
func (*PageUserResponse) ProtoMessage() {}
|
||||
|
||||
func (x *PageUserResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[16]
|
||||
mi := &file_app_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1045,7 +1089,7 @@ func (x *PageUserResponse) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use PageUserResponse.ProtoReflect.Descriptor instead.
|
||||
func (*PageUserResponse) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{16}
|
||||
return file_app_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *PageUserResponse) GetTotal() uint64 {
|
||||
|
|
@ -1080,7 +1124,7 @@ type RegisterUserRequest struct {
|
|||
|
||||
func (x *RegisterUserRequest) Reset() {
|
||||
*x = RegisterUserRequest{}
|
||||
mi := &file_app_proto_msgTypes[17]
|
||||
mi := &file_app_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1092,7 +1136,7 @@ func (x *RegisterUserRequest) String() string {
|
|||
func (*RegisterUserRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RegisterUserRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[17]
|
||||
mi := &file_app_proto_msgTypes[18]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1105,7 +1149,7 @@ func (x *RegisterUserRequest) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use RegisterUserRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RegisterUserRequest) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{17}
|
||||
return file_app_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *RegisterUserRequest) GetEmail() string {
|
||||
|
|
@ -1190,7 +1234,7 @@ type RegisterUserResponse struct {
|
|||
|
||||
func (x *RegisterUserResponse) Reset() {
|
||||
*x = RegisterUserResponse{}
|
||||
mi := &file_app_proto_msgTypes[18]
|
||||
mi := &file_app_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1202,7 +1246,7 @@ func (x *RegisterUserResponse) String() string {
|
|||
func (*RegisterUserResponse) ProtoMessage() {}
|
||||
|
||||
func (x *RegisterUserResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[18]
|
||||
mi := &file_app_proto_msgTypes[19]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1215,7 +1259,7 @@ func (x *RegisterUserResponse) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use RegisterUserResponse.ProtoReflect.Descriptor instead.
|
||||
func (*RegisterUserResponse) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{18}
|
||||
return file_app_proto_rawDescGZIP(), []int{19}
|
||||
}
|
||||
|
||||
func (x *RegisterUserResponse) GetUser() *UserInfo {
|
||||
|
|
@ -1269,7 +1313,7 @@ type RegisteredClaims struct {
|
|||
|
||||
func (x *RegisteredClaims) Reset() {
|
||||
*x = RegisteredClaims{}
|
||||
mi := &file_app_proto_msgTypes[19]
|
||||
mi := &file_app_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1281,7 +1325,7 @@ func (x *RegisteredClaims) String() string {
|
|||
func (*RegisteredClaims) ProtoMessage() {}
|
||||
|
||||
func (x *RegisteredClaims) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[19]
|
||||
mi := &file_app_proto_msgTypes[20]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1294,7 +1338,7 @@ func (x *RegisteredClaims) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use RegisteredClaims.ProtoReflect.Descriptor instead.
|
||||
func (*RegisteredClaims) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{19}
|
||||
return file_app_proto_rawDescGZIP(), []int{20}
|
||||
}
|
||||
|
||||
func (x *RegisteredClaims) GetIssuer() string {
|
||||
|
|
@ -1355,7 +1399,7 @@ type UUIDReq struct {
|
|||
|
||||
func (x *UUIDReq) Reset() {
|
||||
*x = UUIDReq{}
|
||||
mi := &file_app_proto_msgTypes[20]
|
||||
mi := &file_app_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1367,7 +1411,7 @@ func (x *UUIDReq) String() string {
|
|||
func (*UUIDReq) ProtoMessage() {}
|
||||
|
||||
func (x *UUIDReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[20]
|
||||
mi := &file_app_proto_msgTypes[21]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1380,7 +1424,7 @@ func (x *UUIDReq) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use UUIDReq.ProtoReflect.Descriptor instead.
|
||||
func (*UUIDReq) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{20}
|
||||
return file_app_proto_rawDescGZIP(), []int{21}
|
||||
}
|
||||
|
||||
func (x *UUIDReq) GetId() string {
|
||||
|
|
@ -1399,7 +1443,7 @@ type UUIDsReq struct {
|
|||
|
||||
func (x *UUIDsReq) Reset() {
|
||||
*x = UUIDsReq{}
|
||||
mi := &file_app_proto_msgTypes[21]
|
||||
mi := &file_app_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1411,7 +1455,7 @@ func (x *UUIDsReq) String() string {
|
|||
func (*UUIDsReq) ProtoMessage() {}
|
||||
|
||||
func (x *UUIDsReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[21]
|
||||
mi := &file_app_proto_msgTypes[22]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1424,7 +1468,7 @@ func (x *UUIDsReq) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use UUIDsReq.ProtoReflect.Descriptor instead.
|
||||
func (*UUIDsReq) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{21}
|
||||
return file_app_proto_rawDescGZIP(), []int{22}
|
||||
}
|
||||
|
||||
func (x *UUIDsReq) GetIds() []string {
|
||||
|
|
@ -1464,7 +1508,7 @@ type UserInfo struct {
|
|||
|
||||
func (x *UserInfo) Reset() {
|
||||
*x = UserInfo{}
|
||||
mi := &file_app_proto_msgTypes[22]
|
||||
mi := &file_app_proto_msgTypes[23]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1476,7 +1520,7 @@ func (x *UserInfo) String() string {
|
|||
func (*UserInfo) ProtoMessage() {}
|
||||
|
||||
func (x *UserInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[22]
|
||||
mi := &file_app_proto_msgTypes[23]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1489,7 +1533,7 @@ func (x *UserInfo) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use UserInfo.ProtoReflect.Descriptor instead.
|
||||
func (*UserInfo) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{22}
|
||||
return file_app_proto_rawDescGZIP(), []int{23}
|
||||
}
|
||||
|
||||
func (x *UserInfo) GetId() uint64 {
|
||||
|
|
@ -1646,6 +1690,58 @@ func (x *UserInfo) GetUpdatedAt() int64 {
|
|||
return 0
|
||||
}
|
||||
|
||||
type UserToken struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token"`
|
||||
UserId uint64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UserToken) Reset() {
|
||||
*x = UserToken{}
|
||||
mi := &file_app_proto_msgTypes[24]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UserToken) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UserToken) ProtoMessage() {}
|
||||
|
||||
func (x *UserToken) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[24]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UserToken.ProtoReflect.Descriptor instead.
|
||||
func (*UserToken) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{24}
|
||||
}
|
||||
|
||||
func (x *UserToken) GetAccessToken() string {
|
||||
if x != nil {
|
||||
return x.AccessToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UserToken) GetUserId() uint64 {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type VerifyCodeReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Type VerifyCodeType `protobuf:"varint,1,opt,name=type,proto3,enum=app.VerifyCodeType" json:"type"`
|
||||
|
|
@ -1657,7 +1753,7 @@ type VerifyCodeReq struct {
|
|||
|
||||
func (x *VerifyCodeReq) Reset() {
|
||||
*x = VerifyCodeReq{}
|
||||
mi := &file_app_proto_msgTypes[23]
|
||||
mi := &file_app_proto_msgTypes[25]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1669,7 +1765,7 @@ func (x *VerifyCodeReq) String() string {
|
|||
func (*VerifyCodeReq) ProtoMessage() {}
|
||||
|
||||
func (x *VerifyCodeReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[23]
|
||||
mi := &file_app_proto_msgTypes[25]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1682,7 +1778,7 @@ func (x *VerifyCodeReq) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use VerifyCodeReq.ProtoReflect.Descriptor instead.
|
||||
func (*VerifyCodeReq) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{23}
|
||||
return file_app_proto_rawDescGZIP(), []int{25}
|
||||
}
|
||||
|
||||
func (x *VerifyCodeReq) GetType() VerifyCodeType {
|
||||
|
|
@ -1716,7 +1812,7 @@ type VerifyCodeResp struct {
|
|||
|
||||
func (x *VerifyCodeResp) Reset() {
|
||||
*x = VerifyCodeResp{}
|
||||
mi := &file_app_proto_msgTypes[24]
|
||||
mi := &file_app_proto_msgTypes[26]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -1728,7 +1824,7 @@ func (x *VerifyCodeResp) String() string {
|
|||
func (*VerifyCodeResp) ProtoMessage() {}
|
||||
|
||||
func (x *VerifyCodeResp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proto_msgTypes[24]
|
||||
mi := &file_app_proto_msgTypes[26]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -1741,7 +1837,7 @@ func (x *VerifyCodeResp) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use VerifyCodeResp.ProtoReflect.Descriptor instead.
|
||||
func (*VerifyCodeResp) Descriptor() ([]byte, []int) {
|
||||
return file_app_proto_rawDescGZIP(), []int{24}
|
||||
return file_app_proto_rawDescGZIP(), []int{26}
|
||||
}
|
||||
|
||||
func (x *VerifyCodeResp) GetCaptchaCode() string {
|
||||
|
|
@ -1809,7 +1905,9 @@ const file_app_proto_rawDesc = "" +
|
|||
"\rLoginResponse\x12!\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\r.app.UserInfoR\x04user\x12-\n" +
|
||||
"\n" +
|
||||
"auth_token\x18\x02 \x01(\v2\x0e.app.AuthTokenR\tauthToken\"G\n" +
|
||||
"auth_token\x18\x02 \x01(\v2\x0e.app.AuthTokenR\tauthToken\"6\n" +
|
||||
"\x11LogoutUserRequest\x12!\n" +
|
||||
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\"G\n" +
|
||||
"\vNumericDate\x128\n" +
|
||||
"\ttimestamp\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\">\n" +
|
||||
"\vPageInfoReq\x12\x12\n" +
|
||||
|
|
@ -1923,7 +2021,10 @@ const file_app_proto_rawDesc = "" +
|
|||
"\x14_registration_sourceB\x0e\n" +
|
||||
"\f_birthday_atB\r\n" +
|
||||
"\v_created_atB\r\n" +
|
||||
"\v_updated_at\"\x83\x01\n" +
|
||||
"\v_updated_at\"G\n" +
|
||||
"\tUserToken\x12!\n" +
|
||||
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12\x17\n" +
|
||||
"\auser_id\x18\x02 \x01(\x04R\x06userId\"\x83\x01\n" +
|
||||
"\rVerifyCodeReq\x12'\n" +
|
||||
"\x04type\x18\x01 \x01(\x0e2\x13.app.VerifyCodeTypeR\x04type\x123\n" +
|
||||
"\faccount_type\x18\x02 \x01(\x0e2\x10.app.AccountTypeR\vaccountType\x12\x14\n" +
|
||||
|
|
@ -1947,13 +2048,15 @@ const file_app_proto_rawDesc = "" +
|
|||
"\fUPDATE_PHONE\x10\x05\x12\x10\n" +
|
||||
"\fUPDATE_EMAIL\x10\x06\x12\f\n" +
|
||||
"\bWITHDRAW\x10\a\x12\x17\n" +
|
||||
"\x13CHANGE_PAY_PASSWORD\x10\b2\xcb\x02\n" +
|
||||
"\x13CHANGE_PAY_PASSWORD\x10\b2\x81\x03\n" +
|
||||
"\x03App\x12,\n" +
|
||||
"\tAuthToken\x12\f.app.AuthReq\x1a\x11.app.AuthInfoResp\x128\n" +
|
||||
"\rGetVerifyCode\x12\x12.app.VerifyCodeReq\x1a\x13.app.VerifyCodeResp\x12C\n" +
|
||||
"\fRegisterUser\x12\x18.app.RegisterUserRequest\x1a\x19.app.RegisterUserResponse\x122\n" +
|
||||
"\tLoginUser\x12\x11.app.LoginRequest\x1a\x12.app.LoginResponse\x128\n" +
|
||||
"\tListUsers\x12\x14.app.PageUserRequest\x1a\x15.app.PageUserResponse\x12)\n" +
|
||||
"\tListUsers\x12\x14.app.PageUserRequest\x1a\x15.app.PageUserResponse\x124\n" +
|
||||
"\n" +
|
||||
"LogoutUser\x12\x0e.app.UserToken\x1a\x16.app.LogoutUserRequest\x12)\n" +
|
||||
"\fInitDatabase\x12\n" +
|
||||
".app.Empty\x1a\r.app.BaseRespB\aZ\x05./appb\x06proto3"
|
||||
|
||||
|
|
@ -1970,7 +2073,7 @@ func file_app_proto_rawDescGZIP() []byte {
|
|||
}
|
||||
|
||||
var file_app_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_app_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
|
||||
var file_app_proto_msgTypes = make([]protoimpl.MessageInfo, 27)
|
||||
var file_app_proto_goTypes = []any{
|
||||
(AccountType)(0), // 0: app.AccountType
|
||||
(VerifyCodeType)(0), // 1: app.VerifyCodeType
|
||||
|
|
@ -1987,53 +2090,57 @@ var file_app_proto_goTypes = []any{
|
|||
(*IDsReq)(nil), // 12: app.IDsReq
|
||||
(*LoginRequest)(nil), // 13: app.LoginRequest
|
||||
(*LoginResponse)(nil), // 14: app.LoginResponse
|
||||
(*NumericDate)(nil), // 15: app.NumericDate
|
||||
(*PageInfoReq)(nil), // 16: app.PageInfoReq
|
||||
(*PageUserRequest)(nil), // 17: app.PageUserRequest
|
||||
(*PageUserResponse)(nil), // 18: app.PageUserResponse
|
||||
(*RegisterUserRequest)(nil), // 19: app.RegisterUserRequest
|
||||
(*RegisterUserResponse)(nil), // 20: app.RegisterUserResponse
|
||||
(*RegisteredClaims)(nil), // 21: app.RegisteredClaims
|
||||
(*UUIDReq)(nil), // 22: app.UUIDReq
|
||||
(*UUIDsReq)(nil), // 23: app.UUIDsReq
|
||||
(*UserInfo)(nil), // 24: app.UserInfo
|
||||
(*VerifyCodeReq)(nil), // 25: app.VerifyCodeReq
|
||||
(*VerifyCodeResp)(nil), // 26: app.VerifyCodeResp
|
||||
(*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp
|
||||
(*structpb.Struct)(nil), // 28: google.protobuf.Struct
|
||||
(*LogoutUserRequest)(nil), // 15: app.LogoutUserRequest
|
||||
(*NumericDate)(nil), // 16: app.NumericDate
|
||||
(*PageInfoReq)(nil), // 17: app.PageInfoReq
|
||||
(*PageUserRequest)(nil), // 18: app.PageUserRequest
|
||||
(*PageUserResponse)(nil), // 19: app.PageUserResponse
|
||||
(*RegisterUserRequest)(nil), // 20: app.RegisterUserRequest
|
||||
(*RegisterUserResponse)(nil), // 21: app.RegisterUserResponse
|
||||
(*RegisteredClaims)(nil), // 22: app.RegisteredClaims
|
||||
(*UUIDReq)(nil), // 23: app.UUIDReq
|
||||
(*UUIDsReq)(nil), // 24: app.UUIDsReq
|
||||
(*UserInfo)(nil), // 25: app.UserInfo
|
||||
(*UserToken)(nil), // 26: app.UserToken
|
||||
(*VerifyCodeReq)(nil), // 27: app.VerifyCodeReq
|
||||
(*VerifyCodeResp)(nil), // 28: app.VerifyCodeResp
|
||||
(*timestamppb.Timestamp)(nil), // 29: google.protobuf.Timestamp
|
||||
(*structpb.Struct)(nil), // 30: google.protobuf.Struct
|
||||
}
|
||||
var file_app_proto_depIdxs = []int32{
|
||||
21, // 0: app.AuthInfoResp.claims:type_name -> app.RegisteredClaims
|
||||
27, // 1: app.AuthReq.time:type_name -> google.protobuf.Timestamp
|
||||
27, // 2: app.AuthToken.access_token_expires:type_name -> google.protobuf.Timestamp
|
||||
27, // 3: app.AuthToken.refresh_token_expires:type_name -> google.protobuf.Timestamp
|
||||
24, // 4: app.LoginResponse.user:type_name -> app.UserInfo
|
||||
22, // 0: app.AuthInfoResp.claims:type_name -> app.RegisteredClaims
|
||||
29, // 1: app.AuthReq.time:type_name -> google.protobuf.Timestamp
|
||||
29, // 2: app.AuthToken.access_token_expires:type_name -> google.protobuf.Timestamp
|
||||
29, // 3: app.AuthToken.refresh_token_expires:type_name -> google.protobuf.Timestamp
|
||||
25, // 4: app.LoginResponse.user:type_name -> app.UserInfo
|
||||
4, // 5: app.LoginResponse.auth_token:type_name -> app.AuthToken
|
||||
27, // 6: app.NumericDate.timestamp:type_name -> google.protobuf.Timestamp
|
||||
24, // 7: app.PageUserResponse.data:type_name -> app.UserInfo
|
||||
24, // 8: app.RegisterUserResponse.user:type_name -> app.UserInfo
|
||||
29, // 6: app.NumericDate.timestamp:type_name -> google.protobuf.Timestamp
|
||||
25, // 7: app.PageUserResponse.data:type_name -> app.UserInfo
|
||||
25, // 8: app.RegisterUserResponse.user:type_name -> app.UserInfo
|
||||
4, // 9: app.RegisterUserResponse.auth_token:type_name -> app.AuthToken
|
||||
9, // 10: app.RegisteredClaims.audience:type_name -> app.ClaimStrings
|
||||
15, // 11: app.RegisteredClaims.expires_at:type_name -> app.NumericDate
|
||||
15, // 12: app.RegisteredClaims.not_before:type_name -> app.NumericDate
|
||||
15, // 13: app.RegisteredClaims.issued_at:type_name -> app.NumericDate
|
||||
28, // 14: app.UserInfo.metadata:type_name -> google.protobuf.Struct
|
||||
16, // 11: app.RegisteredClaims.expires_at:type_name -> app.NumericDate
|
||||
16, // 12: app.RegisteredClaims.not_before:type_name -> app.NumericDate
|
||||
16, // 13: app.RegisteredClaims.issued_at:type_name -> app.NumericDate
|
||||
30, // 14: app.UserInfo.metadata:type_name -> google.protobuf.Struct
|
||||
1, // 15: app.VerifyCodeReq.type:type_name -> app.VerifyCodeType
|
||||
0, // 16: app.VerifyCodeReq.account_type:type_name -> app.AccountType
|
||||
3, // 17: app.App.AuthToken:input_type -> app.AuthReq
|
||||
25, // 18: app.App.GetVerifyCode:input_type -> app.VerifyCodeReq
|
||||
19, // 19: app.App.RegisterUser:input_type -> app.RegisterUserRequest
|
||||
27, // 18: app.App.GetVerifyCode:input_type -> app.VerifyCodeReq
|
||||
20, // 19: app.App.RegisterUser:input_type -> app.RegisterUserRequest
|
||||
13, // 20: app.App.LoginUser:input_type -> app.LoginRequest
|
||||
17, // 21: app.App.ListUsers:input_type -> app.PageUserRequest
|
||||
10, // 22: app.App.InitDatabase:input_type -> app.Empty
|
||||
2, // 23: app.App.AuthToken:output_type -> app.AuthInfoResp
|
||||
26, // 24: app.App.GetVerifyCode:output_type -> app.VerifyCodeResp
|
||||
20, // 25: app.App.RegisterUser:output_type -> app.RegisterUserResponse
|
||||
14, // 26: app.App.LoginUser:output_type -> app.LoginResponse
|
||||
18, // 27: app.App.ListUsers:output_type -> app.PageUserResponse
|
||||
7, // 28: app.App.InitDatabase:output_type -> app.BaseResp
|
||||
23, // [23:29] is the sub-list for method output_type
|
||||
17, // [17:23] is the sub-list for method input_type
|
||||
18, // 21: app.App.ListUsers:input_type -> app.PageUserRequest
|
||||
26, // 22: app.App.LogoutUser:input_type -> app.UserToken
|
||||
10, // 23: app.App.InitDatabase:input_type -> app.Empty
|
||||
2, // 24: app.App.AuthToken:output_type -> app.AuthInfoResp
|
||||
28, // 25: app.App.GetVerifyCode:output_type -> app.VerifyCodeResp
|
||||
21, // 26: app.App.RegisterUser:output_type -> app.RegisterUserResponse
|
||||
14, // 27: app.App.LoginUser:output_type -> app.LoginResponse
|
||||
19, // 28: app.App.ListUsers:output_type -> app.PageUserResponse
|
||||
15, // 29: app.App.LogoutUser:output_type -> app.LogoutUserRequest
|
||||
7, // 30: app.App.InitDatabase:output_type -> app.BaseResp
|
||||
24, // [24:31] is the sub-list for method output_type
|
||||
17, // [17:24] is the sub-list for method input_type
|
||||
17, // [17:17] is the sub-list for extension type_name
|
||||
17, // [17:17] is the sub-list for extension extendee
|
||||
0, // [0:17] is the sub-list for field type_name
|
||||
|
|
@ -2045,16 +2152,16 @@ func file_app_proto_init() {
|
|||
return
|
||||
}
|
||||
file_app_proto_msgTypes[11].OneofWrappers = []any{}
|
||||
file_app_proto_msgTypes[15].OneofWrappers = []any{}
|
||||
file_app_proto_msgTypes[17].OneofWrappers = []any{}
|
||||
file_app_proto_msgTypes[22].OneofWrappers = []any{}
|
||||
file_app_proto_msgTypes[16].OneofWrappers = []any{}
|
||||
file_app_proto_msgTypes[18].OneofWrappers = []any{}
|
||||
file_app_proto_msgTypes[23].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proto_rawDesc), len(file_app_proto_rawDesc)),
|
||||
NumEnums: 2,
|
||||
NumMessages: 25,
|
||||
NumMessages: 27,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.0
|
||||
// - protoc v3.21.11
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v3.19.4
|
||||
// source: app.proto
|
||||
|
||||
package app
|
||||
|
|
@ -24,6 +24,7 @@ const (
|
|||
App_RegisterUser_FullMethodName = "/app.App/RegisterUser"
|
||||
App_LoginUser_FullMethodName = "/app.App/LoginUser"
|
||||
App_ListUsers_FullMethodName = "/app.App/ListUsers"
|
||||
App_LogoutUser_FullMethodName = "/app.App/LogoutUser"
|
||||
App_InitDatabase_FullMethodName = "/app.App/InitDatabase"
|
||||
)
|
||||
|
||||
|
|
@ -48,6 +49,9 @@ type AppClient interface {
|
|||
// 分页查询用户信息
|
||||
// group: user
|
||||
ListUsers(ctx context.Context, in *PageUserRequest, opts ...grpc.CallOption) (*PageUserResponse, error)
|
||||
// 用户退出登录
|
||||
// group: user
|
||||
LogoutUser(ctx context.Context, in *UserToken, opts ...grpc.CallOption) (*LogoutUserRequest, error)
|
||||
// group: base
|
||||
InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error)
|
||||
}
|
||||
|
|
@ -110,6 +114,16 @@ func (c *appClient) ListUsers(ctx context.Context, in *PageUserRequest, opts ...
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *appClient) LogoutUser(ctx context.Context, in *UserToken, opts ...grpc.CallOption) (*LogoutUserRequest, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(LogoutUserRequest)
|
||||
err := c.cc.Invoke(ctx, App_LogoutUser_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *appClient) InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(BaseResp)
|
||||
|
|
@ -141,6 +155,9 @@ type AppServer interface {
|
|||
// 分页查询用户信息
|
||||
// group: user
|
||||
ListUsers(context.Context, *PageUserRequest) (*PageUserResponse, error)
|
||||
// 用户退出登录
|
||||
// group: user
|
||||
LogoutUser(context.Context, *UserToken) (*LogoutUserRequest, error)
|
||||
// group: base
|
||||
InitDatabase(context.Context, *Empty) (*BaseResp, error)
|
||||
mustEmbedUnimplementedAppServer()
|
||||
|
|
@ -154,22 +171,25 @@ type AppServer interface {
|
|||
type UnimplementedAppServer struct{}
|
||||
|
||||
func (UnimplementedAppServer) AuthToken(context.Context, *AuthReq) (*AuthInfoResp, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AuthToken not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AuthToken not implemented")
|
||||
}
|
||||
func (UnimplementedAppServer) GetVerifyCode(context.Context, *VerifyCodeReq) (*VerifyCodeResp, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetVerifyCode not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetVerifyCode not implemented")
|
||||
}
|
||||
func (UnimplementedAppServer) RegisterUser(context.Context, *RegisterUserRequest) (*RegisterUserResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RegisterUser not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RegisterUser not implemented")
|
||||
}
|
||||
func (UnimplementedAppServer) LoginUser(context.Context, *LoginRequest) (*LoginResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method LoginUser not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method LoginUser not implemented")
|
||||
}
|
||||
func (UnimplementedAppServer) ListUsers(context.Context, *PageUserRequest) (*PageUserResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListUsers not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListUsers not implemented")
|
||||
}
|
||||
func (UnimplementedAppServer) LogoutUser(context.Context, *UserToken) (*LogoutUserRequest, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method LogoutUser not implemented")
|
||||
}
|
||||
func (UnimplementedAppServer) InitDatabase(context.Context, *Empty) (*BaseResp, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method InitDatabase not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method InitDatabase not implemented")
|
||||
}
|
||||
func (UnimplementedAppServer) mustEmbedUnimplementedAppServer() {}
|
||||
func (UnimplementedAppServer) testEmbeddedByValue() {}
|
||||
|
|
@ -182,7 +202,7 @@ type UnsafeAppServer interface {
|
|||
}
|
||||
|
||||
func RegisterAppServer(s grpc.ServiceRegistrar, srv AppServer) {
|
||||
// If the following call panics, it indicates UnimplementedAppServer was
|
||||
// If the following call pancis, it indicates UnimplementedAppServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
|
|
@ -282,6 +302,24 @@ func _App_ListUsers_Handler(srv interface{}, ctx context.Context, dec func(inter
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _App_LogoutUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UserToken)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AppServer).LogoutUser(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: App_LogoutUser_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AppServer).LogoutUser(ctx, req.(*UserToken))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _App_InitDatabase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
|
|
@ -327,6 +365,10 @@ var App_ServiceDesc = grpc.ServiceDesc{
|
|||
MethodName: "ListUsers",
|
||||
Handler: _App_ListUsers_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "LogoutUser",
|
||||
Handler: _App_LogoutUser_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "InitDatabase",
|
||||
Handler: _App_InitDatabase_Handler,
|
||||
|
|
|
|||
Loading…
Reference in New Issue