permserver.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package server
  2. import (
  3. "context"
  4. "crypto/subtle"
  5. "fmt"
  6. "net"
  7. "time"
  8. "perms-system-server/internal/consts"
  9. authHelper "perms-system-server/internal/logic/auth"
  10. pub "perms-system-server/internal/logic/pub"
  11. "perms-system-server/internal/middleware"
  12. permModel "perms-system-server/internal/model/perm"
  13. "perms-system-server/internal/svc"
  14. "perms-system-server/pb"
  15. "github.com/golang-jwt/jwt/v4"
  16. "github.com/zeromicro/go-zero/core/limit"
  17. "github.com/zeromicro/go-zero/core/stores/sqlx"
  18. "google.golang.org/grpc/codes"
  19. "google.golang.org/grpc/peer"
  20. "google.golang.org/grpc/status"
  21. )
  22. type PermServer struct {
  23. svcCtx *svc.ServiceContext
  24. pb.UnimplementedPermServiceServer
  25. }
  26. func NewPermServer(svcCtx *svc.ServiceContext) *PermServer {
  27. return &PermServer{svcCtx: svcCtx}
  28. }
  29. func (s *PermServer) SyncPermissions(ctx context.Context, req *pb.SyncPermissionsReq) (*pb.SyncPermissionsResp, error) {
  30. product, err := s.svcCtx.SysProductModel.FindOneByAppKey(ctx, req.AppKey)
  31. if err != nil {
  32. return nil, status.Error(codes.Unauthenticated, "无效的appKey")
  33. }
  34. if subtle.ConstantTimeCompare([]byte(product.AppSecret), []byte(req.AppSecret)) != 1 {
  35. return nil, status.Error(codes.Unauthenticated, "appSecret验证失败")
  36. }
  37. if product.Status != consts.StatusEnabled {
  38. return nil, status.Error(codes.PermissionDenied, "产品已被禁用")
  39. }
  40. existingMap, err := s.svcCtx.SysPermModel.FindMapByProductCode(ctx, product.Code)
  41. if err != nil {
  42. return nil, status.Error(codes.Internal, "查询权限数据失败")
  43. }
  44. now := time.Now().Unix()
  45. var added, updated int64
  46. codeList := make([]string, 0, len(req.Perms))
  47. var toInsert []*permModel.SysPerm
  48. var toUpdate []*permModel.SysPerm
  49. seen := make(map[string]bool, len(req.Perms))
  50. for _, item := range req.Perms {
  51. if seen[item.Code] {
  52. continue
  53. }
  54. seen[item.Code] = true
  55. codeList = append(codeList, item.Code)
  56. existing, ok := existingMap[item.Code]
  57. if !ok {
  58. toInsert = append(toInsert, &permModel.SysPerm{
  59. ProductCode: product.Code,
  60. Name: item.Name,
  61. Code: item.Code,
  62. Remark: item.Remark,
  63. Status: consts.StatusEnabled,
  64. CreateTime: now,
  65. UpdateTime: now,
  66. })
  67. added++
  68. continue
  69. }
  70. if existing.Name != item.Name || existing.Remark != item.Remark || existing.Status != consts.StatusEnabled {
  71. existing.Name = item.Name
  72. existing.Remark = item.Remark
  73. existing.Status = consts.StatusEnabled
  74. existing.UpdateTime = now
  75. toUpdate = append(toUpdate, existing)
  76. updated++
  77. }
  78. }
  79. var disabled int64
  80. if txErr := s.svcCtx.SysPermModel.TransactCtx(ctx, func(txCtx context.Context, session sqlx.Session) error {
  81. if len(toInsert) > 0 {
  82. if err := s.svcCtx.SysPermModel.BatchInsertWithTx(txCtx, session, toInsert); err != nil {
  83. return err
  84. }
  85. }
  86. if len(toUpdate) > 0 {
  87. if err := s.svcCtx.SysPermModel.BatchUpdateWithTx(txCtx, session, toUpdate); err != nil {
  88. return err
  89. }
  90. }
  91. var err error
  92. disabled, err = s.svcCtx.SysPermModel.DisableNotInCodesWithTx(txCtx, session, product.Code, codeList, now)
  93. return err
  94. }); txErr != nil {
  95. return nil, status.Error(codes.Internal, "同步权限事务失败")
  96. }
  97. if added > 0 || updated > 0 || disabled > 0 {
  98. s.svcCtx.UserDetailsLoader.CleanByProduct(ctx, product.Code)
  99. }
  100. return &pb.SyncPermissionsResp{Added: added, Updated: updated, Disabled: disabled}, nil
  101. }
  102. func (s *PermServer) Login(ctx context.Context, req *pb.LoginReq) (*pb.LoginResp, error) {
  103. if s.svcCtx.GrpcLoginLimiter != nil {
  104. p, ok := peer.FromContext(ctx)
  105. if ok {
  106. ip, _, _ := net.SplitHostPort(p.Addr.String())
  107. if ip == "" {
  108. ip = p.Addr.String()
  109. }
  110. code, _ := s.svcCtx.GrpcLoginLimiter.Take(fmt.Sprintf("grpc:login:%s", ip))
  111. if code == limit.OverQuota {
  112. return nil, status.Error(codes.ResourceExhausted, "请求过于频繁,请稍后再试")
  113. }
  114. }
  115. }
  116. if req.ProductCode == "" {
  117. return nil, status.Error(codes.InvalidArgument, "productCode不能为空")
  118. }
  119. result, err := pub.ValidateProductLogin(ctx, s.svcCtx, req.Username, req.Password, req.ProductCode)
  120. if err != nil {
  121. if le, ok := err.(*pub.LoginError); ok {
  122. switch le.Code {
  123. case 400:
  124. return nil, status.Error(codes.InvalidArgument, le.Message)
  125. case 401:
  126. return nil, status.Error(codes.Unauthenticated, le.Message)
  127. case 403:
  128. return nil, status.Error(codes.PermissionDenied, le.Message)
  129. }
  130. }
  131. return nil, status.Error(codes.Internal, "登录失败")
  132. }
  133. ud := result.UserDetails
  134. return &pb.LoginResp{
  135. AccessToken: result.AccessToken,
  136. RefreshToken: result.RefreshToken,
  137. Expires: time.Now().Unix() + s.svcCtx.Config.Auth.AccessExpire,
  138. UserId: ud.UserId,
  139. Username: ud.Username,
  140. MemberType: ud.MemberType,
  141. Perms: ud.Perms,
  142. }, nil
  143. }
  144. func (s *PermServer) RefreshToken(ctx context.Context, req *pb.RefreshTokenReq) (*pb.RefreshTokenResp, error) {
  145. claims, err := authHelper.ParseRefreshToken(req.RefreshToken, s.svcCtx.Config.Auth.RefreshSecret)
  146. if err != nil {
  147. return nil, status.Error(codes.Unauthenticated, "refreshToken无效或已过期")
  148. }
  149. productCode := claims.ProductCode
  150. if req.ProductCode != "" && req.ProductCode != productCode {
  151. return nil, status.Error(codes.InvalidArgument, "刷新令牌不允许切换产品")
  152. }
  153. ud := s.svcCtx.UserDetailsLoader.Load(ctx, claims.UserId, productCode)
  154. if ud.Status != consts.StatusEnabled {
  155. return nil, status.Error(codes.PermissionDenied, "账号已被冻结")
  156. }
  157. if productCode != "" && !ud.IsSuperAdmin && ud.MemberType == "" {
  158. return nil, status.Error(codes.PermissionDenied, "您已不是该产品的成员")
  159. }
  160. if claims.TokenVersion != ud.TokenVersion {
  161. return nil, status.Error(codes.Unauthenticated, "登录状态已失效,请重新登录")
  162. }
  163. accessToken, err := authHelper.GenerateAccessToken(
  164. s.svcCtx.Config.Auth.AccessSecret, s.svcCtx.Config.Auth.AccessExpire,
  165. ud.UserId, ud.Username, ud.ProductCode, ud.MemberType, ud.Perms, ud.TokenVersion,
  166. )
  167. if err != nil {
  168. return nil, status.Error(codes.Internal, "生成token失败")
  169. }
  170. return &pb.RefreshTokenResp{
  171. AccessToken: accessToken,
  172. RefreshToken: req.RefreshToken,
  173. Expires: time.Now().Unix() + s.svcCtx.Config.Auth.AccessExpire,
  174. }, nil
  175. }
  176. func (s *PermServer) VerifyToken(ctx context.Context, req *pb.VerifyTokenReq) (*pb.VerifyTokenResp, error) {
  177. token, err := jwt.ParseWithClaims(req.AccessToken, &middleware.Claims{}, func(token *jwt.Token) (interface{}, error) {
  178. return []byte(s.svcCtx.Config.Auth.AccessSecret), nil
  179. })
  180. if err != nil || !token.Valid {
  181. return &pb.VerifyTokenResp{Valid: false}, nil
  182. }
  183. claims, ok := token.Claims.(*middleware.Claims)
  184. if !ok || claims.TokenType != consts.TokenTypeAccess {
  185. return &pb.VerifyTokenResp{Valid: false}, nil
  186. }
  187. ud := s.svcCtx.UserDetailsLoader.Load(ctx, claims.UserId, claims.ProductCode)
  188. if ud.Status != consts.StatusEnabled {
  189. return &pb.VerifyTokenResp{Valid: false}, nil
  190. }
  191. if claims.ProductCode != "" && !ud.IsSuperAdmin && ud.MemberType == "" {
  192. return &pb.VerifyTokenResp{Valid: false}, nil
  193. }
  194. if claims.TokenVersion != ud.TokenVersion {
  195. return &pb.VerifyTokenResp{Valid: false}, nil
  196. }
  197. return &pb.VerifyTokenResp{
  198. Valid: true,
  199. UserId: ud.UserId,
  200. Username: ud.Username,
  201. MemberType: ud.MemberType,
  202. Perms: ud.Perms,
  203. }, nil
  204. }
  205. func (s *PermServer) GetUserPerms(ctx context.Context, req *pb.GetUserPermsReq) (*pb.GetUserPermsResp, error) {
  206. ud := s.svcCtx.UserDetailsLoader.Load(ctx, req.UserId, req.ProductCode)
  207. if ud.Username == "" {
  208. return nil, status.Error(codes.NotFound, "用户不存在")
  209. }
  210. return &pb.GetUserPermsResp{
  211. MemberType: ud.MemberType,
  212. Perms: ud.Perms,
  213. }, nil
  214. }