提交
This commit is contained in:
206
internal/middleware/auth.go
Normal file
206
internal/middleware/auth.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"os"
|
||||
"ripper/internal/app/github_auth"
|
||||
"ripper/internal/response"
|
||||
jwtpkg "ripper/pkg/jwt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OAuthCheck struct {
|
||||
ClientId string `json:"client_id" form:"client_id"`
|
||||
DeviceCode string `json:"device_code" form:"device_code"`
|
||||
GrantType string `json:"grant_type" form:"grant_type"`
|
||||
}
|
||||
|
||||
func DeviceCodeCheckAuth(ctx *gin.Context) {
|
||||
checkInfo := &OAuthCheck{}
|
||||
if err := ctx.ShouldBind(&checkInfo); err != nil {
|
||||
response.FailJson(ctx, response.FailStruct{
|
||||
Code: -1,
|
||||
Msg: "Invalid client id.",
|
||||
}, false)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
info, _ := github_auth.GetClientAuthInfoByDeviceCode(checkInfo.DeviceCode)
|
||||
if info.CardCode == "" {
|
||||
ctx.JSON(http.StatusOK, gin.H{
|
||||
"error": "authorization_pending",
|
||||
"error_description": "The authorization request is still pending.",
|
||||
"error_uri": "https://docs.github.com/developers/apps/authorizing-oauth-apps#error-codes-for-the-device-flow",
|
||||
})
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
ctx.Set("client_auth_info", info)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
func AuthCodeFlowCheckAuth(ctx *gin.Context) {
|
||||
checkInfoClient := &github_auth.ClientOAuthInfo{}
|
||||
err := ctx.Bind(&checkInfoClient)
|
||||
if err != nil {
|
||||
response.FailJson(ctx, response.FailStruct{
|
||||
Code: -1,
|
||||
Msg: "Invalid client id.",
|
||||
}, false)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
oauthCodeInfo, err := github_auth.GetOAuthCodeInfoByClientIdAndCode(checkInfoClient.ClientId, checkInfoClient.Code)
|
||||
if err != nil {
|
||||
response.FailJson(ctx, response.FailStruct{
|
||||
Code: -1,
|
||||
Msg: "Invalid client id.",
|
||||
}, false)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Set("client_auth_info", oauthCodeInfo)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
func AccessTokenCheckAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.Request.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
response.FailJsonAndStatusCode(c, http.StatusForbidden, response.NoAccess, false)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
last := strings.Index(token, " ")
|
||||
if len(token) < last || last == -1 {
|
||||
response.FailJsonAndStatusCode(c, http.StatusForbidden, response.TokenWrongful, false)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
token = token[last+1:]
|
||||
chk, jwter, err := jwtpkg.CheckToken(token, &UserLoad{}, "user")
|
||||
if err != nil {
|
||||
errmsg := response.TokenWrongful
|
||||
errmsg.Msg = "令牌验证错误"
|
||||
response.FailJsonAndStatusCode(c, http.StatusForbidden, errmsg, true, err.Error())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if !chk {
|
||||
response.FailJsonAndStatusCode(c, http.StatusForbidden, response.NoAccess, true, "破损令牌")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
chs := true
|
||||
issuerStr := ""
|
||||
issuerStr, err = jwter.GetIssuer()
|
||||
if err != nil {
|
||||
chs = false
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if "user" != issuerStr && issuerStr != "" {
|
||||
chs = false
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if !chs {
|
||||
errmsg := response.TokenWrongful
|
||||
errmsg.Msg = "签名错误"
|
||||
response.FailJsonAndStatusCode(c, http.StatusForbidden, errmsg, true, err.Error())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Set("token", jwter)
|
||||
c.Set("tokenStr", token)
|
||||
c.Set("token.issuer", issuerStr)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func TokenCheckAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
clientType := os.Getenv("COPILOT_CLIENT_TYPE")
|
||||
copilotProxyAll, err := strconv.ParseBool(os.Getenv("COPILOT_PROXY_ALL"))
|
||||
if clientType == "github" && !copilotProxyAll {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
token := c.Request.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenWrongful, false)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
last := strings.Index(token, " ")
|
||||
if len(token) < last || last == -1 {
|
||||
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenWrongful, false)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
token = token[last+1:]
|
||||
parsedToken := parseAuthorizationToken(token)
|
||||
// 校验exp是否过期
|
||||
expired, err := isExpired(parsedToken["exp"])
|
||||
if err != nil {
|
||||
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenWrongful, false)
|
||||
c.Abort()
|
||||
return
|
||||
} else {
|
||||
if expired {
|
||||
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenOverdue, false)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
rawToken := github_auth.JsonMap2Token(map[string]interface{}{
|
||||
"tid": parsedToken["tid"],
|
||||
"exp": parsedToken["exp"],
|
||||
"sku": parsedToken["sku"],
|
||||
"st": parsedToken["st"],
|
||||
"chat": parsedToken["chat"],
|
||||
"u": parsedToken["u"],
|
||||
})
|
||||
sign := "1:" + github_auth.Token2Sign(rawToken)
|
||||
if sign != parsedToken["8kp"] {
|
||||
response.FailJsonAndStatusCode(c, http.StatusUnauthorized, response.TokenWrongful, false)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func parseAuthorizationToken(token string) map[string]string {
|
||||
result := make(map[string]string)
|
||||
pairs := strings.Split(token, ";")
|
||||
|
||||
for _, pair := range pairs {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) == 2 {
|
||||
key := kv[0]
|
||||
value := kv[1]
|
||||
|
||||
if key == "tid" || key == "exp" || key == "sku" || key == "st" || key == "8kp" || key == "chat" || key == "u" {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func isExpired(expStr string) (bool, error) {
|
||||
exp, err := strconv.ParseInt(expStr, 10, 64)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid exp timestamp: %v", err)
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
return now > exp, nil
|
||||
}
|
||||
30
internal/middleware/cors.go
Normal file
30
internal/middleware/cors.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
//Cors 跨域中间件
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
origin := c.Request.Header.Get("origin") //请求头部
|
||||
if len(origin) == 0 {
|
||||
origin = c.Request.Header.Get("Origin")
|
||||
}
|
||||
//接收客户端发送的origin (重要!)
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
//允许客户端传递校验信息比如 cookie (重要)
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
//服务器支持的所有跨域请求的方法
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, DELETE, UPDATE")
|
||||
c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
// 设置预验请求有效期为 86400 秒
|
||||
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(200)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
102
internal/middleware/jwt.go
Normal file
102
internal/middleware/jwt.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"ripper/internal/response"
|
||||
jwtpkg "ripper/pkg/jwt"
|
||||
)
|
||||
|
||||
// JWTCheck 检查是否登陆
|
||||
// 检查完毕会将jwt结构体写入到Context
|
||||
// 适用于同时用于公开与鉴权的路由
|
||||
func JWTCheck(c *gin.Context, model jwtpkg.LoadModel, issure ...string) (bool, error) {
|
||||
token := c.Request.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
return false, nil
|
||||
}
|
||||
if len(token) < 8 {
|
||||
return false, nil
|
||||
}
|
||||
token = token[7:]
|
||||
chk, jwter, err := jwtpkg.CheckToken(token, model, "")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
chs := true
|
||||
for _, v := range issure {
|
||||
jwt, err := jwter.GetIssuer()
|
||||
if err != nil {
|
||||
chs = false
|
||||
break
|
||||
}
|
||||
if v != jwt {
|
||||
chs = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !chs {
|
||||
return false, nil
|
||||
}
|
||||
if !chk {
|
||||
return false, nil
|
||||
}
|
||||
c.Set("token", jwter)
|
||||
c.Next()
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// JWTAuth 为JWT中间件,客户端下需要在header带上Authorization: Bearer <token>
|
||||
// issure 为可选验证签名,支持多参选择
|
||||
func JWTAuth(model jwtpkg.LoadModel, issure ...string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.Request.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
response.FailJson(c, response.NoAccess, false)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if len(token) < 8 {
|
||||
response.FailJson(c, response.TokenWrongful, false)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
token = token[7:]
|
||||
chk, jwter, err := jwtpkg.CheckToken(token, model, "")
|
||||
if err != nil {
|
||||
errmsg := response.TokenWrongful
|
||||
errmsg.Msg = "令牌验证错误"
|
||||
response.FailJson(c, errmsg, true, err.Error())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if !chk {
|
||||
response.FailJson(c, response.NoAccess, true, "破损令牌")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
chs := true
|
||||
issuerStr := ""
|
||||
for _, v := range issure {
|
||||
issuerStr, err = jwter.GetIssuer()
|
||||
if err != nil {
|
||||
chs = false
|
||||
break
|
||||
}
|
||||
if v != issuerStr {
|
||||
chs = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !chs {
|
||||
errmsg := response.TokenWrongful
|
||||
errmsg.Msg = "签名错误"
|
||||
response.FailJson(c, errmsg, true, err.Error())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Set("token", jwter)
|
||||
c.Set("tokenStr", token)
|
||||
c.Set("token.issuer", issuerStr)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
23
internal/middleware/jwt_model.go
Normal file
23
internal/middleware/jwt_model.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
jwtPkg "ripper/pkg/jwt"
|
||||
)
|
||||
|
||||
type AdminLoad struct {
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
type UserLoad struct {
|
||||
UserDisplayName string `json:"userDisplayName,omitempty"`
|
||||
CardCode string `json:"token"`
|
||||
Client string `json:"client"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func NewUserLoad(ID uint, ExpiresAt int64, Issuer string) *UserLoad {
|
||||
return &UserLoad{
|
||||
RegisteredClaims: jwtPkg.CreateStandardClaims(ExpiresAt, Issuer),
|
||||
}
|
||||
}
|
||||
1
internal/middleware/logger.go
Normal file
1
internal/middleware/logger.go
Normal file
@@ -0,0 +1 @@
|
||||
package middleware
|
||||
Reference in New Issue
Block a user