后台管理系统鉴权处理
一、JWT
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
db := c.MustGet(g.CTX_DB).(*gorm.DB)
url, method := c.FullPath()[4:], c.Request.Method
resource, err := model.GetResource(db, url, method)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
fmt.Println(err)
c.Set("skip_check", true)
c.Next()
c.Set("skip_check", false)
return
}
handle.ReturnError(c, g.ErrDbOp, err)
return
}
if resource.Anonymous {
slog.Debug(fmt.Sprintf("[middleware-JWTAuth] resource: %s %s is anonymous, skip jwt auth!", url, method))
c.Set("skip_check", true)
c.Next()
c.Set("skip_check", false)
return
}
authorization := c.Request.Header.Get("Authorization")
if authorization == "" {
handle.ReturnError(c, g.ErrTokenNotExist, nil)
return
}
parts := strings.Split(authorization, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
handle.ReturnError(c, g.ErrTokenType, nil)
return
}
claims, err := jwt.ParseToken(g.Conf.JWT.Secret, parts[1])
if err != nil {
handle.ReturnError(c, g.ErrTokenWrong, err)
return
}
if time.Now().Unix() > claims.ExpiresAt.Unix() {
handle.ReturnError(c, g.ErrTokenRuntime, nil)
return
}
user, err := model.GetUserAuthInfoById(db, claims.UserId)
if err != nil {
handle.ReturnError(c, g.ErrUserNotExist, err)
return
}
session := sessions.Default(c)
session.Set(g.CTX_USER_AUTH, claims.UserId)
session.Save()
c.Set(g.CTX_USER_AUTH, user)
}
}
二、资源访问权限验证
func PermissionCheck() gin.HandlerFunc {
return func(c *gin.Context) {
if c.GetBool("skip_check") {
c.Next()
return
}
db := c.MustGet(g.CTX_DB).(*gorm.DB)
auth, err := handle.CurrentUserAuth(c)
if err != nil {
handle.ReturnError(c, g.ErrUserNotExist, err)
return
}
if auth.IsSuper {
c.Next()
return
}
url := c.FullPath()[4:]
method := c.Request.Method
for _, role := range auth.Roles {
pass, err := model.CheckRoleAuth(db, role.ID, url, method)
if err != nil {
handle.ReturnError(c, g.ErrDbOp, err)
return
}
if !pass {
handle.ReturnError(c, g.ErrPermission, nil)
return
}
}
c.Next()
}
}
三、记录日志
func OperationLog() gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Method != "GET" && !strings.Contains(c.Request.RequestURI, "upload") {
blw := &CustomResponseWriter{
body: bytes.NewBufferString(""),
ResponseWriter: c.Writer,
}
c.Writer = blw
auth, _ := handle.CurrentUserAuth(c)
body, _ := io.ReadAll(c.Request.Body)
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
ipAddress := utils.IP.GetIpAddress(c)
ipSource := utils.IP.GetIpSource(ipAddress)
moduleName := getOptResource(c.HandlerName())
operationLog := model.OperationLog{
OptModule: moduleName,
OptType: GetOptString(c.Request.Method),
OptUrl: c.Request.RequestURI,
OptMethod: c.HandlerName(),
OptDesc: GetOptString(c.Request.Method) + moduleName,
RequestParam: string(body),
RequestMethod: c.Request.Method,
UserId: auth.UserInfoId,
Nickname: auth.UserInfo.Nickname,
IpAddress: ipAddress,
IpSource: ipSource,
}
c.Next()
operationLog.ResponseData = blw.body.String()
db := c.MustGet(g2.CTX_DB).(*gorm.DB)
if err := db.Create(&operationLog).Error; err != nil {
slog.Error("操作日志记录失败: ", err)
handle.ReturnError(c, g2.ErrDbOp, err)
return
}
} else {
c.Next()
}
}
}
四、实时监听用户
func ListenOnline() gin.HandlerFunc {
return func(c *gin.Context) {
ctx := context.Background()
rdb := c.MustGet(g2.CTX_RDB).(*redis.Client)
auth, err := handle.CurrentUserAuth(c)
if err != nil {
handle.ReturnError(c, g2.ErrUserAuth, err)
return
}
onlineKey := g2.ONLINE_USER + strconv.Itoa(auth.ID)
offlineKey := g2.OFFLINE_USER + strconv.Itoa(auth.ID)
if rdb.Exists(ctx, offlineKey).Val() == 1 {
fmt.Println("用户被强制下线")
handle.ReturnError(c, g2.ErrForceOffline, nil)
c.Abort()
return
}
rdb.Set(ctx, onlineKey, auth, 10*time.Minute)
c.Next()
}
}