如何将我的服务开放给用户:构建 API 接口和用户认证的实践指南(下)|青训营

86 阅读5分钟

用户认证

用户向服务器发送用户名和密码。验证服务器后,相关数据(如用户名,用户角色等)将保存在当前会话(session)中。服务器向用户返回 session_id,session 信息都会写入到用户的 Cookie。用户的每个后续请求都将通过在 Cookie 中取出 session_id 传给服务器。服务器收到 session_id 并对比之前保存的数据,确认用户的身份。

上述过程讲解用户认证的过程,用户认证也就是登陆过程,登陆成功就可以进行后续操作。

Token模式

image.png

JWT介绍

JWT 是 JSON Web Token 的缩写,即 JSON Web 令牌,是一种自包含令牌。

JWT 的使用场景:

  • 一种情况是 webapi,类似之前的阿里云播放凭证的功能
  • 另一种情况是多 web 服务器下实现无状态分布式身份验证

JWT的作用:

JWT 最重要的作用就是对 token 信息的防伪作用 JWT 的原理:

一个 JWT 由三个部分组成:JWT 头、有效载荷、签名哈希 最后由这三者组合进行 base64 编码得到 JWT JWT的用法:

客户端接收服务器返回的 JWT,将其存储在 Cookie 或 localStorage 中

此后,客户端将在与服务器交互中都会带 JWT。如果将它存储在 Cookie 中,就可以自动发送,但是不会跨域,因此一般是将它放入 HTTP 请求的 Header Authorization 字段中。

Token 模式是一种身份验证和授权的方案,而 JWT 则是 Token 模式的一种具体实现方式,其中 JWT 是基于 JSON 的开放标准,并且具有一定的特性和优势。

image.png

在gin框架使用JWT

go get "github.com/golang-jwt/jwt/v4"

通过这段可以下载JWT相关库。

JWT引入

JWT的引入是方便我们进行用户认证。用户认证最简单的就是需要账号和密码,那么我们就需要在客户端接收账号和密码,然后进行验证。

生成JWT

func GenerateJWT(userName string) (string, error) {
    claims := jwt.MapClaims{
       "id":  userName,
       "exp": time.Now().Add(time.Hour * 24).Unix(),
       "iss": "your-app",
       "iat": time.Now().Unix(),
    }
    token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
    return token.SigningString()
}
  1. 创建一个包含用户信息的声明 claims。在这个例子中,声明中包含以下字段:

    • "id": 用户名(或用户ID),将作为用户标识。
    • "exp": Token 的过期时间,设定为当前时间加上一天的时间间隔,表示 Token 在一天后过期。
    • "iss": Token 的签发者,可以是你的应用程序名称或标识。
    • "iat": Token 的签发时间,设定为当前时间。
  2. 使用声明 claims 创建一个新的 JWT Token。jwt.NewWithClaims 函数来自 github.com/golang-jwt/jwt/v4 包,它接收两个参数:签名方法和声明。

    • jwt.SigningMethodES256 表示使用 ECDSA 256 位算法作为签名方法。
    • claims 是之前创建的声明。
  3. 调用 SigningString 方法来生成 JWT Token 的字符串表示。SigningString 是 JWT Token 对象提供的方法,它会生成一个用于签名的字符串。

    • 返回的字符串是符合 JWT 标准的字符串结果,可以直接用于传输或存储。
  4. 返回生成的 JWT Token 字符串。

将返回的密文进行解密

解密JWT

func ParseJWT(tokenString string) (*jwt.MapClaims, error) {
    claims, err := jwt.ParseWithClaims(tokenString, &jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) {
       return secretKey, nil
    })
    if err != nil {
       return nil, err
    }
    mapClaims := claims.Claims.(*jwt.MapClaims)
    if !claims.Valid || mapClaims.Valid() != nil {
       return nil, fmt.Errorf("error")
    }
    return mapClaims, nil
}
  1. 接收一个 JWT Token 字符串 tokenString 作为参数。

  2. 使用 jwt.ParseWithClaims 函数解析 JWT Token,并将解析后的声明存储在 claims 变量中。

    • 第一个参数 tokenString 是要解析的 JWT Token 字符串。
    • 第二个参数 &jwt.MapClaims{} 是一个空的 jwt.MapClaims 对象指针,用于存储解析后的声明信息。
    • 第三个参数是一个回调函数,用于提供用于验证签名的密钥。在这里,回调函数返回固定的 secretKey(密钥)。
  3. 检查解析过程中的错误。如果解析失败,将返回 nil 和解析过程中的错误。

  4. 将声明信息的类型断言为 *jwt.MapClaims,并将其存储在 mapClaims 变量中。

    • claims.Claims 是一个 jwt.Claims 接口类型,包含了解析后的声明信息。
    • 通过将 claims.Claims 类型断言为 *jwt.MapClaims,可以将其转换为具体的声明类型。
  5. 检查 Token 的有效性。如果 Token 无效或其中的声明信息无效(如过期),将返回一个包含错误信息的 fmt.Errorf

  6. 返回解析后的声明信息 mapClaims

    • mapClaims 是一个指向解析后的声明信息的指针。1. 接收一个 JWT Token 字符串 tokenString 作为参数。
  7. 使用 jwt.ParseWithClaims 函数解析 JWT Token,并将解析后的声明存储在 claims 变量中。

    • 第一个参数 tokenString 是要解析的 JWT Token 字符串。
    • 第二个参数 &jwt.MapClaims{} 是一个空的 jwt.MapClaims 对象指针,用于存储解析后的声明信息。
    • 第三个参数是一个回调函数,用于提供用于验证签名的密钥。在这里,回调函数返回固定的 secretKey(密钥)。
  8. 检查解析过程中的错误。如果解析失败,将返回 nil 和解析过程中的错误。

  9. 将声明信息的类型断言为 *jwt.MapClaims,并将其存储在 mapClaims 变量中。

    • claims.Claims 是一个 jwt.Claims 接口类型,包含了解析后的声明信息。
    • 通过将 claims.Claims 类型断言为 *jwt.MapClaims,可以将其转换为具体的声明类型。
  10. 检查 Token 的有效性。如果 Token 无效或其中的声明信息无效(如过期),将返回一个包含错误信息的 fmt.Errorf

  11. 返回解析后的声明信息 mapClaims

    • mapClaims 是一个指向解析后的声明信息的指针。

总结

用户认证其实就是登陆的过程。如何有效的进行用户身份验证,减轻服务器访问压力。对于敏感内容需要二次认证,还有减短其有效时间,以保证用户信息的安全。