jwt完成认证的流程能讲一下吗?
来源:7-29 商城服务对接搜索服务 (二)

404_
2023-09-01
老师,我看本项目中使用了jwt,能讲一下在本项目中jwt的认证流程 吗?
7回答
-
少林码僧
2023-09-01
token的校验是使用gin的中间件实现的,代码所在文件为:
https://gitee.com/phper95/shop-main/blob/master/routers/router.go
authApiv1 := r.Group("/api/v1").Use(middleware.AppJwt())
所以需要鉴权的接口都使用了
middleware.AppJwt()
这个中间件当用户请求/api/v1这个路径的后端接口时,就会经过
middleware.AppJwt()
这个中间件的校验。middleware.AppJwt()
中间件的具体实现如下:func AppJwt() gin.HandlerFunc {
return func(c *gin.Context) {
var data interface{}
var appG = app.Gin{C: c}
mytoken := c.Request.Header.Get("Authorization")
if len(mytoken) < bearerLength {
appG.Response(http.StatusUnauthorized, constant.ERROR_AUTH, data)
c.Abort()
return
}
token := strings.TrimSpace(mytoken[bearerLength:])
usr, err := jwt.ValidateToken(token)
if err != nil {
global.LOG.Error(err)
appG.Response(http.StatusUnauthorized, constant.ERROR_AUTH_CHECK_TOKEN_FAIL, data)
c.Abort()
return
}
c.Set(constant.AppAuthUser, usr)
c.Next()
}
}首先从Header的Authorization字段中获取服务端下发的jwt,再从jtw中截取出token,使用
jwt.ValidateToken()
方法进行校验,校验通过调用c.Next()继续执行web请求,调用不通过调用c.Abort()
终止请求。10 -
少林码僧
2023-09-01
由于Token的颁发在业务服务实现,而校验在网关服务实现,就需要在这两个服务中都需要配置和维护秘钥。为了简化秘钥管理,可以把token 的生成也放到网关服务实现:
10 -
少林码僧
2023-09-01
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(
.
)分隔,就可以返回给用户了。客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。
此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息
Authorization
字段里面。Authorization: Bearer <token>
另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。
JWT也并非绝对安全的,需要注意下面几个问题:
JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑;
JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证;
为了避免被盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
通常在实际业务中JWT的鉴权流程如下:
用户认证流程:用户向网关发送登录认证请求,网关将请求转发给认证服务。认证服务校验用户登录信息(用户密码、短信及图片验证码)等信息之后,如果校验成功颁发一个token令牌给该用户(这个令牌可以是JWT令牌)
网关级别访问鉴权:当用户访问系统内的其他业务服务接口时,需要携带登录认证的时候颁发的JWT token。网关验证JWT token的合法性,如果token不合法,则返回接口访问权限不足。如果token合法,则将请求按照网关的路由规则转发至相应的服务。
服务级别访问鉴权:网关级别的访问鉴权只是鉴别了JWT令牌的合法性,初步认定你是这个系统的用户,但是作为系统的用户并不意味着你可以访问所有的服务接口。通常基于用户的角色分类有更严格的访问权限划分。
所以通常网关层面除了转发请求之外需要做两件事:一是校验JWT令牌的合法性,二是从JWT令牌中解析出用户身份,并在转发请求时携带用户身份信息。这样系统内的其他业务服务在收到转发请求的时候,根据用户的身份信息判断决定该用户可以访问哪些接口。
10 -
少林码僧
2023-09-01
1.头部(header)
Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。
{
"alg": "HS256",
"typ": "JWT"
}上面json中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。
最后,将上面的 JSON 对象使用 Base64URL转成字符串,注意, Base64URL跟 Base64是有区别的,JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符
+
、/
和=
,在 URL 里面有特殊含义,所以需要使用 Base64URL 对这些特殊字符进行处理:=
被省略、+
替换成-
,/
替换成_
。2.负载(payload)
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用(注意不是强制的):
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
除了官方字段,你还可以在这个部分定义私有字段,比如:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
这个 JSON 对象也要使用 Base64URL转成字符串。
3.签名(Signature)
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)10 -
少林码僧
2023-09-01
首先需要理解jwt认证的出现是为了解决传统认证方式的不足,那么传统认证方式有哪些?又有哪些不足呢?
首先是Session认证:
服务提供的登录和数据访问接口是基于HTTP的,而HTTP协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么用户的每次请求都要进行用户认证,因为根据http协议,我们并不能知道请是哪个用户发出的,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。
Session认证的问题主要有3个:
Session一般存储在服务器的内存中,所以同一个用户请求需要确保每次访问到同一台服务器上,特别是如果多个服务需要共享session实现单点登录的情况是没法扩展的;
基于浏览器的cookie导致安全性低,一旦cookie被截获,用户就会很容易受到CSRF跨站请求伪造的攻击
为了解决这个问题就出现了基于Token的鉴权机制,用户通过用户名和密码登录成功后服务端会基于用户信息下发一个token令牌返回给客户端,客户端每次请求时带上这个Token令牌到服务端进行验证。通常服务端会把token保存到redis等缓存服务中来进行签名验证,而且token是有有效期的,在一定程度上提高了安全性。
而JWT其实就是在Token鉴权的基础上进行了标准化或者说规范化。JWT是JSON Web Token的缩写,是目前最流行的跨域认证解决方案。
JWT有3部分构成:
10 -
少林码僧
2023-09-01
由于字数限制,只能分开回答,可以从后面向前看
00 -
少林码僧
2023-09-01
在shop-main服务中没有网关层,当然,网关本质上也是一个http服务,通常使用nginx和openresity等web服务来实现。
所以在shop-main服务中,整个token的下发和校验都是在shop-main服务中实现的:
首先是登录下发token,具体代码在https://gitee.com/phper95/shop-main/blob/master/internal/controllers/front/LoginController.go文件中
// @Title 登录
// @Description 登录
// @Success 200 {object} app.Response
// @router /admin/login [post]
func (e *LoginController) Login(c *gin.Context) {
var (
param params.HLoginParam
appG = app.Gin{C: c}
)
paramErr := app.BindAndValidate(c, ¶m)
if paramErr != nil {
appG.Response(http.StatusBadRequest, paramErr.Error(), nil)
return
}
userService := wechat_user_service.User{HLoginParam: ¶m}
user, err := userService.HLogin()
if err != nil {
appG.Response(http.StatusInternalServerError, err.Error(), nil)
return
}
ttl := time.Hour * 24 * 100
token, _ := jwt.GenerateAppToken(user, ttl)
appG.Response(http.StatusOK, constant.SUCCESS, gin.H{
"token": token,
"expires_time": time.Now().Add(ttl).Unix(),
})
}登录成功后使用
jwt.GenerateAppToken(user, ttl)
生成token并范湖给客户端。00
相似问题