192 lines
5.5 KiB
Go
192 lines
5.5 KiB
Go
package admin
|
||
|
||
import (
|
||
"encoding/hex"
|
||
"errors"
|
||
"fmt"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
|
||
"git.pyer.club/kingecg/gohttpd/model"
|
||
"git.pyer.club/kingecg/gohttpd/server"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/golang-jwt/jwt/v5"
|
||
)
|
||
|
||
type LoginModel struct {
|
||
Username string `json:"username"`
|
||
Encrypt string `json:"password"`
|
||
}
|
||
|
||
// login 处理用户登录请求,验证凭证并生成会话令牌。
|
||
// 参数:
|
||
//
|
||
// w: HTTP响应写入器,用于返回响应状态和数据。
|
||
// r: 指向HTTP请求的指针,包含请求上下文和数据。
|
||
//
|
||
// 返回值:
|
||
//
|
||
// 无直接返回值,通过w写入HTTP响应。
|
||
func login(c *gin.Context) {
|
||
// 从请求上下文中获取请求数据
|
||
// ctx := c.Request.Context()
|
||
// ctxData := ctx.Value(server.RequestCtxKey("data")).(map[string]interface{})
|
||
// data, ok := ctxData["data"]
|
||
loginModel := LoginModel{}
|
||
// 绑定请求体到LoginModel结构体
|
||
if err := c.ShouldBindJSON(&loginModel); err != nil {
|
||
c.Writer.WriteHeader(http.StatusBadRequest)
|
||
c.Writer.Write(server.NewErrorResult(err))
|
||
return
|
||
}
|
||
|
||
// 验证是否为管理员账号
|
||
if loginModel.Username == "admin" {
|
||
// 解密密码并验证与配置文件中密码是否匹配
|
||
decryptText, _ := Decrypt(loginModel.Encrypt)
|
||
if decryptText == model.GetConfig().Admin.Password {
|
||
// 生成JWT令牌
|
||
token, err := GenerateToken(loginModel.Username)
|
||
if err != nil {
|
||
c.Writer.WriteHeader(http.StatusInternalServerError)
|
||
c.Writer.Write(server.NewErrorResult(err))
|
||
return
|
||
}
|
||
// 设置认证Cookie并返回成功响应
|
||
cookie := &http.Cookie{
|
||
Name: "token",
|
||
Value: token,
|
||
Path: "/",
|
||
SameSite: http.SameSiteStrictMode,
|
||
Expires: time.Now().Add(time.Hour * 24 * 7),
|
||
}
|
||
c.Writer.Header().Set("Set-Cookie", cookie.String())
|
||
c.Writer.WriteHeader(http.StatusOK)
|
||
c.Writer.Write(server.NewSuccessResult("Login success"))
|
||
return
|
||
}
|
||
} else {
|
||
// 非管理员账号返回禁止访问响应
|
||
c.Writer.WriteHeader(http.StatusForbidden)
|
||
resp := server.NewErrorResult(errors.New("not allowed user/password"))
|
||
c.Writer.Write(resp)
|
||
return
|
||
}
|
||
// 默认返回未授权响应
|
||
c.Writer.WriteHeader(http.StatusUnauthorized)
|
||
c.Writer.Write(server.NewErrorResult(errors.New("not allowed user/password")))
|
||
}
|
||
|
||
// 实现非对称加密
|
||
func Encrypt(plaintext string) (string, error) {
|
||
ciphertext := make([]byte, len(plaintext))
|
||
for i := 0; i < len(plaintext); i++ {
|
||
ciphertext[i] = plaintext[i] ^ 0xFF
|
||
}
|
||
return string(ciphertext), nil
|
||
}
|
||
|
||
// 实现非对称解密
|
||
func Decrypt(ciphertext string) (string, error) {
|
||
plaintext := make([]byte, 1)
|
||
cipbyte := []byte(ciphertext)
|
||
// 每次取前2字节
|
||
for i := 0; i < len(cipbyte); i += 2 {
|
||
d := cipbyte[i : i+2]
|
||
// 转成16进制
|
||
dd, err := hex.DecodeString(string(d))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
// 异或
|
||
plaintext = append(plaintext, dd[0]^0xFF)
|
||
}
|
||
//去除末尾13个字节
|
||
plaintext = plaintext[1 : len(plaintext)-13]
|
||
return string(plaintext), nil
|
||
}
|
||
|
||
// 生成token
|
||
func GenerateToken(username string) (string, error) {
|
||
// jwt token
|
||
jwtConfig := model.GetConfig().Admin.Jwt
|
||
secret := jwtConfig.Secret
|
||
expire := jwtConfig.Expire
|
||
issuer := jwtConfig.Issuer
|
||
audience := jwtConfig.Audience
|
||
claim := &jwt.RegisteredClaims{
|
||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expire) * time.Hour)),
|
||
Issuer: issuer,
|
||
Audience: []string{audience},
|
||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||
Subject: username,
|
||
}
|
||
// 生成token
|
||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
|
||
return token.SignedString([]byte(secret))
|
||
}
|
||
|
||
func ginBashiAuth(c *gin.Context) {
|
||
// first get Url
|
||
url := c.Request.URL.Path
|
||
// check if url is login
|
||
if strings.Contains(url, "login") || strings.Contains(url, "logout") {
|
||
c.Next()
|
||
return
|
||
}
|
||
accountMap := map[string]string{}
|
||
accountMap[model.GetConfig().Admin.Username] = model.GetConfig().Admin.Password
|
||
handler := gin.BasicAuth(accountMap) // 使用gin的BasicAuth中间件进行认证
|
||
handler(c) // 调用认证处理函数
|
||
}
|
||
|
||
func jwtAuth(c *gin.Context) {
|
||
|
||
config := model.GetConfig().Admin
|
||
if config == nil || config.Jwt == nil {
|
||
c.AbortWithError(http.StatusInternalServerError, errors.New("Jwt config error"))
|
||
// http.Error(w, "Jwt config error", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
url := c.Request.URL.Path
|
||
// check if url is login
|
||
if strings.Contains(url, "login") || strings.Contains(url, "logout") {
|
||
c.Next()
|
||
return
|
||
}
|
||
jwtConfig := config.Jwt
|
||
if jwtConfig.Secret == "" {
|
||
c.Next()
|
||
return
|
||
}
|
||
// 从cookie中获取token
|
||
tokenCookie, err := c.Cookie("auth_token") //r.Cookie("auth_token")
|
||
if err != nil {
|
||
c.AbortWithStatus(http.StatusUnauthorized)
|
||
return
|
||
}
|
||
tokenString := tokenCookie
|
||
token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||
// 确保签名方法是正确的
|
||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||
}
|
||
return []byte(jwtConfig.Secret), nil
|
||
})
|
||
if err != nil {
|
||
// l.Error("Failed to parse JWT: %v", err)
|
||
c.AbortWithStatus(http.StatusUnauthorized)
|
||
return
|
||
}
|
||
if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid {
|
||
// 验证通过,将用户信息存储在请求上下文中
|
||
// ctx := context.WithValue(r.Context(), "user", claims)
|
||
// next.ServeHTTP(w, r.WithContext(ctx))
|
||
c.Set("user", claims)
|
||
return
|
||
}
|
||
c.AbortWithStatus(http.StatusUnauthorized)
|
||
// http.Error(w, "Unauthorized.", http.StatusUnauthorized)
|
||
}
|