如何在 Golang 中检索经过身份验证的用户

How to retrieve the authenticated user in Golang

func Login(c echo.Context) error {
    user := &users.User{}
    if err := c.Bind(&user); err != nil {
        return err
    }

    return token.SigIn(c, user.Email, user.Password)
}

这是我的登录函数,用于在用户发送请求时检索令牌。

处理令牌的登录函数

func SigIn(c echo.Context, email, password string) error {
    user := users.User{}

    db := database.SetUp()

    if err := db.Where("email = ?", email).First(&user).Error; gorm.IsRecordNotFoundError(err) {

        restErr := errors.NewBadRequestError("Invalid credentials")

        return c.JSON(http.StatusBadRequest, restErr)
    }

    if user.VerifyPassword(password) != nil {
        restErr := errors.NewUnauthorizedError("Couldn't log you in with these credentials")

        return c.JSON(http.StatusUnauthorized, restErr)
    }

    //user is successfull

    return CreateToken(c)
}

CreateToken函数如下

type TokenJWT struct {
    Token string `json:"token"`
}

func CreateToken(c echo.Context) error {
    token := jwt.New(jwt.SigningMethodHS256)

    claims := token.Claims.(jwt.MapClaims)
    claims["authorized"] = true
    claims["name"] = "Pascal Gaetan"
    claims["exp"] = time.Now().Add(time.Hour * 1).Unix()

    // Generate encoded token and send it as response.
    t, err := token.SignedString([]byte("my_secret_key"))
    if err != nil {
        return err
    }

    return c.JSON(http.StatusOK, TokenJWT{
        Token: t,
    })
}

当一切都成功时,我想通过调用 Me 函数的 URL /api/me 获得经过身份验证的用户

这是一种相当常见的设计模式,用于创建经过身份验证的客户端,然后在其上调用各种操作方法。您可以执行以下操作:

type Client struct {
    ... // other members
    token string // unexported unless there is a special reason to do otherwise
}

func NewClient(c echo.Context, email, password string) (*Client, error) {
    user := users.User{}
    cl := Client{}
    ... // your original method
    cl.token = token
    return &cl, nil
}

func (c *Client) DoSomething(...) ... { ... }

让我将您的问题分为两部分:第一部分是如何轻松地在 JWT 令牌中或从 JWT 令牌中对用户进行编码和解码,第二部分是如何编写可以从任何地方检索用户的通用代码。

在您的示例中,我提到您创建了一个 MapClaims,但为了降低解析的复杂性,最好使用自定义声明类型创建一个令牌。如果您使用的是 dgrijalva/jwt-go,那么根据文档,您可以执行类似的操作

type UserClaims struct {
    Name string `json:"name"`
    jwt.StandardClaims
}

// encode it as before, but with your created type 
t := jwt.New(signer)
userClaims := &UserClaims{Name: "Burmese"}
t.Claims = userClaims
tokenString, err = t.SignedString(]byte("my_secret_key"))

然后您可以使用

在您的 router/framework 中间件中解析您的用户
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"

token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
    return []byte("my_secret_key"), nil
})

if claims, ok := token.Claims.(*UserClaims); ok && token.Valid {
    fmt.Printf("%v %v", claims.Name, claims.StandardClaims.ExpiresAt)
} else {
    fmt.Println(err)
}

本例取自官方文档here

现在您知道了如何轻松解析经过身份验证的用户结构,下一步逻辑是将其包装到您的中间件中。是否有很多实现细节,比如你可以从 cookie、header 或查询中检索 JWT,还在它们上定义一些顺序,要点如下:你应该将上述代码包装到你的中间件中,在解析结构后你可以传递它通过您的请求上下文。我不使用 echo 和其他框架,但是对于纯 net/http 你可以使用

从中间件传递你解析的结构
context.WithValue(ctx, UserCtxKey, claims)

希望对您有所帮助!