add:
This commit is contained in:
@@ -0,0 +1,601 @@
|
||||
# JWT 集成指南
|
||||
|
||||
指导如何将 JWT Manager 集成到 RPC Handlers 和业务逻辑中。
|
||||
|
||||
## 1. gRPC Unary Interceptor 实现
|
||||
|
||||
在 RPC 服务中添加 JWT 验证拦截器。
|
||||
|
||||
### 创建拦截器
|
||||
|
||||
创建文件 [app/users/rpc/internal/interceptor/jwt_interceptor.go](../../../app/users/rpc/internal/interceptor/jwt_interceptor.go):
|
||||
|
||||
```go
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"yourmodule/app/users/rpc/internal/svc"
|
||||
)
|
||||
|
||||
// JwtUnaryInterceptor 验证 gRPC 请求中的 JWT 令牌
|
||||
func JwtUnaryInterceptor(svcCtx *svc.ServiceContext) grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
// 获取请求元数据
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Unauthenticated, "missing metadata")
|
||||
}
|
||||
|
||||
// 从 Authorization 头提取令牌
|
||||
tokens := md.Get("authorization")
|
||||
if len(tokens) == 0 {
|
||||
return nil, status.Error(codes.Unauthenticated, "missing authorization header")
|
||||
}
|
||||
|
||||
token := tokens[0]
|
||||
|
||||
// 验证令牌
|
||||
claims, err := svcCtx.JwtManager.Valid(ctx, token)
|
||||
if err != nil {
|
||||
log.Printf("Token validation failed: %v", err)
|
||||
|
||||
// 尝试刷新令牌(如果过期但仍在 Redis 中)
|
||||
newToken, refreshErr := svcCtx.JwtManager.Renew(ctx, token)
|
||||
if refreshErr == nil && newToken != "" {
|
||||
// 在响应头中返回新令牌
|
||||
grpc.SetHeader(ctx, metadata.Pairs("authorization", newToken))
|
||||
// 继续处理请求,使用原令牌的声明
|
||||
// 注意:实际应用中需要重新验证新令牌
|
||||
newClaims, err := svcCtx.JwtManager.Valid(ctx, newToken)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "token refresh failed")
|
||||
}
|
||||
claims = newClaims
|
||||
} else {
|
||||
return nil, status.Error(codes.Unauthenticated, "invalid or expired token")
|
||||
}
|
||||
}
|
||||
|
||||
// 将声明附加到上下文,供处理器使用
|
||||
newCtx := context.WithValue(ctx, "claims", claims)
|
||||
|
||||
return handler(newCtx, req)
|
||||
}
|
||||
}
|
||||
|
||||
// JwtStreamInterceptor 验证流式 gRPC 请求中的 JWT 令牌
|
||||
func JwtStreamInterceptor(svcCtx *svc.ServiceContext) grpc.StreamServerInterceptor {
|
||||
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
md, ok := metadata.FromIncomingContext(ss.Context())
|
||||
if !ok {
|
||||
return status.Error(codes.Unauthenticated, "missing metadata")
|
||||
}
|
||||
|
||||
tokens := md.Get("authorization")
|
||||
if len(tokens) == 0 {
|
||||
return status.Error(codes.Unauthenticated, "missing authorization header")
|
||||
}
|
||||
|
||||
token := tokens[0]
|
||||
claims, err := svcCtx.JwtManager.Valid(ss.Context(), token)
|
||||
if err != nil {
|
||||
return status.Error(codes.Unauthenticated, "invalid token")
|
||||
}
|
||||
|
||||
// 创建包装流以注入上下文
|
||||
wrappedStream := &WrappedStream{
|
||||
ServerStream: ss,
|
||||
ctx: context.WithValue(ss.Context(), "claims", claims),
|
||||
}
|
||||
|
||||
return handler(srv, wrappedStream)
|
||||
}
|
||||
}
|
||||
|
||||
// WrappedStream 包装 grpc.ServerStream 以注入新的上下文
|
||||
type WrappedStream struct {
|
||||
grpc.ServerStream
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (w *WrappedStream) Context() context.Context {
|
||||
return w.ctx
|
||||
}
|
||||
```
|
||||
|
||||
### 在 Server 中注册拦截器
|
||||
|
||||
修改 [app/users/rpc/usercenter/usercenter.go](../../../app/users/rpc/usercenter/usercenter.go):
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"yourmodule/app/users/rpc/internal/config"
|
||||
"yourmodule/app/users/rpc/internal/interceptor"
|
||||
"yourmodule/app/users/rpc/internal/server"
|
||||
"yourmodule/app/users/rpc/internal/svc"
|
||||
"yourmodule/app/users/rpc/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/conf"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var configFile = flag.String("f", "etc/pb.yaml", "the config file")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
var c config.Config
|
||||
conf.MustLoad(*configFile, &c)
|
||||
ctx := svc.NewServiceContext(c)
|
||||
|
||||
logx.DisableStat()
|
||||
|
||||
s := grpc.NewServer(
|
||||
grpc.UnaryInterceptor(interceptor.JwtUnaryInterceptor(ctx)),
|
||||
grpc.StreamInterceptor(interceptor.JwtStreamInterceptor(ctx)),
|
||||
)
|
||||
|
||||
pb.RegisterUsercenterServer(s, server.NewUsercenterServer(ctx))
|
||||
|
||||
logx.Infof("Starting gRPC server on %s:%d", c.Host, c.Port)
|
||||
if err := s.Serve(net.Listen("tcp", "0.0.0.0:"+fmt.Sprintf("%d", c.Port))); err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 登录 Handler 实现
|
||||
|
||||
实现 [app/users/api/internal/handler/user/loginHandler.go](../../../app/users/api/internal/handler/user/loginHandler.go):
|
||||
|
||||
```go
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"yourmodule/app/users/api/internal/logic/user"
|
||||
"yourmodule/app/users/api/internal/svc"
|
||||
"yourmodule/app/users/api/internal/types"
|
||||
)
|
||||
|
||||
// LoginHandler 处理用户登录
|
||||
func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.LoginRequest
|
||||
|
||||
// 解析请求体...
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 调用业务逻辑
|
||||
resp, err := user.NewLoginLogic(r.Context(), svcCtx).Login(&req)
|
||||
if err != nil {
|
||||
log.Printf("Login failed: %v", err)
|
||||
http.Error(w, "Login failed", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回令牌
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实现 [app/users/api/internal/logic/user/loginLogic.go](../../../app/users/api/internal/logic/user/loginLogic.go):
|
||||
|
||||
```go
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"yourmodule/app/users/api/internal/svc"
|
||||
"yourmodule/app/users/api/internal/types"
|
||||
)
|
||||
|
||||
type LoginLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
|
||||
return &LoginLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LoginLogic) Login(req *types.LoginRequest) (*types.LoginResponse, error) {
|
||||
// 1. 验证用户凭证(密码等)
|
||||
user, err := l.svcCtx.UserModel.FindByEmail(l.ctx, req.Email)
|
||||
if err != nil {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
|
||||
// 2. 验证密码
|
||||
if !user.VerifyPassword(req.Password) {
|
||||
return nil, errors.New("invalid password")
|
||||
}
|
||||
|
||||
// 3. 生成 JWT 令牌
|
||||
token, err := l.svcCtx.JwtManager.New(
|
||||
l.ctx,
|
||||
user.ID,
|
||||
user.Email,
|
||||
user.Name,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to generate token")
|
||||
}
|
||||
|
||||
// 4. 返回令牌
|
||||
return &types.LoginResponse{
|
||||
Token: token,
|
||||
User: types.User{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 在 Handlers 中使用声明
|
||||
|
||||
在 Protected Handlers 中提取并使用声明:
|
||||
|
||||
```go
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"yourmodule/app/users/api/internal/svc"
|
||||
"yourmodule/app/users/api/internal/types"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
// GetUserInfoHandler 获取当前用户信息
|
||||
func GetUserInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// 从上下文提取声明(由拦截器设置)
|
||||
claims, ok := r.Context().Value("claims").(*jwt.RegisteredClaims)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 使用声明中的用户信息
|
||||
userID := claims.Subject // 用户 ID 存储在 Subject 中
|
||||
log.Printf("User %s requested their info", userID)
|
||||
|
||||
// 查询用户信息
|
||||
user, err := svcCtx.UserModel.FindByID(r.Context(), userID)
|
||||
if err != nil {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回用户信息
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 令牌刷新端点
|
||||
|
||||
实现令牌刷新端点:
|
||||
|
||||
```go
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"yourmodule/app/users/api/internal/svc"
|
||||
"yourmodule/app/users/api/internal/types"
|
||||
)
|
||||
|
||||
// RefreshTokenHandler 刷新过期的 JWT 令牌
|
||||
func RefreshTokenHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.RefreshTokenRequest
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 提取旧令牌
|
||||
oldToken := req.Token
|
||||
|
||||
// 尝试刷新令牌
|
||||
newToken, err := svcCtx.JwtManager.Renew(r.Context(), oldToken)
|
||||
if err != nil {
|
||||
http.Error(w, "Token refresh failed", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回新令牌
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(types.RefreshTokenResponse{
|
||||
Token: newToken,
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 登出处理
|
||||
|
||||
实现登出端点以撤销令牌:
|
||||
|
||||
```go
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"yourmodule/app/users/api/internal/svc"
|
||||
)
|
||||
|
||||
// LogoutHandler 登出用户(撤销令牌)
|
||||
func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// 从上下文提取声明
|
||||
claims, ok := r.Context().Value("claims").(*jwt.RegisteredClaims)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
userID := claims.Subject
|
||||
|
||||
// 获取用户当前令牌
|
||||
currentToken := r.Header.Get("Authorization")
|
||||
|
||||
// 撤销令牌
|
||||
err := svcCtx.JwtManager.Revoke(r.Context(), userID, currentToken)
|
||||
if err != nil {
|
||||
http.Error(w, "Logout failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{"message": "logged out successfully"})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 特定端点的 JWT 验证
|
||||
|
||||
对于 REST API,在需要的 handlers 中手动验证令牌:
|
||||
|
||||
### 在 Routes 中配置
|
||||
|
||||
修改 [app/users/api/internal/handler/routes.go](../../../app/users/api/internal/handler/routes.go):
|
||||
|
||||
```go
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"yourmodule/app/users/api/internal/middleware"
|
||||
"yourmodule/app/users/api/internal/svc"
|
||||
"yourmodule/app/users/api/internal/handler/user"
|
||||
)
|
||||
|
||||
// RegisterRoutes 注册所有路由
|
||||
func RegisterRoutes(router *http.ServeMux, svcCtx *svc.ServiceContext) {
|
||||
// 公开路由
|
||||
router.HandleFunc("POST /api/v1/auth/login", user.LoginHandler(svcCtx))
|
||||
router.HandleFunc("POST /api/v1/auth/refresh", user.RefreshTokenHandler(svcCtx))
|
||||
|
||||
// 受保护的路由(需要 JWT 验证)
|
||||
protected := middleware.JwtMiddleware(svcCtx)
|
||||
router.HandleFunc("GET /api/v1/users/me", protected(user.GetUserInfoHandler(svcCtx)))
|
||||
router.HandleFunc("POST /api/v1/users/logout", protected(user.LogoutHandler(svcCtx)))
|
||||
router.HandleFunc("PUT /api/v1/users/me", protected(user.UpdateUserInfoHandler(svcCtx)))
|
||||
}
|
||||
```
|
||||
|
||||
### 创建 JWT 中间件
|
||||
|
||||
创建 [app/users/api/internal/middleware/jwt.go](../../../app/users/api/internal/middleware/jwt.go):
|
||||
|
||||
```go
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"yourmodule/app/users/api/internal/svc"
|
||||
)
|
||||
|
||||
// JwtMiddleware 为 HTTP 处理器添加 JWT 验证
|
||||
func JwtMiddleware(svcCtx *svc.ServiceContext) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 从 Authorization 头提取令牌
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
http.Error(w, "Missing authorization header", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 期望格式: "Bearer <token>"
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
http.Error(w, "Invalid authorization header", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
token := parts[1]
|
||||
|
||||
// 验证令牌
|
||||
claims, err := svcCtx.JwtManager.Valid(r.Context(), token)
|
||||
if err != nil {
|
||||
// 尝试刷新
|
||||
newToken, refreshErr := svcCtx.JwtManager.Renew(r.Context(), token)
|
||||
if refreshErr != nil {
|
||||
http.Error(w, "Invalid or expired token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 在响应头返回新令牌
|
||||
w.Header().Set("X-New-Token", newToken)
|
||||
|
||||
// 重新验证新令牌
|
||||
claims, err = svcCtx.JwtManager.Valid(r.Context(), newToken)
|
||||
if err != nil {
|
||||
http.Error(w, "Token refresh failed", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 将声明附加到上下文
|
||||
newCtx := context.WithValue(r.Context(), "claims", claims)
|
||||
next.ServeHTTP(w, r.WithContext(newCtx))
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 错误处理最佳实践
|
||||
|
||||
```go
|
||||
package logic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"yourmodule/app/users/rpc/internal/utils"
|
||||
)
|
||||
|
||||
// HandleJwtError 处理 JWT 相关错误
|
||||
func HandleJwtError(err error) error {
|
||||
if errors.Is(err, utils.ErrTokenExpired) {
|
||||
log.Println("Token has expired, user needs to refresh")
|
||||
return errors.New("token expired - use refresh endpoint")
|
||||
}
|
||||
|
||||
if errors.Is(err, utils.ErrTokenInvalid) {
|
||||
log.Println("Token is invalid or malformed")
|
||||
return errors.New("invalid token")
|
||||
}
|
||||
|
||||
if errors.Is(err, utils.ErrTokenNotFound) {
|
||||
log.Println("Token not found in Redis (revoked or expired)")
|
||||
return errors.New("token revoked or expired")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 测试 JWT 集成
|
||||
|
||||
### 单元测试示例
|
||||
|
||||
```go
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func TestJwtUnaryInterceptor_ValidToken(t *testing.T) {
|
||||
// 1. 创建有效的令牌
|
||||
token, err := svcCtx.JwtManager.New(context.Background(), "user123", "user@example.com", "John")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create token: %v", err)
|
||||
}
|
||||
|
||||
// 2. 创建包含令牌的上下文
|
||||
md := metadata.Pairs("authorization", token)
|
||||
ctx := metadata.NewIncomingContext(context.Background(), md)
|
||||
|
||||
// 3. 调用拦截器
|
||||
_, err = JwtUnaryInterceptor(svcCtx)(ctx, nil, nil, func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return "success", nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJwtUnaryInterceptor_ExpiredToken(t *testing.T) {
|
||||
// 1. 创建过期的令牌或使用无效令牌
|
||||
token := "invalid.token.here"
|
||||
|
||||
// 2. 创建包含令牌的上下文
|
||||
md := metadata.Pairs("authorization", token)
|
||||
ctx := metadata.NewIncomingContext(context.Background(), md)
|
||||
|
||||
// 3. 调用拦截器
|
||||
_, err := JwtUnaryInterceptor(svcCtx)(ctx, nil, nil, func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return "success", nil
|
||||
})
|
||||
|
||||
// 4. 验证错误
|
||||
st, ok := status.FromError(err)
|
||||
if !ok || st.Code() != codes.Unauthenticated {
|
||||
t.Errorf("Expected Unauthenticated error, got: %v", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 9. 生产部署清单
|
||||
|
||||
在将 JWT 集成部署到生产环境前:
|
||||
|
||||
- [ ] 所有令牌端点都进行了压力测试
|
||||
- [ ] 令牌刷新逻辑已验证
|
||||
- [ ] 错误处理覆盖了所有 JWT 失败情况
|
||||
- [ ] 审计日志记录了所有认证尝试
|
||||
- [ ] 密钥轮换计划已确定
|
||||
- [ ] 监控和告警已配置
|
||||
- [ ] 灾难恢复流程已文档化
|
||||
- [ ] 所有依赖于 JWT 的服务都已更新
|
||||
|
||||
## 相关文件
|
||||
|
||||
- [app/users/rpc/internal/utils/jwt.go](../../../app/users/rpc/internal/utils/jwt.go) - JWT Manager 实现
|
||||
- [app/users/rpc/internal/config/config.go](../../../app/users/rpc/internal/config/config.go) - JWT 配置
|
||||
- [app/users/rpc/internal/svc/serviceContext.go](../../../app/users/rpc/internal/svc/serviceContext.go) - 依赖注入
|
||||
- [deploy/k8s/secrets/jwt-secret.yaml](./jwt-secret.yaml) - Secret 和 RBAC
|
||||
- [deploy/k8s/secrets/DEPLOYMENT.md](./DEPLOYMENT.md) - 部署指南
|
||||
@@ -28,7 +28,6 @@ func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic
|
||||
}
|
||||
|
||||
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return
|
||||
return &types.LoginResp{}, nil
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"juwan-backend/app/users/api/internal/svc"
|
||||
"juwan-backend/app/users/api/internal/types"
|
||||
"juwan-backend/app/users/rpc/pb"
|
||||
"juwan-backend/common/utils"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
@@ -31,25 +32,44 @@ func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Register
|
||||
}
|
||||
|
||||
func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
user, err := l.svcCtx.UserRpc.GetUserByUsername(l.ctx, &pb.GetUserByUsernameReq{
|
||||
// 检查用户是否已存在
|
||||
existingUser, err := l.svcCtx.UserRpc.GetUserByUsername(l.ctx, &pb.GetUserByUsernameReq{
|
||||
Username: req.Username,
|
||||
})
|
||||
if err == nil || user != nil {
|
||||
return nil, errors.New("User is exisit")
|
||||
}
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, errors.New("Register is failed")
|
||||
if err == nil && existingUser != nil {
|
||||
return nil, errors.New("用户已存在")
|
||||
}
|
||||
|
||||
_, err = l.svcCtx.UserRpc.AddUsers(l.ctx, &pb.AddUsersReq{
|
||||
UserId: id.String(),
|
||||
// 生成用户ID
|
||||
userId, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, errors.New("注册失败:无法生成用户ID")
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
hashedPassword, err := utils.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
return nil, errors.New("注册失败:密码加密失败")
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
newUser, err := l.svcCtx.UserRpc.AddUsers(l.ctx, &pb.AddUsersReq{
|
||||
UserId: userId.String(),
|
||||
Username: req.Username,
|
||||
Passwd: req.Password,
|
||||
Passwd: hashedPassword,
|
||||
Phone: req.Phone,
|
||||
State: true,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorf("AddUsers failed: %v", err)
|
||||
return nil, errors.New("注册失败:创建用户失败")
|
||||
}
|
||||
|
||||
return
|
||||
// 返回响应
|
||||
return &types.RegisterResp{
|
||||
UserId: int64(newUser.), // RPC 返回的可能是用户信息,这里简化处理
|
||||
Username: req.Username,
|
||||
Email: req.Email,
|
||||
Message: "注册成功",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ ListenOn: 0.0.0.0:9001
|
||||
|
||||
Prometheus:
|
||||
Host: 0.0.0.0
|
||||
Port: 9001
|
||||
Port: 4001
|
||||
Path: /metrics
|
||||
|
||||
DataSource: "${DB_URI}?sslmode=disable"
|
||||
@@ -13,3 +13,7 @@ CacheConf:
|
||||
Type: cluster
|
||||
Pass: "${REDIS_PASSWORD}"
|
||||
User: "default"
|
||||
|
||||
Jwt:
|
||||
SecretKey: "${JWT_SECRET_KEY}"
|
||||
Issuer: "juwan-user-rpc"
|
||||
|
||||
@@ -5,8 +5,14 @@ import (
|
||||
"github.com/zeromicro/go-zero/zrpc"
|
||||
)
|
||||
|
||||
type JwtConfig struct {
|
||||
SecretKey string `json:"secretKey"`
|
||||
Issuer string `json:"issuer"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
zrpc.RpcServerConf
|
||||
DataSource string `json:"dataSource"`
|
||||
CacheConf cache.CacheConf
|
||||
Jwt JwtConfig `json:"jwt"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"juwan-backend/app/users/rpc/internal/svc"
|
||||
"juwan-backend/app/users/rpc/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type CheckPermissionLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewCheckPermissionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckPermissionLogic {
|
||||
return &CheckPermissionLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CheckPermissionLogic) CheckPermission(in *pb.CheckPermissionReq) (*pb.CheckPermissionResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &pb.CheckPermissionResp{}, nil
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"juwan-backend/app/users/rpc/internal/svc"
|
||||
"juwan-backend/app/users/rpc/pb"
|
||||
"juwan-backend/common/converter"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
@@ -23,8 +24,19 @@ func NewGetUserByUsernameLogic(ctx context.Context, svcCtx *svc.ServiceContext)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetUserByUsernameLogic) GetUserByUsername(in *pb.GetUsersByIdReq) (*pb.GetUsersByIdResp, error) {
|
||||
func (l *GetUserByUsernameLogic) GetUserByUsername(in *pb.GetUserByUsernameReq) (*pb.GetUserByUsernameResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &pb.GetUsersByIdResp{}, nil
|
||||
user, err := l.svcCtx.UsersModel.FindOneByUsername(l.ctx, in.Username)
|
||||
pbUsers := &pb.Users{}
|
||||
converter.StructToStruct(user, pbUsers)
|
||||
if err == nil || user != nil {
|
||||
return &pb.GetUserByUsernameResp{
|
||||
Users: pbUsers,
|
||||
}, nil
|
||||
}
|
||||
if err.Error() != "not found" {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.GetUserByUsernameResp{}, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"juwan-backend/app/users/rpc/internal/svc"
|
||||
"juwan-backend/app/users/rpc/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type LoginLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
|
||||
return &LoginLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LoginLogic) Login(in *pb.LoginReq) (*pb.LoginResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &pb.LoginResp{}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"juwan-backend/app/users/rpc/internal/svc"
|
||||
"juwan-backend/app/users/rpc/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ValidateTokenLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewValidateTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ValidateTokenLogic {
|
||||
return &ValidateTokenLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ValidateTokenLogic) ValidateToken(in *pb.ValidateTokenReq) (*pb.ValidateTokenResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &pb.ValidateTokenResp{}, nil
|
||||
}
|
||||
@@ -53,3 +53,18 @@ func (s *UsercenterServer) SearchUsers(ctx context.Context, in *pb.SearchUsersRe
|
||||
l := logic.NewSearchUsersLogic(ctx, s.svcCtx)
|
||||
return l.SearchUsers(in)
|
||||
}
|
||||
|
||||
func (s *UsercenterServer) Login(ctx context.Context, in *pb.LoginReq) (*pb.LoginResp, error) {
|
||||
l := logic.NewLoginLogic(ctx, s.svcCtx)
|
||||
return l.Login(in)
|
||||
}
|
||||
|
||||
func (s *UsercenterServer) ValidateToken(ctx context.Context, in *pb.ValidateTokenReq) (*pb.ValidateTokenResp, error) {
|
||||
l := logic.NewValidateTokenLogic(ctx, s.svcCtx)
|
||||
return l.ValidateToken(in)
|
||||
}
|
||||
|
||||
func (s *UsercenterServer) CheckPermission(ctx context.Context, in *pb.CheckPermissionReq) (*pb.CheckPermissionResp, error) {
|
||||
l := logic.NewCheckPermissionLogic(ctx, s.svcCtx)
|
||||
return l.CheckPermission(in)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"juwan-backend/app/users/rpc/internal/config"
|
||||
"juwan-backend/app/users/rpc/internal/models"
|
||||
"juwan-backend/app/users/rpc/internal/utils"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
@@ -15,6 +16,7 @@ type ServiceContext struct {
|
||||
Config config.Config
|
||||
UsersModel models.UsersModel
|
||||
RedisCluster *redis.ClusterClient
|
||||
JwtManager *utils.JwtManager
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
@@ -39,9 +41,13 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize JWT Manager
|
||||
jwtManager := utils.NewJwtManager(redisCluster, c.Jwt.SecretKey, c.Jwt.Issuer)
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
UsersModel: models.NewUsersModel(conn, c.CacheConf),
|
||||
RedisCluster: redisCluster,
|
||||
JwtManager: jwtManager,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
// JWKS (JSON Web Key Set) 结构
|
||||
type JWKSKey struct {
|
||||
Kty string `json:"kty"`
|
||||
Use string `json:"use"`
|
||||
Kid string `json:"kid"`
|
||||
N string `json:"n,omitempty"`
|
||||
E string `json:"e,omitempty"`
|
||||
K string `json:"k,omitempty"` // 对称密钥
|
||||
Alg string `json:"alg"`
|
||||
}
|
||||
|
||||
type JWKS struct {
|
||||
Keys []JWKSKey `json:"keys"`
|
||||
}
|
||||
|
||||
// GenerateJWKSFromSecret 从密钥生成 JWKS(用于对称加密 HS256)
|
||||
func GenerateJWKSFromSecret(secretKey string, keyID string) *JWKS {
|
||||
// 对于 HS256,将密钥进行 base64 编码
|
||||
encodedSecret := base64.RawURLEncoding.EncodeToString([]byte(secretKey))
|
||||
|
||||
return &JWKS{
|
||||
Keys: []JWKSKey{
|
||||
{
|
||||
Kty: "oct",
|
||||
Use: "sig",
|
||||
Kid: keyID,
|
||||
K: encodedSecret,
|
||||
Alg: "HS256",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateJWKSEndpoint 生成可以被 Envoy 使用的 JWKS JSON
|
||||
// 此端点应在 user-rpc 中暴露,URL 为 /.well-known/jwks.json
|
||||
func GenerateJWKSEndpoint(secretKey string, keyID string) (string, error) {
|
||||
if secretKey == "" {
|
||||
return "", fmt.Errorf("secret key cannot be empty")
|
||||
}
|
||||
|
||||
jwks := GenerateJWKSFromSecret(secretKey, keyID)
|
||||
|
||||
jsonData, err := json.MarshalIndent(jwks, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(jsonData), nil
|
||||
}
|
||||
|
||||
// TokenPayload 令牌负载
|
||||
type TokenMetadata struct {
|
||||
IssuedAt time.Time
|
||||
ExpiresAt time.Time
|
||||
Subject string // userId
|
||||
Issuer string
|
||||
Audience string
|
||||
}
|
||||
|
||||
// ExtractTokenMetadata 从 token 中提取元数据(不验证签名)
|
||||
func ExtractTokenMetadata(tokenString string) (*TokenMetadata, error) {
|
||||
token, _, err := new(jwt.Parser).ParseUnverified(tokenString, &Claims{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(*Claims)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid token claims type")
|
||||
}
|
||||
|
||||
return &TokenMetadata{
|
||||
IssuedAt: claims.IssuedAt.Time,
|
||||
ExpiresAt: claims.ExpiresAt.Time,
|
||||
Subject: claims.UserId,
|
||||
Issuer: claims.Issuer,
|
||||
Audience: "", // 如果需要,可以增加到 Claims 中
|
||||
}, nil
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type TokenPayload struct {
|
||||
@@ -10,11 +16,16 @@ type TokenPayload struct {
|
||||
IsAdmin bool
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
TokenPayload
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
const (
|
||||
tokenCachePrefixUser = "jwt:user:"
|
||||
tokenCachePrefixToken = "jwt:token:"
|
||||
tokenCacheTTL = 60 * 24 * time.Hour
|
||||
tokenLifetime = 5 * 24 * time.Hour
|
||||
tokenCacheTTL = 30 * 24 * time.Hour
|
||||
tokenLifetime = 7 * 24 * time.Hour
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -22,9 +33,211 @@ var (
|
||||
errInvalidToken = errors.New("invalid token claims")
|
||||
errTokenNotInCache = errors.New("token not found in cache")
|
||||
errNoRedisClient = errors.New("redis client not configured")
|
||||
// errExpiredToken = errors.New("token expired")
|
||||
)
|
||||
|
||||
func NewToken(payload TokenPayload) (string, error) {
|
||||
|
||||
return "", nil
|
||||
type JwtManager struct {
|
||||
redisCluster *redis.ClusterClient
|
||||
secretKey string
|
||||
issuer string
|
||||
}
|
||||
|
||||
func NewJwtManager(redisCluster *redis.ClusterClient, secretKey, issuer string) *JwtManager {
|
||||
return &JwtManager{
|
||||
redisCluster: redisCluster,
|
||||
secretKey: secretKey,
|
||||
issuer: issuer,
|
||||
}
|
||||
}
|
||||
|
||||
// New 生成新的 JWT token
|
||||
func (m *JwtManager) New(ctx context.Context, payload *TokenPayload) (string, error) {
|
||||
if m.redisCluster == nil {
|
||||
return "", errNoRedisClient
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
expiresAt := now.Add(tokenLifetime)
|
||||
|
||||
claims := &Claims{
|
||||
TokenPayload: *payload,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
||||
IssuedAt: jwt.NewNumericDate(now),
|
||||
Issuer: m.issuer,
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString([]byte(m.secretKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 存储 token 到 Redis,TTL 为 30 天
|
||||
userKey := tokenCachePrefixUser + payload.UserId
|
||||
tokenKey := tokenCachePrefixToken + tokenString
|
||||
|
||||
tokenData, _ := json.Marshal(payload)
|
||||
|
||||
// 同时存储两个 key:用户 -> token 和 token -> payload
|
||||
pipe := m.redisCluster.Pipeline()
|
||||
pipe.Set(ctx, userKey, tokenString, tokenCacheTTL)
|
||||
pipe.Set(ctx, tokenKey, string(tokenData), tokenCacheTTL)
|
||||
_, err = pipe.Exec(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
// Valid 验证 token 有效性,支持自动换票
|
||||
func (m *JwtManager) Valid(ctx context.Context, tokenString string) (*TokenPayload, error) {
|
||||
if m.redisCluster == nil {
|
||||
return nil, errNoRedisClient
|
||||
}
|
||||
|
||||
if tokenString == "" {
|
||||
return nil, errMissingToken
|
||||
}
|
||||
|
||||
// 检查 token 是否在 Redis 中
|
||||
tokenKey := tokenCachePrefixToken + tokenString
|
||||
tokenData, err := m.redisCluster.Get(ctx, tokenKey).Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var payload TokenPayload
|
||||
if err == redis.Nil {
|
||||
return nil, errTokenNotInCache
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(tokenData), &payload)
|
||||
if err != nil {
|
||||
return nil, errInvalidToken
|
||||
}
|
||||
|
||||
// 解析 JWT 并验证签名和过期时间
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(m.secretKey), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(*Claims)
|
||||
if !ok || !token.Valid {
|
||||
return nil, errInvalidToken
|
||||
}
|
||||
|
||||
return &claims.TokenPayload, nil
|
||||
}
|
||||
|
||||
// Renew 换票逻辑:如果 token 过期但 Redis 中还存在,则生成新 token
|
||||
func (m *JwtManager) Renew(ctx context.Context, tokenString string) (string, error) {
|
||||
if m.redisCluster == nil {
|
||||
return "", errNoRedisClient
|
||||
}
|
||||
|
||||
// 检查 token 是否在 Redis 中(不检查过期时间)
|
||||
tokenKey := tokenCachePrefixToken + tokenString
|
||||
tokenData, err := m.redisCluster.Get(ctx, tokenKey).Result()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
return "", errTokenNotInCache
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
var payload TokenPayload
|
||||
err = json.Unmarshal([]byte(tokenData), &payload)
|
||||
if err != nil {
|
||||
return "", errInvalidToken
|
||||
}
|
||||
|
||||
// 删除旧 token 记录
|
||||
userKey := tokenCachePrefixUser + payload.UserId
|
||||
m.redisCluster.Del(ctx, tokenKey, userKey)
|
||||
|
||||
// 生成新 token
|
||||
return m.New(ctx, &payload)
|
||||
}
|
||||
|
||||
// extract payload from token without validating expiration (used for auto-renewal)
|
||||
func (m *JwtManager) Extract(ctx context.Context, tokenString string) (*TokenPayload, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(m.secretKey), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(*Claims)
|
||||
if !ok {
|
||||
return nil, errInvalidToken
|
||||
}
|
||||
|
||||
return &claims.TokenPayload, nil
|
||||
}
|
||||
|
||||
// check if token exists in Redis (i.e. is valid and not revoked)
|
||||
func (m *JwtManager) Exists(ctx context.Context, tokenString string) (bool, error) {
|
||||
if m.redisCluster == nil {
|
||||
return false, errNoRedisClient
|
||||
}
|
||||
|
||||
tokenKey := tokenCachePrefixToken + tokenString
|
||||
exists, err := m.redisCluster.Exists(ctx, tokenKey).Result()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return exists > 0, nil
|
||||
}
|
||||
|
||||
// extract payload from JWT claims
|
||||
func (m *JwtManager) ClaimsToPayload(claims *Claims) *TokenPayload {
|
||||
return &claims.TokenPayload
|
||||
}
|
||||
|
||||
// revoke token by deleting both user -> token and token -> payload keys from Redis
|
||||
func (m *JwtManager) Revoke(ctx context.Context, tokenString string) error {
|
||||
if m.redisCluster == nil {
|
||||
return errNoRedisClient
|
||||
}
|
||||
|
||||
payload, err := m.Extract(ctx, tokenString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userKey := tokenCachePrefixUser + payload.UserId
|
||||
tokenKey := tokenCachePrefixToken + tokenString
|
||||
|
||||
pipe := m.redisCluster.Pipeline()
|
||||
pipe.Del(ctx, userKey)
|
||||
pipe.Del(ctx, tokenKey)
|
||||
_, err = pipe.Exec(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *JwtManager) GetUserToken(ctx context.Context, userID string) (string, error) {
|
||||
if m.redisCluster == nil {
|
||||
return "", errNoRedisClient
|
||||
}
|
||||
|
||||
userKey := tokenCachePrefixUser + userID
|
||||
token, err := m.redisCluster.Get(ctx, userKey).Result()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
return "", fmt.Errorf("user %s has no token", userID)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
+375
-12
@@ -906,6 +906,334 @@ func (x *GetUserByUsernameResp) GetUsers() *Users {
|
||||
return nil
|
||||
}
|
||||
|
||||
type LoginReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Passwd string `protobuf:"bytes,2,opt,name=passwd,proto3" json:"passwd,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *LoginReq) Reset() {
|
||||
*x = LoginReq{}
|
||||
mi := &file_users_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *LoginReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LoginReq) ProtoMessage() {}
|
||||
|
||||
func (x *LoginReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_users_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 LoginReq.ProtoReflect.Descriptor instead.
|
||||
func (*LoginReq) Descriptor() ([]byte, []int) {
|
||||
return file_users_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *LoginReq) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LoginReq) GetPasswd() string {
|
||||
if x != nil {
|
||||
return x.Passwd
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type LoginResp struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *LoginResp) Reset() {
|
||||
*x = LoginResp{}
|
||||
mi := &file_users_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *LoginResp) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LoginResp) ProtoMessage() {}
|
||||
|
||||
func (x *LoginResp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_users_proto_msgTypes[14]
|
||||
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 LoginResp.ProtoReflect.Descriptor instead.
|
||||
func (*LoginResp) Descriptor() ([]byte, []int) {
|
||||
return file_users_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *LoginResp) GetToken() string {
|
||||
if x != nil {
|
||||
return x.Token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ValidateTokenReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` // JWT token
|
||||
UserId string `protobuf:"bytes,2,opt,name=userId,proto3" json:"userId,omitempty"` // 用户ID
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ValidateTokenReq) Reset() {
|
||||
*x = ValidateTokenReq{}
|
||||
mi := &file_users_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ValidateTokenReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ValidateTokenReq) ProtoMessage() {}
|
||||
|
||||
func (x *ValidateTokenReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_users_proto_msgTypes[15]
|
||||
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 ValidateTokenReq.ProtoReflect.Descriptor instead.
|
||||
func (*ValidateTokenReq) Descriptor() ([]byte, []int) {
|
||||
return file_users_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *ValidateTokenReq) GetToken() string {
|
||||
if x != nil {
|
||||
return x.Token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ValidateTokenReq) GetUserId() string {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ValidateTokenResp struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Valid bool `protobuf:"varint,1,opt,name=valid,proto3" json:"valid,omitempty"` // token 是否有效(不在黑名单中)
|
||||
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // 验证失败原因
|
||||
UserId string `protobuf:"bytes,3,opt,name=userId,proto3" json:"userId,omitempty"` // 用户ID
|
||||
RoleType int64 `protobuf:"varint,4,opt,name=roleType,proto3" json:"roleType,omitempty"` // 用户角色
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ValidateTokenResp) Reset() {
|
||||
*x = ValidateTokenResp{}
|
||||
mi := &file_users_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ValidateTokenResp) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ValidateTokenResp) ProtoMessage() {}
|
||||
|
||||
func (x *ValidateTokenResp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_users_proto_msgTypes[16]
|
||||
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 ValidateTokenResp.ProtoReflect.Descriptor instead.
|
||||
func (*ValidateTokenResp) Descriptor() ([]byte, []int) {
|
||||
return file_users_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *ValidateTokenResp) GetValid() bool {
|
||||
if x != nil {
|
||||
return x.Valid
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *ValidateTokenResp) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ValidateTokenResp) GetUserId() string {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ValidateTokenResp) GetRoleType() int64 {
|
||||
if x != nil {
|
||||
return x.RoleType
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type CheckPermissionReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
UserId string `protobuf:"bytes,1,opt,name=userId,proto3" json:"userId,omitempty"` // 用户ID
|
||||
Resource string `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` // 资源 ID
|
||||
Action string `protobuf:"bytes,3,opt,name=action,proto3" json:"action,omitempty"` // 操作类型: read/write/delete
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CheckPermissionReq) Reset() {
|
||||
*x = CheckPermissionReq{}
|
||||
mi := &file_users_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CheckPermissionReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckPermissionReq) ProtoMessage() {}
|
||||
|
||||
func (x *CheckPermissionReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_users_proto_msgTypes[17]
|
||||
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 CheckPermissionReq.ProtoReflect.Descriptor instead.
|
||||
func (*CheckPermissionReq) Descriptor() ([]byte, []int) {
|
||||
return file_users_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *CheckPermissionReq) GetUserId() string {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CheckPermissionReq) GetResource() string {
|
||||
if x != nil {
|
||||
return x.Resource
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CheckPermissionReq) GetAction() string {
|
||||
if x != nil {
|
||||
return x.Action
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CheckPermissionResp struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Allowed bool `protobuf:"varint,1,opt,name=allowed,proto3" json:"allowed,omitempty"` // 是否有权限
|
||||
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // 拒绝原因
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CheckPermissionResp) Reset() {
|
||||
*x = CheckPermissionResp{}
|
||||
mi := &file_users_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CheckPermissionResp) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckPermissionResp) ProtoMessage() {}
|
||||
|
||||
func (x *CheckPermissionResp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_users_proto_msgTypes[18]
|
||||
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 CheckPermissionResp.ProtoReflect.Descriptor instead.
|
||||
func (*CheckPermissionResp) Descriptor() ([]byte, []int) {
|
||||
return file_users_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *CheckPermissionResp) GetAllowed() bool {
|
||||
if x != nil {
|
||||
return x.Allowed
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *CheckPermissionResp) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_users_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_users_proto_rawDesc = "" +
|
||||
@@ -987,7 +1315,27 @@ const file_users_proto_rawDesc = "" +
|
||||
"\x14GetUserByUsernameReq\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\"8\n" +
|
||||
"\x15GetUserByUsernameResp\x12\x1f\n" +
|
||||
"\x05users\x18\x01 \x01(\v2\t.pb.UsersR\x05users2\xdf\x02\n" +
|
||||
"\x05users\x18\x01 \x01(\v2\t.pb.UsersR\x05users\">\n" +
|
||||
"\bLoginReq\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12\x16\n" +
|
||||
"\x06passwd\x18\x02 \x01(\tR\x06passwd\"!\n" +
|
||||
"\tLoginResp\x12\x14\n" +
|
||||
"\x05token\x18\x01 \x01(\tR\x05token\"@\n" +
|
||||
"\x10ValidateTokenReq\x12\x14\n" +
|
||||
"\x05token\x18\x01 \x01(\tR\x05token\x12\x16\n" +
|
||||
"\x06userId\x18\x02 \x01(\tR\x06userId\"w\n" +
|
||||
"\x11ValidateTokenResp\x12\x14\n" +
|
||||
"\x05valid\x18\x01 \x01(\bR\x05valid\x12\x18\n" +
|
||||
"\amessage\x18\x02 \x01(\tR\amessage\x12\x16\n" +
|
||||
"\x06userId\x18\x03 \x01(\tR\x06userId\x12\x1a\n" +
|
||||
"\broleType\x18\x04 \x01(\x03R\broleType\"`\n" +
|
||||
"\x12CheckPermissionReq\x12\x16\n" +
|
||||
"\x06userId\x18\x01 \x01(\tR\x06userId\x12\x1a\n" +
|
||||
"\bresource\x18\x02 \x01(\tR\bresource\x12\x16\n" +
|
||||
"\x06action\x18\x03 \x01(\tR\x06action\"I\n" +
|
||||
"\x13CheckPermissionResp\x12\x18\n" +
|
||||
"\aallowed\x18\x01 \x01(\bR\aallowed\x12\x18\n" +
|
||||
"\amessage\x18\x02 \x01(\tR\amessage2\x87\x04\n" +
|
||||
"\n" +
|
||||
"usercenter\x12-\n" +
|
||||
"\bAddUsers\x12\x0f.pb.AddUsersReq\x1a\x10.pb.AddUsersResp\x126\n" +
|
||||
@@ -995,7 +1343,10 @@ const file_users_proto_rawDesc = "" +
|
||||
"\bDelUsers\x12\x0f.pb.DelUsersReq\x1a\x10.pb.DelUsersResp\x129\n" +
|
||||
"\fGetUsersById\x12\x13.pb.GetUsersByIdReq\x1a\x14.pb.GetUsersByIdResp\x12H\n" +
|
||||
"\x11GetUserByUsername\x12\x18.pb.GetUserByUsernameReq\x1a\x19.pb.GetUserByUsernameResp\x126\n" +
|
||||
"\vSearchUsers\x12\x12.pb.SearchUsersReq\x1a\x13.pb.SearchUsersRespB\x06Z\x04./pbb\x06proto3"
|
||||
"\vSearchUsers\x12\x12.pb.SearchUsersReq\x1a\x13.pb.SearchUsersResp\x12$\n" +
|
||||
"\x05Login\x12\f.pb.LoginReq\x1a\r.pb.LoginResp\x12<\n" +
|
||||
"\rValidateToken\x12\x14.pb.ValidateTokenReq\x1a\x15.pb.ValidateTokenResp\x12B\n" +
|
||||
"\x0fCheckPermission\x12\x16.pb.CheckPermissionReq\x1a\x17.pb.CheckPermissionRespB\x06Z\x04./pbb\x06proto3"
|
||||
|
||||
var (
|
||||
file_users_proto_rawDescOnce sync.Once
|
||||
@@ -1009,7 +1360,7 @@ func file_users_proto_rawDescGZIP() []byte {
|
||||
return file_users_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_users_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||
var file_users_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
|
||||
var file_users_proto_goTypes = []any{
|
||||
(*Users)(nil), // 0: pb.Users
|
||||
(*AddUsersReq)(nil), // 1: pb.AddUsersReq
|
||||
@@ -1024,6 +1375,12 @@ var file_users_proto_goTypes = []any{
|
||||
(*SearchUsersResp)(nil), // 10: pb.SearchUsersResp
|
||||
(*GetUserByUsernameReq)(nil), // 11: pb.GetUserByUsernameReq
|
||||
(*GetUserByUsernameResp)(nil), // 12: pb.GetUserByUsernameResp
|
||||
(*LoginReq)(nil), // 13: pb.LoginReq
|
||||
(*LoginResp)(nil), // 14: pb.LoginResp
|
||||
(*ValidateTokenReq)(nil), // 15: pb.ValidateTokenReq
|
||||
(*ValidateTokenResp)(nil), // 16: pb.ValidateTokenResp
|
||||
(*CheckPermissionReq)(nil), // 17: pb.CheckPermissionReq
|
||||
(*CheckPermissionResp)(nil), // 18: pb.CheckPermissionResp
|
||||
}
|
||||
var file_users_proto_depIdxs = []int32{
|
||||
0, // 0: pb.GetUsersByIdResp.users:type_name -> pb.Users
|
||||
@@ -1035,14 +1392,20 @@ var file_users_proto_depIdxs = []int32{
|
||||
7, // 6: pb.usercenter.GetUsersById:input_type -> pb.GetUsersByIdReq
|
||||
11, // 7: pb.usercenter.GetUserByUsername:input_type -> pb.GetUserByUsernameReq
|
||||
9, // 8: pb.usercenter.SearchUsers:input_type -> pb.SearchUsersReq
|
||||
2, // 9: pb.usercenter.AddUsers:output_type -> pb.AddUsersResp
|
||||
4, // 10: pb.usercenter.UpdateUsers:output_type -> pb.UpdateUsersResp
|
||||
6, // 11: pb.usercenter.DelUsers:output_type -> pb.DelUsersResp
|
||||
8, // 12: pb.usercenter.GetUsersById:output_type -> pb.GetUsersByIdResp
|
||||
12, // 13: pb.usercenter.GetUserByUsername:output_type -> pb.GetUserByUsernameResp
|
||||
10, // 14: pb.usercenter.SearchUsers:output_type -> pb.SearchUsersResp
|
||||
9, // [9:15] is the sub-list for method output_type
|
||||
3, // [3:9] is the sub-list for method input_type
|
||||
13, // 9: pb.usercenter.Login:input_type -> pb.LoginReq
|
||||
15, // 10: pb.usercenter.ValidateToken:input_type -> pb.ValidateTokenReq
|
||||
17, // 11: pb.usercenter.CheckPermission:input_type -> pb.CheckPermissionReq
|
||||
2, // 12: pb.usercenter.AddUsers:output_type -> pb.AddUsersResp
|
||||
4, // 13: pb.usercenter.UpdateUsers:output_type -> pb.UpdateUsersResp
|
||||
6, // 14: pb.usercenter.DelUsers:output_type -> pb.DelUsersResp
|
||||
8, // 15: pb.usercenter.GetUsersById:output_type -> pb.GetUsersByIdResp
|
||||
12, // 16: pb.usercenter.GetUserByUsername:output_type -> pb.GetUserByUsernameResp
|
||||
10, // 17: pb.usercenter.SearchUsers:output_type -> pb.SearchUsersResp
|
||||
14, // 18: pb.usercenter.Login:output_type -> pb.LoginResp
|
||||
16, // 19: pb.usercenter.ValidateToken:output_type -> pb.ValidateTokenResp
|
||||
18, // 20: pb.usercenter.CheckPermission:output_type -> pb.CheckPermissionResp
|
||||
12, // [12:21] is the sub-list for method output_type
|
||||
3, // [3:12] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
@@ -1059,7 +1422,7 @@ func file_users_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_users_proto_rawDesc), len(file_users_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 13,
|
||||
NumMessages: 19,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -25,6 +25,9 @@ const (
|
||||
Usercenter_GetUsersById_FullMethodName = "/pb.usercenter/GetUsersById"
|
||||
Usercenter_GetUserByUsername_FullMethodName = "/pb.usercenter/GetUserByUsername"
|
||||
Usercenter_SearchUsers_FullMethodName = "/pb.usercenter/SearchUsers"
|
||||
Usercenter_Login_FullMethodName = "/pb.usercenter/Login"
|
||||
Usercenter_ValidateToken_FullMethodName = "/pb.usercenter/ValidateToken"
|
||||
Usercenter_CheckPermission_FullMethodName = "/pb.usercenter/CheckPermission"
|
||||
)
|
||||
|
||||
// UsercenterClient is the client API for Usercenter service.
|
||||
@@ -38,6 +41,9 @@ type UsercenterClient interface {
|
||||
GetUsersById(ctx context.Context, in *GetUsersByIdReq, opts ...grpc.CallOption) (*GetUsersByIdResp, error)
|
||||
GetUserByUsername(ctx context.Context, in *GetUserByUsernameReq, opts ...grpc.CallOption) (*GetUserByUsernameResp, error)
|
||||
SearchUsers(ctx context.Context, in *SearchUsersReq, opts ...grpc.CallOption) (*SearchUsersResp, error)
|
||||
Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginResp, error)
|
||||
ValidateToken(ctx context.Context, in *ValidateTokenReq, opts ...grpc.CallOption) (*ValidateTokenResp, error)
|
||||
CheckPermission(ctx context.Context, in *CheckPermissionReq, opts ...grpc.CallOption) (*CheckPermissionResp, error)
|
||||
}
|
||||
|
||||
type usercenterClient struct {
|
||||
@@ -108,6 +114,36 @@ func (c *usercenterClient) SearchUsers(ctx context.Context, in *SearchUsersReq,
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *usercenterClient) Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(LoginResp)
|
||||
err := c.cc.Invoke(ctx, Usercenter_Login_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *usercenterClient) ValidateToken(ctx context.Context, in *ValidateTokenReq, opts ...grpc.CallOption) (*ValidateTokenResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ValidateTokenResp)
|
||||
err := c.cc.Invoke(ctx, Usercenter_ValidateToken_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *usercenterClient) CheckPermission(ctx context.Context, in *CheckPermissionReq, opts ...grpc.CallOption) (*CheckPermissionResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CheckPermissionResp)
|
||||
err := c.cc.Invoke(ctx, Usercenter_CheckPermission_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UsercenterServer is the server API for Usercenter service.
|
||||
// All implementations must embed UnimplementedUsercenterServer
|
||||
// for forward compatibility.
|
||||
@@ -119,6 +155,9 @@ type UsercenterServer interface {
|
||||
GetUsersById(context.Context, *GetUsersByIdReq) (*GetUsersByIdResp, error)
|
||||
GetUserByUsername(context.Context, *GetUserByUsernameReq) (*GetUserByUsernameResp, error)
|
||||
SearchUsers(context.Context, *SearchUsersReq) (*SearchUsersResp, error)
|
||||
Login(context.Context, *LoginReq) (*LoginResp, error)
|
||||
ValidateToken(context.Context, *ValidateTokenReq) (*ValidateTokenResp, error)
|
||||
CheckPermission(context.Context, *CheckPermissionReq) (*CheckPermissionResp, error)
|
||||
mustEmbedUnimplementedUsercenterServer()
|
||||
}
|
||||
|
||||
@@ -147,6 +186,15 @@ func (UnimplementedUsercenterServer) GetUserByUsername(context.Context, *GetUser
|
||||
func (UnimplementedUsercenterServer) SearchUsers(context.Context, *SearchUsersReq) (*SearchUsersResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SearchUsers not implemented")
|
||||
}
|
||||
func (UnimplementedUsercenterServer) Login(context.Context, *LoginReq) (*LoginResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
|
||||
}
|
||||
func (UnimplementedUsercenterServer) ValidateToken(context.Context, *ValidateTokenReq) (*ValidateTokenResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ValidateToken not implemented")
|
||||
}
|
||||
func (UnimplementedUsercenterServer) CheckPermission(context.Context, *CheckPermissionReq) (*CheckPermissionResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CheckPermission not implemented")
|
||||
}
|
||||
func (UnimplementedUsercenterServer) mustEmbedUnimplementedUsercenterServer() {}
|
||||
func (UnimplementedUsercenterServer) testEmbeddedByValue() {}
|
||||
|
||||
@@ -276,6 +324,60 @@ func _Usercenter_SearchUsers_Handler(srv interface{}, ctx context.Context, dec f
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Usercenter_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(LoginReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UsercenterServer).Login(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Usercenter_Login_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UsercenterServer).Login(ctx, req.(*LoginReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Usercenter_ValidateToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ValidateTokenReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UsercenterServer).ValidateToken(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Usercenter_ValidateToken_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UsercenterServer).ValidateToken(ctx, req.(*ValidateTokenReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Usercenter_CheckPermission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CheckPermissionReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UsercenterServer).CheckPermission(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Usercenter_CheckPermission_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UsercenterServer).CheckPermission(ctx, req.(*CheckPermissionReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Usercenter_ServiceDesc is the grpc.ServiceDesc for Usercenter service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -307,6 +409,18 @@ var Usercenter_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "SearchUsers",
|
||||
Handler: _Usercenter_SearchUsers_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Login",
|
||||
Handler: _Usercenter_Login_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ValidateToken",
|
||||
Handler: _Usercenter_ValidateToken_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CheckPermission",
|
||||
Handler: _Usercenter_CheckPermission_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "users.proto",
|
||||
|
||||
@@ -16,17 +16,23 @@ import (
|
||||
type (
|
||||
AddUsersReq = pb.AddUsersReq
|
||||
AddUsersResp = pb.AddUsersResp
|
||||
CheckPermissionReq = pb.CheckPermissionReq
|
||||
CheckPermissionResp = pb.CheckPermissionResp
|
||||
DelUsersReq = pb.DelUsersReq
|
||||
DelUsersResp = pb.DelUsersResp
|
||||
GetUserByUsernameReq = pb.GetUserByUsernameReq
|
||||
GetUserByUsernameResp = pb.GetUserByUsernameResp
|
||||
GetUsersByIdReq = pb.GetUsersByIdReq
|
||||
GetUsersByIdResp = pb.GetUsersByIdResp
|
||||
LoginReq = pb.LoginReq
|
||||
LoginResp = pb.LoginResp
|
||||
SearchUsersReq = pb.SearchUsersReq
|
||||
SearchUsersResp = pb.SearchUsersResp
|
||||
UpdateUsersReq = pb.UpdateUsersReq
|
||||
UpdateUsersResp = pb.UpdateUsersResp
|
||||
Users = pb.Users
|
||||
ValidateTokenReq = pb.ValidateTokenReq
|
||||
ValidateTokenResp = pb.ValidateTokenResp
|
||||
|
||||
Usercenter interface {
|
||||
// -----------------------users-----------------------
|
||||
@@ -36,6 +42,9 @@ type (
|
||||
GetUsersById(ctx context.Context, in *GetUsersByIdReq, opts ...grpc.CallOption) (*GetUsersByIdResp, error)
|
||||
GetUserByUsername(ctx context.Context, in *GetUserByUsernameReq, opts ...grpc.CallOption) (*GetUserByUsernameResp, error)
|
||||
SearchUsers(ctx context.Context, in *SearchUsersReq, opts ...grpc.CallOption) (*SearchUsersResp, error)
|
||||
Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginResp, error)
|
||||
ValidateToken(ctx context.Context, in *ValidateTokenReq, opts ...grpc.CallOption) (*ValidateTokenResp, error)
|
||||
CheckPermission(ctx context.Context, in *CheckPermissionReq, opts ...grpc.CallOption) (*CheckPermissionResp, error)
|
||||
}
|
||||
|
||||
defaultUsercenter struct {
|
||||
@@ -79,3 +88,18 @@ func (m *defaultUsercenter) SearchUsers(ctx context.Context, in *SearchUsersReq,
|
||||
client := pb.NewUsercenterClient(m.cli.Conn())
|
||||
return client.SearchUsers(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultUsercenter) Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginResp, error) {
|
||||
client := pb.NewUsercenterClient(m.cli.Conn())
|
||||
return client.Login(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultUsercenter) ValidateToken(ctx context.Context, in *ValidateTokenReq, opts ...grpc.CallOption) (*ValidateTokenResp, error) {
|
||||
client := pb.NewUsercenterClient(m.cli.Conn())
|
||||
return client.ValidateToken(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultUsercenter) CheckPermission(ctx context.Context, in *CheckPermissionReq, opts ...grpc.CallOption) (*CheckPermissionResp, error) {
|
||||
client := pb.NewUsercenterClient(m.cli.Conn())
|
||||
return client.CheckPermission(ctx, in, opts...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user