龙岗网站建设开发设计公司,做网站的图片要多少像素,网站做好了如何发布,wordpress源码最新文章目录 JWT安装JWT使用什么是Claims默认Claims自定义Claims生成JWT解析JWT 在gin框架中使用JWT获取Token渠道定义方法设置中间件注册路由 总结一下 JWT
JWT全称JSON Web Token是一种跨域认证解决方案#xff0c;属于一个开放的标准#xff0c;它规定了一种Token实现方式属于一个开放的标准它规定了一种Token实现方式目前多用于前后端分离项目和OAuth2.0业务场景下。
诺是不用这个JWT我们就需要去使用Cookie-Session模式实现用户认证。
流程为
用户在浏览器端填写用户名和密码并发送给服务端服务端对用户名和密码校验通过后会生成一份保存当前用户相关信息的session数据和一个与之对应的标识通常称为session_id服务端返回响应时将上一步的session_id写入用户浏览器的Cookie后续用户来自该浏览器的每次请求都会自动携带包含session_id的Cookie服务端通过请求中的session_id就能找到之前保存的该用户那份session数据从而获取该用户的相关信息。
这种方案依赖于客户端浏览器保存 Cookie并且需要在服务端存储用户的session数据。
在移动互联网时代我们的用户可能使用浏览器也可能使用APP来访问我们的服务我们的web应用可能是前后端分开部署在不同的端口有时候我们还需要支持第三方登录这下Cookie-Session的模式就有些力不从心了。
JWT就是一种基于Token的轻量级认证模式服务端认证通过后会生成一个JSON对象经过签名后得到一个Token令牌再发回给用户用户后续请求只需要带上这个Token服务端解密之后就能获取该用户的相关信息了。
安装JWT
我们使用 Go 语言社区中的 jwt 相关库来构建我们的应用例如https://github.com/golang-jwt/jwt
方法1:
go get github.com/golang-jwt/jwt/v4方法2
import github.com/golang-jwt/jwt/v4使用
什么是Claims
JWT token的 payload 部分是一个json串是要传递数据的一组声明这些声明被JWT标准称为claims。 JWT标准里面定义的标准claim包括
iss(Issuser)JWT的签发主体sub(Subject)JWT的所有者aud(Audience)JWT的接收对象exp(Expiration time)JWT的过期时间nbf(Not Before)JWT的生效开始时间iat(Issued at)JWT的签发时间jti(JWT ID)是JWT的唯一标识
默认Claims
如果我们直接使用JWT中默认的字段没有其他定制化的需求则可以直接使用这个包中的和方法快速生成和解析token。
// 用于签名的字符串
var mySigningKey []byte(liwenzhou.com)// GenRegisteredClaims 使用默认声明创建jwt
func GenRegisteredClaims() (string, error) {// 创建 Claimsclaims : jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), // 过期时间Issuer: qimi, // 签发人}// 生成token对象token : jwt.NewWithClaims(jwt.SigningMethodHS256, claims)// 生成签名字符串return token.SignedString(mySigningKey)
}// ParseRegisteredClaims 解析jwt
func ValidateRegisteredClaims(tokenString string) bool {// 解析tokentoken, err : jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {return mySigningKey, nil})if err ! nil { // 解析token失败return false}return token.Valid
}自定义Claims
除了以上标准声明以外。还有因为自己的需求不同来决定JWT中来保存那些数据。
例子我们规定在JWT中要存储username信息那么我们就定义一个MyClaims结构体。
// CustomClaims 自定义声明类型 并内嵌jwt.RegisteredClaims
// jwt包自带的jwt.RegisteredClaims只包含了官方字段
// 假设我们这里需要额外记录一个username字段所以要自定义结构体
// 如果想要保存更多信息都可以添加到这个结构体中
type CustomClaims struct {// 可根据需要自行添加字段Username string json:usernamejwt.RegisteredClaims // 内嵌标准的声明
}自定义签名的字符串的过期时间
const TokenExpireDuration time.Hour * 24自定义签名
// CustomSecret 用于加盐的字符串
var CustomSecret []byte(秋天秋天悄悄过去留下小秘密)生成JWT
根据自己的业务需要封装一个生成 token 的函数。
// GenToken 生成JWT
func GenToken(username string) (string, error) {// 创建一个我们自己的声明claims : CustomClaims{username, // 自定义字段jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),Issuer: my-project, // 签发人},}// 使用指定的签名方法创建签名对象token : jwt.NewWithClaims(jwt.SigningMethodHS256, claims)// 使用指定的secret签名并获得完整的编码后的字符串tokenreturn token.SignedString(CustomSecret)
}解析JWT
jwt.ParseWithClaims
// ParseToken 解析JWT
func ParseToken(tokenString string) (*CustomClaims, error) {// 解析token// 如果是自定义Claim结构体则需要使用 ParseWithClaims 方法token, err : jwt.ParseWithClaims(tokenString, CustomClaims{}, func(token *jwt.Token) (i interface{}, err error) {// 直接使用标准的Claim则可以直接使用Parse方法//token, err : jwt.Parse(tokenString, func(token *jwt.Token) (i interface{}, err error) {return CustomSecret, nil})if err ! nil {return nil, err}// 对token对象中的Claim进行类型断言if claims, ok : token.Claims.(*CustomClaims); ok token.Valid { // 校验tokenreturn claims, nil}return nil, errors.New(invalid token)
}在gin框架中使用JWT
获取Token渠道
我们注册一条路由/auth对外提供获取Token的渠道
r.POST(/auth, authHandler)定义方法
authHandler定义
func authHandler(c *gin.Context) {// 用户发送用户名和密码过来var user UserInfoerr : c.ShouldBind(user)if err ! nil {c.JSON(http.StatusOK, gin.H{code: 2001,msg: 无效的参数,})return}// 校验用户名和密码是否正确if user.Username q1mi user.Password q1mi123 {// 生成TokentokenString, _ : GenToken(user.Username)c.JSON(http.StatusOK, gin.H{code: 2000,msg: success,data: gin.H{token: tokenString},})return}c.JSON(http.StatusOK, gin.H{code: 2002,msg: 鉴权失败,})return
}设置中间件
// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {return func(c *gin.Context) {// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI// 这里假设Token放在Header的Authorization中并使用Bearer开头// 这里的具体实现方式要依据你的实际业务情况决定authHeader : c.Request.Header.Get(Authorization)if authHeader {c.JSON(http.StatusOK, gin.H{code: 2003,msg: 请求头中auth为空,})c.Abort()return}// 按空格分割parts : strings.SplitN(authHeader, , 2)if !(len(parts) 2 parts[0] Bearer) {c.JSON(http.StatusOK, gin.H{code: 2004,msg: 请求头中auth格式有误,})c.Abort()return}// parts[1]是获取到的tokenString我们使用之前定义好的解析JWT的函数来解析它mc, err : ParseToken(parts[1])if err ! nil {c.JSON(http.StatusOK, gin.H{code: 2005,msg: 无效的Token,})c.Abort()return}// 将当前请求的username信息保存到请求的上下文c上c.Set(username, mc.Username)c.Next() // 后续的处理函数可以用过c.Get(username)来获取当前请求的用户信息}
}注册路由
注册一个/home路由
r.GET(/home, JWTAuthMiddleware(), homeHandler)func homeHandler(c *gin.Context) {username : c.MustGet(username).(string)c.JSON(http.StatusOK, gin.H{code: 2000,msg: success,data: gin.H{username: username},})
}以上是自己定义的组件但是咱们可以使用使用Github上别人封装好的包比如https://github.com/appleboy/gin-jwt。用法是直接调用API就成。
总结一下
这种方式是最为基本的在go语言中Token。应用非常广泛。而以上的token是最为基础的Token在官方文案里有说明这种Token名字叫access Token。
我在这里简单说明一下
访问令牌acces Token是用于访问受保护资源的凭据。在上面的文章中有写道。传统的访问运用的Cookie-Session的模式但是想必大家肯定有所耳闻这种模式下的cookie值并不安全。在抓包工具下。会直接暴漏。
为了增加安全性此时就需要对信息进行加密。
访问令牌access Token是一个是一个字符串表示一个授权的客户端。这个字符串对于客户端来说是看不清的无法直接识别。
令牌中间件是由资源服务器和授权强制执行服务器授权持续时间和影响范围给所有者。
令牌的主要作用就是就是作为检索的标识符或者携带某种验证方式一些数据和一个签名类似于校验和。
在Token中有一个抽象层这个抽象层会赋予资源服务器权限单一实现。这个抽象层会提供一个各种方法的验证身份的方法。
访问令牌可以具有不同的格式、结构和方法基于资源的利用率(例如加密属性)服务器安全要求。访问令牌属性和访问受保护资源的方法不在本文的范围之内此规范和由配套规范定义。
我简单而言就是说对于token在保护信息的作用上又可以实现类似Java的springAOP 的拦截验证的机制。
我后续会给一个详细的Token的介绍。