登录功能完成

This commit is contained in:
2022-10-04 00:36:01 +08:00
parent 26278707bb
commit 968eab9b06
31 changed files with 1022 additions and 36 deletions

View File

@@ -0,0 +1,23 @@
package handler
import (
"github.com/gin-gonic/gin"
"mc-client-updater-server/internal/service"
"mc-client-updater-server/pkg/param"
"mc-client-updater-server/pkg/result"
)
func HandleLogin(c *gin.Context) {
srv := service.NewUserService(c)
res := result.NewResult(c)
loginParam := param.LoginParam{}
err := c.ShouldBindJSON(&loginParam)
if err != nil {
res.BadRequest()
return
}
srv.Login(loginParam.Username, loginParam.Password)
}

View File

@@ -0,0 +1,39 @@
package middleware
import (
"github.com/gin-gonic/gin"
"mc-client-updater-server/internal/service"
"mc-client-updater-server/pkg/result"
"strings"
)
func AdminRequired(c *gin.Context) {
res := result.NewResult(c)
authorization := c.GetHeader("Authorization")
if authorization == "" {
res.Unauthorized()
return
}
split := strings.Split(authorization, " ")
if len(split) <= 1 || (len(split) >= 2 && split[0] != "Bearer") {
res.BadRequest()
return
}
tokenSrv := service.NewTokenService(c)
token := split[1]
tokenRow, ok := tokenSrv.VerifyToken(token)
// 若!ok则返回值已被service处理无需再次返回
if !ok {
return
}
userSrv := service.NewUserService(c)
hasRole := userSrv.JudgeRoleByToken("ROLE_admin", tokenRow)
if !hasRole {
res.NoPermission()
return
}
c.Next()
}

View File

@@ -0,0 +1,11 @@
package middleware
import (
"github.com/gin-gonic/gin"
"mc-client-updater-server/pkg/log"
)
func GrantRequired(c *gin.Context) {
instName := c.Param("name")
log.Logger.Info(instName)
}

View File

@@ -0,0 +1,43 @@
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"mc-client-updater-server/pkg/log"
"time"
)
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
startTime := time.Now()
c.Next() // 调用该请求的剩余处理程序
stopTime := time.Since(startTime)
spendTime := fmt.Sprintf("%.3f ms", float64(stopTime.Nanoseconds())/1000000)
statusCode := c.Writer.Status()
dataSize := c.Writer.Size()
if dataSize < 0 {
dataSize = 0
}
method := c.Request.Method
url := c.Request.RequestURI
Log := log.Logger.WithFields(logrus.Fields{
"spendTime": spendTime,
"path": url,
"method": method,
"status": statusCode,
})
if len(c.Errors) > 0 { // 内部错误
Log.Error(c.Errors.ByType(gin.ErrorTypePrivate))
}
if statusCode == 500 || (statusCode >= 5000 && statusCode < 6000) {
Log.Error()
} else if statusCode >= 400 && statusCode < 500 {
Log.Warn()
} else {
Log.Info()
}
}
}

View File

@@ -1,45 +1,46 @@
package v1
import "github.com/gin-gonic/gin"
import (
"github.com/gin-gonic/gin"
"mc-client-updater-server/internal/api/v1/handler"
"mc-client-updater-server/internal/api/v1/middleware"
"net/http"
)
func NewRouter() *gin.Engine {
r := gin.Default()
r := gin.New()
r.Use()
r.Use(middleware.LoggerMiddleware())
r.Use(gin.Recovery())
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello Minecraft-Client-Updater service!")
})
v1 := r.Group("/api/v1")
/**
GET /status
*/
v1.GET("/status")
v1.POST("/login", handler.HandleLogin)
/**
GROUP /instance/:name
Auth required
ROLE == ROLE_inst_{name} || ROLE >= ROLE_admin
Grant required
*/
inst := v1.Group("/instance/:name")
/**
GET /instance/:name/detail
*/
inst.GET("/detail")
inst := v1.Group("/instance/:name", middleware.GrantRequired)
{
inst.GET("/detail")
}
/**
GROUP /admin
Auth required
ROLE >= ROLE_admin
*/
admin := v1.Group("/admin")
/**
GET /admin/instances
*/
admin.GET("/instances")
/**
GET /admin/users
*/
admin.GET("/users")
/**
GET /admin/updates
*/
admin.GET("/updates")
admin := v1.Group("/admin", middleware.AdminRequired)
{
admin.GET("/instances")
admin.GET("/users")
admin.GET("/updates")
}
return r
}

57
internal/service/token.go Normal file
View File

@@ -0,0 +1,57 @@
package service
import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"mc-client-updater-server/pkg/dao"
"mc-client-updater-server/pkg/dao/entity"
"mc-client-updater-server/pkg/result"
"time"
)
type TokenService struct {
ctx *gin.Context
}
func NewTokenService(c *gin.Context) *TokenService {
return &TokenService{ctx: c}
}
func (s *TokenService) VerifyToken(token string) (*entity.Token, bool) {
res := result.NewResult(s.ctx)
// 是否存在
tokenRow := s.getToken(token)
if tokenRow == nil {
res.Unauthorized()
return nil, false
}
// 是否过期
if tokenRow.TTL != 0 && int(time.Since(tokenRow.CreatedAt).Seconds()) > tokenRow.TTL {
res.LoginExpired()
return tokenRow, false
}
return tokenRow, true
}
func (s *TokenService) getToken(token string) *entity.Token {
tokenRow := entity.Token{Token: token}
tx := dao.DB().Last(&tokenRow)
if tx.Error == gorm.ErrRecordNotFound {
return nil
}
return &tokenRow
}
func (s *TokenService) getTokenByUsername(username string) *entity.Token {
tokenRow := entity.Token{GrantTo: username}
tx := dao.DB().First(&tokenRow)
if tx.Error == gorm.ErrRecordNotFound {
return nil
}
return &tokenRow
}
func (s *TokenService) AddToken(tokenObj *entity.Token) error {
tx := dao.DB().Create(tokenObj)
return tx.Error
}

78
internal/service/user.go Normal file
View File

@@ -0,0 +1,78 @@
package service
import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"mc-client-updater-server/pkg/dao"
"mc-client-updater-server/pkg/dao/entity"
"mc-client-updater-server/pkg/password"
"mc-client-updater-server/pkg/result"
"mc-client-updater-server/pkg/token"
"mc-client-updater-server/pkg/util"
"strings"
)
type UserService struct {
ctx *gin.Context
}
func NewUserService(c *gin.Context) *UserService {
return &UserService{ctx: c}
}
func (s *UserService) Login(username, pwd string) {
res := result.NewResult(s.ctx)
user := entity.User{Username: username}
tx := dao.DB().First(&user)
if tx.Error != nil {
switch tx.Error {
case gorm.ErrRecordNotFound:
res.LoginError()
return
default:
res.LoginError()
return
}
}
// 验证密码
ok, err := password.Password().Verify(pwd, user.Password)
if err != nil || !ok {
res.LoginError()
return
}
tokenStr := token.NewToken(user.Username)
tokenResult := &entity.Token{
Token: tokenStr,
GrantTo: user.Username,
TTL: 86400,
}
tokenSrv := NewTokenService(s.ctx)
err = tokenSrv.AddToken(tokenResult)
if err != nil {
res.InternalServerError("创建登录记录时发生意外错误")
return
}
res.Success(tokenResult)
}
func (s *UserService) hasRole(role string, user *entity.User) bool {
roles := strings.Split(user.Roles, ",")
return util.InStringSlice(roles, role)
}
func (s *UserService) getUserByUsername(name string) *entity.User {
user := entity.User{Username: name}
tx := dao.DB().First(&user)
if tx.Error != nil {
return nil
}
return &user
}
func (s *UserService) JudgeRoleByToken(role string, token *entity.Token) bool {
user := s.getUserByUsername(token.GrantTo)
return s.hasRole(role, user)
}