2024-10-19 12:09:16 -04:00
|
|
|
package controller
|
|
|
|
|
|
|
|
import (
|
2024-10-20 02:05:43 -04:00
|
|
|
"fmt"
|
2024-10-19 12:09:16 -04:00
|
|
|
"log"
|
2024-10-20 02:05:43 -04:00
|
|
|
"net/http"
|
2024-10-19 12:09:16 -04:00
|
|
|
"time"
|
|
|
|
|
|
|
|
jwt "github.com/appleboy/gin-jwt/v2"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/naiba/nezha/model"
|
2024-10-19 23:47:45 -04:00
|
|
|
"github.com/naiba/nezha/service/singleton"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
2024-10-19 12:09:16 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
func initParams() *jwt.GinJWTMiddleware {
|
|
|
|
return &jwt.GinJWTMiddleware{
|
2024-10-19 23:47:45 -04:00
|
|
|
Realm: singleton.Conf.SiteName,
|
|
|
|
Key: []byte(singleton.Conf.SecretKey),
|
2024-10-20 02:05:43 -04:00
|
|
|
CookieName: "nz-jwt",
|
2024-10-19 12:09:16 -04:00
|
|
|
Timeout: time.Hour,
|
|
|
|
MaxRefresh: time.Hour,
|
|
|
|
IdentityKey: model.CtxKeyAuthorizedUser,
|
|
|
|
PayloadFunc: payloadFunc(),
|
|
|
|
|
|
|
|
IdentityHandler: identityHandler(),
|
|
|
|
Authenticator: authenticator(),
|
|
|
|
Authorizator: authorizator(),
|
|
|
|
Unauthorized: unauthorized(),
|
2024-10-20 02:05:43 -04:00
|
|
|
TokenLookup: "header: Authorization, query: token, cookie: nz-jwt",
|
2024-10-19 12:09:16 -04:00
|
|
|
TokenHeadName: "Bearer",
|
|
|
|
TimeFunc: time.Now,
|
2024-10-20 02:05:43 -04:00
|
|
|
|
|
|
|
LoginResponse: func(c *gin.Context, code int, token string, expire time.Time) {
|
|
|
|
c.JSON(http.StatusOK, model.CommonResponse[model.LoginResponse]{
|
|
|
|
Success: true,
|
|
|
|
Data: model.LoginResponse{
|
|
|
|
Token: token,
|
|
|
|
Expire: expire.Format(time.RFC3339),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
},
|
|
|
|
RefreshResponse: refreshResponse,
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func handlerMiddleWare(authMiddleware *jwt.GinJWTMiddleware) gin.HandlerFunc {
|
|
|
|
return func(context *gin.Context) {
|
|
|
|
errInit := authMiddleware.MiddlewareInit()
|
|
|
|
if errInit != nil {
|
|
|
|
log.Fatal("authMiddleware.MiddlewareInit() Error:" + errInit.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func payloadFunc() func(data interface{}) jwt.MapClaims {
|
|
|
|
return func(data interface{}) jwt.MapClaims {
|
2024-10-20 02:05:43 -04:00
|
|
|
if v, ok := data.(string); ok {
|
2024-10-19 12:09:16 -04:00
|
|
|
return jwt.MapClaims{
|
2024-10-20 02:05:43 -04:00
|
|
|
model.CtxKeyAuthorizedUser: v,
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return jwt.MapClaims{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func identityHandler() func(c *gin.Context) interface{} {
|
|
|
|
return func(c *gin.Context) interface{} {
|
|
|
|
claims := jwt.ExtractClaims(c)
|
2024-10-20 02:05:43 -04:00
|
|
|
userId := claims[model.CtxKeyAuthorizedUser].(string)
|
2024-10-19 23:47:45 -04:00
|
|
|
var user model.User
|
|
|
|
if err := singleton.DB.First(&user, userId).Error; err != nil {
|
|
|
|
return nil
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|
2024-10-19 23:47:45 -04:00
|
|
|
return &user
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-20 02:05:43 -04:00
|
|
|
// User Login
|
|
|
|
// @Summary user login
|
2024-10-19 23:47:45 -04:00
|
|
|
// @Schemes
|
2024-10-20 02:05:43 -04:00
|
|
|
// @Description user login
|
2024-10-19 23:47:45 -04:00
|
|
|
// @Accept json
|
2024-10-20 02:05:43 -04:00
|
|
|
// @param request body model.LoginRequest true "Login Request"
|
2024-10-19 23:47:45 -04:00
|
|
|
// @Produce json
|
2024-10-20 02:05:43 -04:00
|
|
|
// @Success 200 {object} model.CommonResponse[model.LoginResponse]
|
|
|
|
// @Router /login [post]
|
2024-10-19 12:09:16 -04:00
|
|
|
func authenticator() func(c *gin.Context) (interface{}, error) {
|
|
|
|
return func(c *gin.Context) (interface{}, error) {
|
|
|
|
var loginVals model.LoginRequest
|
|
|
|
if err := c.ShouldBind(&loginVals); err != nil {
|
|
|
|
return "", jwt.ErrMissingLoginValues
|
|
|
|
}
|
|
|
|
|
2024-10-19 23:47:45 -04:00
|
|
|
var user model.User
|
2024-10-20 02:05:43 -04:00
|
|
|
if err := singleton.DB.Select("id", "password").Where("username = ?", loginVals.Username).First(&user).Error; err != nil {
|
2024-10-19 23:47:45 -04:00
|
|
|
return nil, jwt.ErrFailedAuthentication
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|
2024-10-19 23:47:45 -04:00
|
|
|
|
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginVals.Password)); err != nil {
|
|
|
|
return nil, jwt.ErrFailedAuthentication
|
|
|
|
}
|
|
|
|
|
2024-10-20 02:05:43 -04:00
|
|
|
if err := singleton.DB.Model(&user).Update("login_expire", time.Now().Add(time.Hour)).Error; err != nil {
|
|
|
|
return nil, jwt.ErrFailedAuthentication
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%d", user.ID), nil
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func authorizator() func(data interface{}, c *gin.Context) bool {
|
|
|
|
return func(data interface{}, c *gin.Context) bool {
|
2024-10-19 23:47:45 -04:00
|
|
|
_, ok := data.(*model.User)
|
|
|
|
return ok
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func unauthorized() func(c *gin.Context, code int, message string) {
|
|
|
|
return func(c *gin.Context, code int, message string) {
|
2024-10-20 02:05:43 -04:00
|
|
|
c.JSON(http.StatusOK, model.CommonResponse[interface{}]{
|
|
|
|
Success: false,
|
|
|
|
Error: "ApiErrorUnauthorized",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refresh token
|
|
|
|
// @Summary Refresh token
|
|
|
|
// @Security BearerAuth
|
|
|
|
// @Schemes
|
|
|
|
// @Description Refresh token
|
|
|
|
// @Tags auth required
|
|
|
|
// @Produce json
|
|
|
|
// @Success 200 {object} model.CommonResponse[model.LoginResponse]
|
|
|
|
// @Router /refresh_token [get]
|
|
|
|
func refreshResponse(c *gin.Context, code int, token string, expire time.Time) {
|
|
|
|
claims := jwt.ExtractClaims(c)
|
|
|
|
userId := claims[model.CtxKeyAuthorizedUser].(string)
|
|
|
|
if err := singleton.DB.Model(&model.User{}).Where("id = ?", userId).Update("login_expire", expire).Error; err != nil {
|
|
|
|
c.JSON(http.StatusOK, model.CommonResponse[interface{}]{
|
2024-10-19 12:09:16 -04:00
|
|
|
Success: false,
|
2024-10-20 02:05:43 -04:00
|
|
|
Error: "ApiErrorUnauthorized",
|
2024-10-19 12:09:16 -04:00
|
|
|
})
|
2024-10-20 02:05:43 -04:00
|
|
|
return
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|
2024-10-20 02:05:43 -04:00
|
|
|
c.JSON(http.StatusOK, model.CommonResponse[model.LoginResponse]{
|
|
|
|
Success: true,
|
|
|
|
Data: model.LoginResponse{
|
|
|
|
Token: token,
|
|
|
|
Expire: expire.Format(time.RFC3339),
|
|
|
|
},
|
|
|
|
})
|
2024-10-19 12:09:16 -04:00
|
|
|
}
|