showliuzp 2025-08-27 10:28 采纳率: 84.3%
浏览 7
已结题

golang鉴权中间件


import (
    "net/http"
    "x_dream/types"
    "x_dream/response"
    "github.com/zeromicro/go-zero/rest"
    )

func Router() (r []rest.Route) {
    r = []rest.Route{      
        //add_router(types.POST,"/live/warn",response.WrapResponse(controller.LiveWarn)),
        add_router(types.POST,"/check/login",response.WrapResponse(Login)),
        add_router(types.POST,"/check/refresh/token",response.WrapResponse(RefreshToken)),
    }

    return r
}

func add_router(method_type string, path string, handler http.HandlerFunc) rest.Route {
    return rest.Route{
        Method:  method_type,
        Path:    path,
        Handler: handler,
    }
}

package response

import "fmt"
import "net/http"
import "google.golang.org/grpc/status"
import "github.com/pkg/errors"
import "github.com/zeromicro/go-zero/rest/httpx"


type Response struct {
        Code    int         `json:"code"`
        Msg     string      `json:"msg"`
        Data    interface{} `json:"data"`
}

type CodeError struct {
        errCode int
        errMsg  string
}

func (e *CodeError) GetErrCode() int {
        return e.errCode
}

func (e *CodeError) GetErrMsg() string {
        return e.errMsg
}

func (e *CodeError) Error() string {
        return fmt.Sprintf("ErrCode:%d,ErrMsg:%s", e.errCode, e.errMsg)
}

func NewErrCodeMsg(errCode int, errMsg string) *CodeError {
        return &CodeError{errCode: errCode, errMsg: errMsg}
}

func WrapResponse(fn func(r *http.Request) (interface{}, error)) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
                resp, err := fn(r)
                if err == nil {
                        r := &Response{
                                Code:    0,
                                Msg:    "success",
                                Data:    resp,
                        }

                        httpx.WriteJson(w, http.StatusOK, r)
                } else {
                        errcode := int(500)
                        errmsg := "服务器错误"

                        causeErr := errors.Cause(err)
                        if e, ok := causeErr.(*CodeError); ok {
                                errcode = e.GetErrCode()
                                errmsg = e.GetErrMsg()
                        } else {
                                if gstatus, ok := status.FromError(causeErr); ok {
                                        grpcCode := int(gstatus.Code())
                                        errcode = grpcCode
                                        errmsg = gstatus.Message()
                                }
                        }

                        httpx.WriteJson(w, http.StatusBadRequest, &Response{
                                Code:   errcode,
                                Msg:    errmsg,
                                Data:   nil,
                        })
                }
        }
}

我想在response.WrapResponse加入鉴权中间件,check/login不需要鉴权,其它路由都需要鉴权,没有通过显示鉴权错误信息

鉴权代码:

func CheckToken(r *http.Request)(err error){
    authorization := (*r).Header.Get("authorization")
    exists,err := CheckToken(&authorization);
    if err != nil{
        return
    }

    if !exists{
                return response.NewErrCodeMsg(100, "已过期,请重新通过refresh_token获取token")
    }

    return
}


func CheckToken(token *string)(bools bool,err error){
    if len(*token) <1{
        err = response.NewErrCodeMsg(401, "token为空")
        return
    }

    token_data,err := ParseToken(token)
    if err != nil{       
        err = response.NewErrCodeMsg(300, fmt.Sprintf("%v", err))
        return
    }
  • 写回答

4条回答 默认 最新

  • 檀越@新空间 2025-08-27 10:29
    关注

    上午好☀️☀️☀️️
    本答案参考通义千问

    要在 response.WrapResponse 中加入鉴权中间件,确保 /check/login 路由不需要鉴权,而其他路由需要鉴权,可以通过在 WrapResponse 中添加一个鉴权逻辑,并在处理请求前进行判断。

    下面是详细的解决方案:


    1. 修改 WrapResponse 函数,加入鉴权逻辑

    我们需要在 WrapResponse 中添加一个中间件,用于检查请求是否需要鉴权。对于 /check/login 接口,我们跳过鉴权;对于其他接口,则执行鉴权。

    func WrapResponse(fn func(r *http.Request) (interface{}, error)) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            // 检查当前请求路径是否为 /check/login,如果是则跳过鉴权
            if r.URL.Path == "/check/login" {
                resp, err := fn(r)
                if err == nil {
                    httpx.WriteJson(w, http.StatusOK, &Response{
                        Code: 0,
                        Msg:  "success",
                        Data: resp,
                    })
                } else {
                    // 处理错误
                    handleErrorResponse(w, err)
                }
                return
            }
    
            // 否则,执行鉴权
            err := CheckToken(r)
            if err != nil {
                handleErrorResponse(w, err)
                return
            }
    
            // 鉴权通过后调用原始函数
            resp, err := fn(r)
            if err == nil {
                httpx.WriteJson(w, http.StatusOK, &Response{
                    Code: 0,
                    Msg:  "success",
                    Data: resp,
                })
            } else {
                handleErrorResponse(w, err)
            }
        }
    }
    
    // handleErrorResponse 是一个辅助函数,用于统一处理错误响应
    func handleErrorResponse(w http.ResponseWriter, err error) {
        errcode := int(500)
        errmsg := "服务器错误"
    
        causeErr := errors.Cause(err)
        if e, ok := causeErr.(*CodeError); ok {
            errcode = e.GetErrCode()
            errmsg = e.GetErrMsg()
        } else if gstatus, ok := status.FromError(causeErr); ok {
            grpcCode := int(gstatus.Code())
            errcode = grpcCode
            errmsg = gstatus.Message()
        }
    
        httpx.WriteJson(w, http.StatusBadRequest, &Response{
            Code: errcode,
            Msg:  errmsg,
            Data: nil,
        })
    }
    

    2. 确保 CheckToken 函数返回正确的错误类型

    你提供的 CheckToken 函数应该返回 *CodeError 类型的错误,这样在 WrapResponse 中才能正确识别并返回相应的错误码和信息。

    修改后的 CheckToken 函数:

    func CheckToken(r *http.Request) error {
        authorization := r.Header.Get("authorization")
        exists, err := CheckToken(&authorization)
        if err != nil {
            return err
        }
    
        if !exists {
            return response.NewErrCodeMsg(100, "已过期,请重新通过refresh_token获取token")
        }
    
        return nil
    }
    
    func CheckToken(token *string) (bool, error) {
        if len(*token) < 1 {
            return false, response.NewErrCodeMsg(401, "token为空")
        }
    
        tokenData, err := ParseToken(token)
        if err != nil {
            return false, response.NewErrCodeMsg(300, fmt.Sprintf("%v", err))
        }
    
        // 这里可以添加更复杂的验证逻辑,例如检查 token 是否有效、是否过期等
    
        return true, nil
    }
    

    3. 确保 ParseToken 返回正确的数据结构

    你需要确保 ParseToken 函数能正确解析 token,并返回有效的用户信息或错误。如果解析失败,应返回 *CodeError 类型的错误。

    示例(假设你使用 JWT):

    func ParseToken(token *string) (*User, error) {
        // 假设这里使用 jwt.Parse 解析 token
        claims, err := jwt.ParseWithClaims(*token, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
            return []byte("your-secret-key"), nil
        })
    
        if err != nil {
            return nil, response.NewErrCodeMsg(401, "无效的 token")
        }
    
        if !claims.Valid {
            return nil, response.NewErrCodeMsg(401, "token 无效")
        }
    
        user := &User{
            ID:   claims.ID,
            Name: claims.Name,
        }
    
        return user, nil
    }
    

    4. 最终效果说明

    • /check/login 接口:无需鉴权,直接调用对应的 handler。
    • 其他接口:在调用 handler 前会先执行 CheckToken,若鉴权失败,返回 401 Unauthorized 或自定义错误码。
    • 错误统一处理:所有错误都会通过 handleErrorResponse 统一格式化输出。

    5. 可选优化:使用中间件封装鉴权逻辑

    如果你希望更优雅地实现鉴权逻辑,可以将鉴权逻辑封装为一个中间件函数,然后将其应用到所有需要鉴权的路由上。

    func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            // 如果是登录接口,跳过鉴权
            if r.URL.Path == "/check/login" {
                next(w, r)
                return
            }
    
            err := CheckToken(r)
            if err != nil {
                handleErrorResponse(w, err)
                return
            }
    
            next(w, r)
        }
    }
    

    然后在 add_router 中使用:

    add_router(types.POST, "/check/login", response.WrapResponse(AuthMiddleware(Login)))
    

    注意:这种方式可能需要对 WrapResponseAuthMiddleware 的组合方式进行调整,以保证逻辑顺序正确。


    ✅ 总结

    • WrapResponse 中加入鉴权逻辑,区分 /check/login 接口。
    • 使用 CheckToken 函数进行鉴权,返回 *CodeError 错误。
    • 所有错误统一处理,提升代码可维护性。
    • 可选使用中间件封装鉴权逻辑,提高复用性。

    如需进一步帮助,请提供 ParseTokenCheckToken 的完整实现。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

问题事件

  • 系统已结题 9月14日
  • 已采纳回答 9月6日
  • 创建了问题 8月27日