88 lines
2.0 KiB
Go
88 lines
2.0 KiB
Go
package auth
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"database/sql"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
type Auth struct {
|
|
secret []byte
|
|
}
|
|
|
|
func New(secret string) *Auth {
|
|
return &Auth{secret: []byte(secret)}
|
|
}
|
|
|
|
func (a *Auth) Issue(userID string) (string, error) {
|
|
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
"sub": userID,
|
|
"iat": time.Now().Unix(),
|
|
"exp": time.Now().Add(7 * 24 * time.Hour).Unix(),
|
|
})
|
|
return t.SignedString(a.secret)
|
|
}
|
|
|
|
func (a *Auth) parseToken(token string) (string, error) {
|
|
parsed, err := jwt.Parse(token, func(t *jwt.Token) (any, error) {
|
|
if t.Method.Alg() != jwt.SigningMethodHS256.Alg() {
|
|
return nil, jwt.ErrSignatureInvalid
|
|
}
|
|
return a.secret, nil
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if !parsed.Valid {
|
|
return "", jwt.ErrTokenInvalidClaims
|
|
}
|
|
claims, ok := parsed.Claims.(jwt.MapClaims)
|
|
if !ok {
|
|
return "", jwt.ErrTokenInvalidClaims
|
|
}
|
|
sub, ok := claims["sub"].(string)
|
|
if !ok || sub == "" {
|
|
return "", jwt.ErrTokenInvalidClaims
|
|
}
|
|
return sub, nil
|
|
}
|
|
|
|
func (a *Auth) RequireAuth(sqlite *sql.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
hdr := c.GetHeader("Authorization")
|
|
if hdr == "" || !strings.HasPrefix(hdr, "Bearer ") {
|
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "未登录"})
|
|
return
|
|
}
|
|
token := strings.TrimSpace(strings.TrimPrefix(hdr, "Bearer "))
|
|
userID, err := a.parseToken(token)
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "Token 无效"})
|
|
return
|
|
}
|
|
|
|
var exists int
|
|
if err := sqlite.QueryRow(`SELECT COUNT(1) FROM users WHERE id = ?`, userID).Scan(&exists); err != nil {
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "服务异常"})
|
|
return
|
|
}
|
|
if subtle.ConstantTimeEq(int32(exists), 1) != 1 {
|
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "账号不存在"})
|
|
return
|
|
}
|
|
c.Set("userID", userID)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func UserID(c *gin.Context) string {
|
|
v, _ := c.Get("userID")
|
|
s, _ := v.(string)
|
|
return s
|
|
}
|