登录功能完成
This commit is contained in:
23
internal/api/v1/handler/admin.go
Normal file
23
internal/api/v1/handler/admin.go
Normal 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)
|
||||
|
||||
}
|
||||
39
internal/api/v1/middleware/admin.go
Normal file
39
internal/api/v1/middleware/admin.go
Normal 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()
|
||||
}
|
||||
11
internal/api/v1/middleware/grant.go
Normal file
11
internal/api/v1/middleware/grant.go
Normal 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)
|
||||
}
|
||||
43
internal/api/v1/middleware/logger.go
Normal file
43
internal/api/v1/middleware/logger.go
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
57
internal/service/token.go
Normal 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
78
internal/service/user.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user