鉴权模块封装

dev
truthhun 2 years ago
parent 793ca6c767
commit 9839aaf127

@ -18,22 +18,21 @@ message User {
google.protobuf.Timestamp updated_at = 21 [ (gogoproto.stdtime) = true ];
int64 id = 1;
string username = 2;
string nickname = 3;
string mobile = 4;
string email = 5;
string address = 6;
string signature = 7;
string last_login_ip = 8;
string register_ip = 9;
int32 doc_count = 10;
int32 follow_count = 11;
int32 fans_count = 12;
int32 favorite_count = 13;
int32 comment_count = 14;
int32 status = 15;
string avatar = 16;
string identity = 17;
string realname = 18;
string mobile = 3;
string email = 4;
string address = 5;
string signature = 6;
string last_login_ip = 7;
string register_ip = 8;
int32 doc_count = 9;
int32 follow_count = 10;
int32 fans_count = 11;
int32 favorite_count = 12;
int32 comment_count = 13;
int32 status = 14;
string avatar = 15;
string identity = 16;
string realname = 17;
}
message RegisterAndLoginRequest {

@ -0,0 +1,99 @@
package biz
import (
"context"
"moredoc/model"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type AuthService struct {
dbModel *model.DBModel
logger *zap.Logger
}
func NewAuthService(dbModel *model.DBModel, logger *zap.Logger) (service *UserAPIService) {
return &UserAPIService{dbModel: dbModel, logger: logger.Named("AuthService")}
}
type ContextKey string
func (ck ContextKey) String() string {
return string(ck)
}
const (
CtxKeyUserClaims ContextKey = "user"
)
const (
messageInvalidToken = "您的登录令牌已过期,请重新登录"
)
type ServiceAuthFuncOverride interface {
AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error)
}
func (s *AuthService) AuthUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
newCtx, err := s.AuthGRPC(ctx, info)
if err != nil {
return nil, err
}
return handler(newCtx, req)
}
}
// AuthGRPC 验证 gRPC 请求
// 1. 从权限表中查询API如果存在该API则表示该API需要权限才能访问如果不存在则跳过
// 2. 如果用户携带有token则根据token判断是否有效如果有效则获取用户信息放到ctx否则跳过
func (s *AuthService) AuthGRPC(ctx context.Context, info *grpc.UnaryServerInfo) (context.Context, error) {
token, err := grpc_auth.AuthFromMD(ctx, "bearer")
if err != nil {
// 可忽略错误
return ctx, err
}
claims, err := s.dbModel.CheckUserJWTToken(token)
// token存在但是不正确或者已过期这时需要返回错误前端清除存储的错误登录信息
if err != nil || claims == nil || claims.ExpiresAt < time.Now().Unix() || s.dbModel.IsInvalidToken(claims.UUID) {
return ctx, status.Error(codes.Unauthenticated, messageInvalidToken)
}
newCtx := context.WithValue(ctx, CtxKeyUserClaims, claims)
return newCtx, nil
}
// AuthGin 验证 HTTP 请求
func (s *AuthService) AuthGin() gin.HandlerFunc {
return func(ctx *gin.Context) {
auth := ctx.Request.Header.Get("authorization")
bearer := strings.Split(auth, " ")
if auth == "" || len(bearer) != 2 {
ctx.Next()
return
}
token := bearer[1]
claims, err := s.dbModel.CheckUserJWTToken(token)
if err != nil || claims == nil || claims.ExpiresAt < time.Now().Unix() || s.dbModel.IsInvalidToken(claims.UUID) {
ctx.JSON(http.StatusUnauthorized, status.Error(codes.Unauthenticated, messageInvalidToken))
ctx.Abort()
return
}
ctx.Set(CtxKeyUserClaims.String(), claims)
ctx.Next()
}
}

@ -5,6 +5,7 @@ import (
"errors"
"moredoc/conf"
"strings"
"sync"
"go.uber.org/zap"
"gorm.io/driver/mysql"
@ -33,6 +34,8 @@ type DBModel struct {
logger *zap.Logger
tableFields map[string][]string
tableFieldsMap map[string]map[string]struct{}
validToken sync.Map // map[tokenUUID]struct{} 有效的token uuid
invalidToken sync.Map // map[tokenUUID]struct{} 存在未过期但无效token比如读者退出登录后的token
}
func NewDBModel(cfg *conf.Database, lg *zap.Logger) (m *DBModel, err error) {

@ -0,0 +1,55 @@
package model
import (
"time"
"go.uber.org/zap"
)
type Logout struct {
Id int64 `gorm:"primaryKey;autoIncrement"`
UserId int64
UUID string `gorm:"column:uuid;type:varchar(36);size:36;not null;uniqueIndex;comment:jwt的uuid"`
ExpiredAt int64 `gorm:"column:expired_at;type:bigint;not null;comment:过期时间,超过这个时间之后,可以从当前数据表中删除;index"`
CreatedAt time.Time
}
func (Logout) TableName() string {
return tablePrefix + "logout"
}
// SetLogout 设置退出登录
func (m *DBModel) Logout(userId int64, uuid string, expiredAt int64) {
// 将token加入到退出登录表中
m.invalidToken.Store(uuid, struct{}{})
m.validToken.Delete(uuid)
logout := &Logout{
UserId: userId,
UUID: uuid,
ExpiredAt: expiredAt,
}
if err := m.db.Create(logout).Error; err != nil {
m.logger.Error("SetLogout", zap.Error(err))
}
}
// IsInvalidToken 根据token对应的uuid判断token是否有效
func (m *DBModel) IsInvalidToken(uuid string) bool {
if _, ok := m.invalidToken.Load(uuid); ok {
return ok
}
if _, ok := m.validToken.Load(uuid); ok {
return !ok
}
logout := &Logout{}
m.db.Select("id").Where("uuid = ?", uuid).First(logout)
if logout.Id > 0 {
m.invalidToken.Store(uuid, struct{}{})
m.validToken.Delete(uuid)
return true
}
m.validToken.Store(uuid, struct{}{})
return false
}

@ -16,7 +16,6 @@ type User struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:用户 id;"`
Username string `form:"username" json:"username,omitempty" gorm:"column:username;type:varchar(64);size:64;index:username,unique;comment:用户名;"`
Password string `form:"password" json:"password,omitempty" gorm:"column:password;type:varchar(128);size:128;comment:密码;"`
Nickname string `form:"nickname" json:"nickname,omitempty" gorm:"column:nickname;type:varchar(64);size:64;comment:用户昵称;"`
Mobile string `form:"mobile" json:"mobile,omitempty" gorm:"column:mobile;type:varchar(20);size:20;index:mobile;comment:手机号;"`
Email string `form:"email" json:"email,omitempty" gorm:"column:email;type:varchar(64);size:64;index:email;comment:联系邮箱;"`
Address string `form:"address" json:"address,omitempty" gorm:"column:address;type:varchar(255);size:255;comment:联系地址;"`

@ -1,10 +1,12 @@
<template>
<div>{{ $route }}</div>
<div>{{ $route.name }}</div>
</template>
<script>
export default {
layout: 'admin',
created() {},
created() {
this.$router.push({ name: 'admin-dashboard' })
},
}
</script>

Loading…
Cancel
Save