当前位置: 首页 > 工具软件 > go-saml > 使用案例 >

go-zero 成长之路—微服务电商实战系列(八、jwt鉴权)

印宏阔
2023-12-01

该系列源码已开源:micro-shop

1. 概述

什么是JWT

JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑而独立的方法,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。

什么时候应该使用JWT

  • 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单一登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。

  • 信息交换:JSON Web令牌是在各方之间安全地传输信息的一种好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。

为什么要使用JSON Web令牌 ?

  • 由于JSON不如XML冗长,因此在编码时JSON的大小也较小,从而使JWTSAML更为紧凑。这使得JWT是在HTMLHTTP环境中传递的不错的选择。

  • 在安全方面,只能使用HMAC算法由共享机密对SWT进行对称签名。但是,JWTSAML令牌可以使用X.509证书形式的公用/专用密钥对进行签名。与签署JSON的简单性相比,使用XML Digital Signature签署XML而不引入模糊的安全漏洞是非常困难的。

  • JSON解析器在大多数编程语言中都很常见,因为它们直接映射到对象。相反,XML没有自然的文档到对象的映射。与SAML断言相比,这使使用JWT更加容易。

  • 关于用法,JWT是在Internet规模上使用的。这突显了在多个平台(尤其是移动平台)上对JSON Web令牌进行客户端处理的简便性。

注意:
以上内容全部来自 jwt官网介绍

如果历史文章不是很清楚的,可通过如下传送门:

2. 如何使用 JWT

jwt鉴权一般在api层使用,在这里咱们是用在用户服务上面的。

具体步骤:

  • 第一步:生成服务 user api
  • 第二步:在登录时生成 jwt token
  • 第三步:在获取用户信息时验证 jwt token

第一步:生成服务 user api

添加配置定义

vim user/api/internal/config/config.go
type Config struct {
	rest.RestConf
	SecretKey string

	Auth struct {
		AccessSecret string
		AccessExpire int64
	}
}

配置yaml配置项

vim user/api/etc/user-api.yaml
# user 模块配置
Name: user-api
Host: 0.0.0.0
Port: 9001

SecretKey: C8xHnG6s
Auth:
  AccessSecret: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  AccessExpire: 64800

AccessSecret:生成jwt token的密钥,最简单的方式可以使用一个uuid值。
AccessExpirejwt token有效期,单位:秒

第二步:在登录时生成 jwt token

生成

vim user/api/internal/logic/login-logic.go
func (l *LoginLogic) GetToken(iat int64, secretKey string, payloads map[string]any, seconds int64) (string, error) {
	claims := make(jwt.MapClaims)
	claims["expTime"] = iat + seconds
	claims["iat"] = iat
	for k, v := range payloads {
		claims[k] = v
	}

	token := jwt.New(jwt.SigningMethodHS256)
	token.Claims = claims

	return token.SignedString([]byte(secretKey))
}

使用

payloads := make(map[string]any)
payloads["userIdentity"] = userData.UserIdentity

accessToken, tokenErr := l.GetToken(time.Now().Unix(), l.svcCtx.Config.Auth.AccessSecret, payloads, l.svcCtx.Config.Auth.AccessExpire)
if tokenErr != nil {
	return nil, tokenErr
}

第三步:在获取用户信息时验证 jwt token

编写user.api文件

vim user/api/user.api
type (

	UserInfoReq struct{}

	UserInfoResply {
		Code    int64         `json:"Code"`
		Message string        `json:"Message"`
		Data    *UserInfoItem `json:"Data"`
	}

	UserInfoItem {
		UserIdentity string `json:"UserIdentity"` // 用户唯一表哦是
		UserName     string `json:"UserName"`     // 用户名
		UserNick     string `json:"UserNick"`     // 用户昵称
		UserFace     string `json:"UserFace"`     // 用户头像地址
		UserSex      int64  `json:"UserSex"`      // 用户性别:0男,1女,2保密
		UserEmail    string `json:"UserEmail"`    // 用户邮箱
		UserPhone    string `json:"UserPhone"`    // 用户手机号
	}
)

@server(
	jwt: Auth
)

service user-api{
	@doc (
		summary: "用户信息"
	)
	@handler userInfo
	post /userinfo (UserInfoReq) returns (UserInfoResply)
}

jwt: Auth:开启jwt鉴权
如果路由需要jwt鉴权,则需要在service上方声明此语法标志
不需要jwt鉴权的路由就无需声明

3. 获取jwt token中携带的信息

go-zerojwt token解析后会将用户生成token时传入的kv原封不动的放在http.Request的Context中,因此我们可以通过Context就可以拿到你想要的值

vim user/api/internal/logic/user-info-logic.go
func (l *UserInfoLogic) UserInfo(req types.UserInfoReq) (resp *types.UserInfoResply, err error) {

	userIdentity := fmt.Sprintf("%v", l.ctx.Value("userIdentity"))
	
	// 这里的key和生成jwt token时传入的key一致
	logx.Infof("userId: %v",userIdentity)

	return &types.UserInfoResply{}, nil
}

4. 结束语

本篇文章介绍说明了在开发过程中会出现的一些清情况:

  • 什么是 JWT
  • 如何生成 JWT Token
  • 如何使 JWT Token 生效

另外,如果你感兴趣,非常欢迎你加入,我们一起来完成这个项目,为社区献出自己的一份力。

希望本篇文章对你有所帮助,谢谢。

 类似资料: