网站关键词挖掘工具,网建安,快速建设网站服务,天津网站建设教程go-zero JWT鉴权
还记得我们之前登录功能#xff0c;返回的信息是token吗#xff1f; 这个token其实就是JSON Web Token简称JWT,它是一种开放标准#xff08;RFC 7519#xff09;#xff0c;用于在网络应用环境间安全地传递声明信息。
它是一种基于 JSON 的令牌#xf…go-zero JWT鉴权
还记得我们之前登录功能返回的信息是token吗 这个token其实就是JSON Web Token简称JWT,它是一种开放标准RFC 7519用于在网络应用环境间安全地传递声明信息。
它是一种基于 JSON 的令牌由三部分组成头部Header、载荷Payload和签名Signature。签名是将头部和载荷组合后使用密钥生成的哈希值用于验证令牌的真实性。
一、配置Auth
Auth是 jwt 密钥过期等信息配置的 golang 结构体名称 所以我们要在user-api.yaml中配置Auth
Auth:AccessSecret: sss12345678 # AccessSecret的值要是8位以上的字符串AccessExpire: 10000 #AccessExpire是过期时间单位是秒
注意Auth的名字要和jwt: Auth 这个传入的一样。
接在我们在config.go文件里面定义用来解析配置文件的结构体
type Config struct {rest.RestConfMysqlDb struct {DbSource string json:dbSource}//增加Auth 结构体Auth struct { AccessSecret stringAccessExpire int64}
}
到这边我们初步配置就完成了
二、生成JWT
为了简化操作我们直接在loginlogic.go 代码中添加生成token的方法
/*
secretKey string: 用于加密 JWT 的密钥通常是一个足够复杂的随机字符串确保 JWT 不易被伪造。
iat int64: 表示 JWT 的签发时间Issued At为一个时间戳通常是当前时间的 UNIX 时间戳以秒为单位可以通过 time.Now().Unix() 获得。
seconds int64: 表示 JWT 的有效时长单位为秒。这是通过 iat 参数计算出 JWT 的过期时间exp。
username string: 自定义有效载荷通常是是使用用户唯一性的数据作为载体这里我们为了方便演示使用了username。
*/func getJwtToken(secretKey string, iat, seconds int64,username string) (string, error) {// 创建一个 MapClaims 类型的声明claims : make(jwt.MapClaims)// 计算过期时间claims[exp] iat seconds // 设置 JWT 的过期时间exp通常需要一个 UNIX 时间戳claims[iat] iat // 设置签发时间iatclaims[username] username // 自定义的负载payload可以设置为任何信息例如用户名、用户ID等// 创建新的 JWTtoken : jwt.New(jwt.SigningMethodHS256) // 使用 HMAC SHA256 签名方法创建新的 JWT// 将声明分配给 JWTtoken.Claims claims// 使用 secretKey 签名JWT并返回生成的字符串和错误如果有return token.SignedString([]byte(secretKey))
}JWT有三种加密方式分别是 SigningMethodHS256、SigningMethodHS384、SigningMethodHS512 这里我们用的是hs256
然后修改Login中的代码添加token生成功能。
go-zero中jwt的传递是在HTTP请求添加名为Authorization的header形式如下 Authorization: Bearer token 注意 Bearer token 之间有空格
func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {// todo: add your logic here and delete this line//因为我们目前还没涉及到jwt鉴权所以先把token当面message使用userModel : l.svcCtx.UserModeluser, _ : userModel.FindOneByUsername(l.ctx, req.Username)//从配置文件中获取secret 、secret secret : l.svcCtx.Config.Auth.AccessSecretexpire : l.svcCtx.Config.Auth.AccessExpire//生成jwt tokentoken, err : getJwtToken(secret, time.Now().Unix(), expire, req.Username)if err ! nil {return nil, errors.New(4, token生成失败)}//查询username判断是否有数据if user ! nil {//如果有数据密码是否和数据库匹配if req.Password user.Password {return types.LoginResponse{Token:Bearer token, //开头添加Bearer }, nil} else {return nil, errors.New(5, 密码错误)}} else {return nil, errors.New(6, 用户未注册)}
} 注意从后面我们响应信息默认使用 xhttp.JsonBaseResponseCtx() 即zeromicro的x库 运行项目测试结果如下
JWT验证go-zero自动帮我们做了所以我们不需要单独的去写一个验证方法。
三、使用jwt
注册和登录的功能已经实现现在我们来实现用户的查询和更新功能这两个功能在正常的业务逻辑中都是需要通过登录实现的也就是说需要使用jwt来验证和传递信息。
现在我们来编辑新的api文件并演示如何启用jwt, 在api中我们通过可选参数【jwtAuth 】来控制是否启用 JWT。具体实现如下
syntax v1/*
省略注册和登录
*///因为我们想要通过jwt来传递数据所以我们不需求请求信息
type (GetInfoResponse {Username string json:username Password string json:password }
)type (//更新数据我们就简单修改下密码UpdataRequest {Password string json:password }UpdataResponse {Message string json:message}
)server (group: userprefix: /v1jwt: Auth //开启jwt
)
service user-api {handler GetInfoHandler// 不需要请求信息post /getinfo returns (GetInfoResponse)handler UpdataHandlerpost /updata (UpdataRequest) returns (UpdataResponse)
}
接下来我们使用goctl生成新的代码
goctl api go --api user.api --dir ./我们来看下go-zero是怎么帮我们实现jwt认证的我们先看下routes.go 文件。
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {/*省略注册和登录的代码*/server.AddRoutes([]rest.Route{{Method: http.MethodPost,Path: /getinfo,Handler: getinfo.GetInfoHandler(serverCtx),},},//开启jwtrest.WithJwt(serverCtx.Config.Auth.AccessSecret),rest.WithPrefix(/v1),)server.AddRoutes([]rest.Route{{Method: http.MethodPost,Path: /updata,Handler: updata.UpdataHandler(serverCtx),},},//开启jwtrest.WithJwt(serverCtx.Config.Auth.AccessSecret),rest.WithPrefix(/v1),)
}goctl 帮我在/getinfo和/updata两个路由使用rest.WithJwt开启了jwt验证。
四、获取载体信息
1. 实现信息查询
我们首先去实现查询和更新这两个业务逻辑到logic目录下打开getinfologic.go 修改代码如下
func (l *GetInfoLogic) GetInfo() (resp *types.GetInfoResponse, err error) {// todo: add your logic here and delete this line//使用l.ctx.Value() 来获取jwt的内容username : l.ctx.Value(username).(string)usermodel : l.svcCtx.UserModeluser, err : usermodel.FindOneByUsername(l.ctx, username)if err ! nil err ! model.ErrNotFound {return nil, errors.New(0, 数据库连接失败)}if user nil {return nil, errors.New(11, 查询失败没有该用户信息)}return types.GetInfoResponse{Username: user.Username,Password: user.Password,}, nilreturn
}在go-zero中使用l.ctx.Value()来获取数据 这里我们使用的username其实就是我们获取token的时候传入的自定义payload 总而言之就是你传什么字段就获取什么字段。因为Value()是any类型所以需要对它进行断言。
我们先不传递Authorization的值运行项目测试一下可以发现直接报错了
我们可以看下运行日志提示我们认证失败没有token 现在添加Authorization的值再测试下可以看到成功执行
2. 实现信息修改
func (l *UpdataLogic) Updata(req *types.UpdataRequest) (resp *types.UpdataResponse, err error) {// todo: add your logic here and delete this lineusername : l.ctx.Value(username).(string)usermodel : l.svcCtx.UserModeluser, err : usermodel.FindOneByUsername(l.ctx, username)if err ! nil err ! model.ErrNotFound {return nil, errors.New(0, 数据库连接失败)}if user nil {return nil, errors.New(11, 查询失败没有该用户信息)}newUser : model.Users{Id: user.Id,Username: user.Username,Password: req.Password, //使用请求响应的密码CreatedAt: user.CreatedAt,}err usermodel.Update(l.ctx, newUser)if err ! nil {return nil, errors.New(12, 数据更新失败)}return types.UpdataResponse{Message: 数据更新成功,}, nil}运行项目测试 五、JWT 认证失败自定义处理返回
刚刚我们测试了一下JWT认证失败的情况它直接给我们返回来401代码这显然不是我们想要的现在我们来看下JWT 认证失败自定义处理返回。
我们需要在main中定义一个callback,并注册到服务中 , rest.WithUnauthorizedCallback 会全局捕捉jwt认证失败的请求 server : rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(func(w http.ResponseWriter, r *http.Request, err error) {
接下来我们来具体实现它:
//定义一个返回信息
func unauthorizedHandler(w http.ResponseWriter, r *http.Request, err error) {xhttp.JsonBaseResponse(w, 认证失败:err.Error())
}func main() {/*....*/
// 在main函数中添加一个callbackserver : rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(unauthorizedHandler))// 自定义处理返回}))/*....*/
}运行项目再次测试认证失败的情况