使用golang开发后端api,使用的是gin框架。上线之后需要用到权限控制,就得先把登录功能加上。 添加登录过程中,引入了gin的sessions间件 : https://github.com/
顺晟科技
2021-06-16 10:27:09
335
JSON网络令牌,全称JWT,是一种跨域认证解决方案,属于开放标准。它指定了一种令牌实现方法,目前用于前端分离项目和OAuth2.0业务场景。
什么是JWT?
JSON网络令牌,全称JWT,是一种跨域认证解决方案,属于开放标准。它指定了一种令牌实现方法,目前用于前端分离项目和OAuth2.0业务场景。
为什么我需要JWT?
在以前的一些web项目中,我们通常使用Cookie-Session模式来实现用户认证。相关流程大致如下:
用户在浏览器上填写用户名和密码,并将其发送到服务器
服务器验证用户名和密码后,会生成一个会话数据,保存当前用户的相关信息和对应的标识符(通常称为session_id)
当服务器返回响应时,它将前一步的session_id写入用户浏览器的Cookie中
每个后续用户从浏览器发出的请求都会自动携带一个包含会话标识的Cookie
服务器可以通过请求中的session_id找到用户之前保存的会话数据,从而获得用户的相关信息。
该方案依靠客户端(浏览器)保存Cookie,需要将用户的会话数据存储在服务器上。
在移动互联网时代,我们的用户可以使用浏览器或应用程序来访问我们的服务。我们的网络应用程序可能分别部署在不同的端口上,有时我们需要支持第三方登录,因此Cookie-Session模式有些不够。
JWT是一种基于令牌的轻量级认证模式。服务器认证通过后,会生成一个JSON对象,签名后得到一个Token,然后发回给用户。用户只需携带此Token进行后续请求,服务器解密后即可获得用户的相关信息。
如果你想联系JWT原理,我推荐你读读阮一峰的JWT入门教程
生成JWT并解析JWT
在这里,我们直接使用JWT-go库来实现生成JWT和解析JWT的功能。
定义需求
我们需要定制自己的需求,以决定在JWT存储哪些数据。例如,我们规定用户名信息应该存储在JWT,因此我们定义了如下的我的声明结构:
//MyClaims自定义声明结构并嵌入jwt。标准索赔
//jwt。jwt包中包含的标准声明仅包含官方字段
//我们需要在这里记录一个额外的用户名字段,所以我们需要定制结构
//如果想保存更多信息,可以添加到这个结构中。
类型MyClaims结构{
用户名字符串“json:”用户名“
jwt。标准索赔
}
然后我们定义JWT的到期时间。这里,以2小时为例:
const TokenExpireDuration=时间。小时* 2
接下来,您需要定义秘密:
Var MySecret=[]byte('夏天悄悄过去')
生成JWT
//GenToken生成JWT
func GenToken(用户名字符串)(字符串,错误){
//创建我们自己的声明
c :=我的索赔{
用户名',//自定义字段
jwt。标准索赔{
:次到期。现在()。add (tokenexpireduration)。UNIX(),//到期时间
发行者: '我项目',//发行者
},
}
//使用指定的签名方法创建签名对象
token :=jwt。NewWithClaims(jwt。签名方法S256,c)
//使用指定的秘密签名并获取完整的编码字符串标记
返回令牌。签名戒指(我的秘密)
}
解析JWT
//解析令牌解析JWT
func ParseToken(标记字符串)(*“我的索赔”,错误){
//解析令牌
token,err :=jwt。parsewitchclaims(TokenString,MyClaims{},func(token *jwt。Token) (i接口{},err error) {
返回我的密码,零
})
如果出错!=零{
返回零,错误
}
如果是理赔,ok :=token . claims .(* my claims);Ok令牌。有效的{//验证令牌
退货索赔,无
}
返回零,错误。新('无效令牌')
}
在gin框架中使用JWT
首先,我们注册一个路由/授权来提供获取令牌的外部通道:
r.POST('/auth ',authHandler)
我们的授权处理程序定义如下:
func authHandler(c *gin。上下文){
//用户发送用户名和密码
var用户用户信息
err :=c . ShouldBind(用户)
如果出错!=零{
c.JSON(http。StatusOK,gin。H{
代码' : 2001,
Msg': '无效参数',
})
返回
}
//验证用户名和密码是否正确
如果用户。Username=='q1mi '用户。密码==' q1mi123 ' {
//生成令牌
令牌字符串,_ :=GenToken(用户。用户名)
c.JSON(http。StatusOK,gin。H{
代码' : 2000,
msg': '成功',
数据' : gin。H{'token': tokenString},
})
返回
}
c.JSON(http。StatusOK,gin。H{
代码' : 2002,
消息' : '验证失败',
})
返回
}
用户通过上面的接口获得Token后,会携带Token,然后请求我们的其他接口。此时,有必要验证这些请求的令牌。显然,我们应该实现一个验证令牌的中间件,其实现如下:
//基于jwt的jwtuthimedium认证中间件
func JWTAuthMiddleware()func(c * gin。上下文){
返回func(c *gin。上下文){
//客户端携带Token有三种方式:1。把它放在请求头2。放入请求正文3。把它放在URI
//假设令牌放置在报头的授权中,并从承载开始
//这里的具体实现要根据你的实际业务情况来决定
授权头:=c . request . header . get(' Authorization ')
if authHeader=='' {
c.JSON(http。StatusOK,gin。H{
代码' : 2003,
Msg': '请求头中授权为空',
})
c.中止()
返回
}
//除以空间
parts :=字符串。SplitN(authHeader,' ',2)
如果!(len(parts)==2 parts[0]=='持票人'){
c.JSON(http。StatusOK,gin。H{
代码' : 2004,
Msg': '请求头中授权格式错误',
})
c.中止()
返回
}
//parts[1]是获取的标记字符串,我们使用之前定义的解析JWT的函数来解析它
mc,err :=ParseToken(parts[1])
如果出错!=零{
c.JSON(http。StatusOK,gin。H{
代码' : 2005,
消息' : '无效令牌',
})
c.中止()
返回
}
//将当前请求的用户名信息保存到请求的上下文c中
c.设置('用户名',mc。用户名)
C.Next() //后续处理函数可以使用c.Get('username ')获取当前请求的用户信息
}
}
注册一个/home路由,并发送请求进行验证。
r.GET('/home ',JWTAuthMiddleware(),homeHandler)
func homeHandler(c *gin。上下文){
username :=c . MustGet(' username ')。(字符串)
c.JSON(http。StatusOK,gin。H{
代码' : 2000,
msg': '成功',
数据' : gin。H{ '用户名' :用户名},
})
}
如果不想自己实现以上功能,也可以在Github上使用别人打包的包
28
2021-08
28
2021-08
28
2021-08
28
2021-08
28
2021-08
28
2021-08