gohttp/admin/login.go

192 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}