- JWTJWT介绍什么场景适合用JWTJWT结构HeaderPayloadSignature合并jwt三部分JWT工作原理目录结构代码示例
JWT
JWT介绍
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
什么场景适合用JWT
- 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,允许用户访问该令牌允许的路由,服务和资源。Single Sign On(单点登录)是一种现在广泛使用JWT的功能,因为它的开销很小,并且能够在不同的域中轻松使用。
- 信息交换:JWT是在各方之间安全传输信息的好方法。 因为JWT可以签名 - 例如,使用公钥/私钥对 - 您可以确定发件人是他们所说的人。此外,由于使用Header和payload,Signature,您还可以验证内容是否未被篡改。
JWT结构
在紧凑的形式中,JWT由三个部分组成,用点(.)分隔,
- Header
- Payload
- Signature
因此,JWT通常如下所(token)
xxxxx.yyyyy.zzzzz (Header.Payload.Signature)`
Header
Header通常由两部分组成:令牌的类型,即JWT,以及正在使用的散列算法,例如HMAC SHA256或RSA。示例如下:
{ "alg": "HS256", "typ": "JWT"}`
然后,这个JSON被编码为Base64Url,形成JWT的第一部分。
Payload
token的第二部分是payload,其中包含claims。claims是关于实体(通常是用户)和其他数据的声明。claims有三种类型:registered, public, private claims
- Registered claims:这些是一组预定义声明,不是强制性的,但建议使用,以提供一组有用的,可互操作的声明。 其中一些是:iss(发行人),exp(过期时间),sub(主题),aud(观众)等。
- Public claims:这些可以由使用JWT的人随意定义。但是为避免冲突,应在IANA JSON Web令牌注册表中定义它们,或者将其定义为包含防冲突命名空间的URI。
- Private claims:这些声明是为了在同意使用它们的各方之间共享信息而创建的,并且既不是注册声明也不是公开声明。
Payload示例
{ "sub": "1234567890", "name": "John Doe", "admin": true}`
然后,Payload经过Base64Url编码,形成JSON Web Token的第二部分,数据虽然是不可串改,但是确实透明的
Signature
要创建签名部分,您必须采用base64Url编码header,base64Url编码的payload,secret,header中指定的算法,并对其进行签名
例如,如果要使用HMAC SHA256算法,将按以下方式创建签名:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)`
签名用于验证消息在此过程中未被更改,并且,在使用私钥签名的令牌的情况下,它还可以验证JWT的发件人是否是它所声称的人。
合并jwt三部分
输出是三个由点分隔的Base64-URL字符串,可以在HTML和HTTP环境中轻松传递,与SAML等基于XML的标准相比更加紧凑。
下面显示了一个JWT,它具有Header和Payload,并使Signature
//encodedeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c//decoded//Header 部分{ "alg": "HS256", "typ": "JWT"}//Payload 部分{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022}//Signature 部分HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret)
JWT工作原理
在身份验证中,当用户使用其凭据成功登录时,将返回JSON Web Token。由于Token是凭证,因此必须非常小心以防止出现安全问题。一般情况下,您不应该将令牌保留的时间超过要求。
每当用户想要访问受保护的路由或资源时,用户代理应该使用承载模式发送JWT,通常在Authorization Header中。Header的内容应如下所示:
Authorization: Bearer <token>
在某些情况下,这可以是无状态授权机制。服务器的受保护路由将在Authorization Header中检查有效的JWT,如果存在,则允许用户访问受保护的资源。如果JWT包含必要的数据,则可以减少查询数据库以进行某些操作的需要,尽管可能并非总是如此。
如果在Authorization Header中发送Token,则跨域资源共享(CORS)将不会成为问题,因为它不使用cookie。
下图显示了如何获取JWT并用于访问API或资源:
- 应用程序或客户端向授权服务器请求授权。这是通过其中一个不同的授权流程执行的。例如,典型的OpenID Connect兼容Web应用程序将使用授权代码流通过/oauth/authorize端点。
- 授予授权后,授权服务器会向应用程序返回访问Token。
- 应用程序使用访问Token来访问受保护资源(如API)。
请注意,使用签名Token,Token中包含的所有信息都会向用户或其他方公开,即使他们无法更改。这意味着您不应该在Token中放置秘密信息。
目录结构
主目录jwt
—— main.go
代码示例
main.go
// iris提供了一些基本的中间件,大部分用于学习曲线。//您可以将任何net/http请求的中间件与iris.FromStd(把net/http转化成iris context形式)包装器一起使用//适用于Golang新手的JWT net/http视频教程:https://www.youtube.com/watchv=dgJFeqeXVKw//这个中间件是唯一一个从外部源克隆的中间件:https://github.com/auth0/go-jwt-middleware//(因为它使用“context”来定义用户,但我们不需要这样,因此简单的iris.FromStd将无法按预期工作。)package main// $ go get -u github.com/dgrijalva/jwt-go// $ go run main.goimport ( "github.com/kataras/iris" "github.com/dgrijalva/jwt-go" jwtmiddleware "github.com/iris-contrib/middleware/jwt")func myHandler(ctx iris.Context) { //如果解密成功,将会进入这里,获取解密了的token token := ctx.Values().Get("jwt").(*jwt.Token) //或者这样 //userMsg :=ctx.Values().Get("jwt").(*jwt.Token).Claims.(jwt.MapClaims) // userMsg["id"].(float64) == 1 // userMsg["nick_name"].(string) == iris ctx.Writef("This is an authenticated request\n") ctx.Writef("Claim content:\n") //可以了解一下token的数据结构 ctx.Writef("%s", token.Signature)}func main() { app := iris.New() //jwt中间件 jwtHandler := jwtmiddleware.New(jwtmiddleware.Config{ //这个方法将验证jwt的token ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { //自己加密的秘钥或者说盐值 return []byte("My Secret"), nil }, //设置后,中间件会验证令牌是否使用特定的签名算法进行签名 //如果签名方法不是常量,则可以使用ValidationKeyGetter回调来实现其他检查 //重要的是要避免此处的安全问题:https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/ //加密的方式 SigningMethod: jwt.SigningMethodHS256, //验证未通过错误处理方式 //ErrorHandler: func(context.Context, string) //debug 模式 //Debug: bool }) app.Use(jwtHandler.Serve) //解释: //jwtmiddleware.New是配置中间件的错误返回,是否为调试模式,机密秘钥,加密模式等 //app.Use(jwtHandler.Serve) 是把中间件注册到处理程序中 //注册次中间件的路由,中间件每一次都会去获取header头Authorization字段用户判断 // 生成加密串过程 // token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ // "nick_name": "iris", // "email":"go-iris@qq.com", // "id":"1", // "iss":"Iris", // "iat":time.Now().Unix(), // "jti":"9527", // "exp":time.Now().Add(10*time.Hour * time.Duration(1)).Unix(), // }) // 把token已约定的加密方式和加密秘钥加密,当然也可以使用不对称加密 // tokenString, _ := token.SignedString([]byte("My Secret")) // 登录时候,把tokenString返回给客户端,然后需要登录的页面就在header上面附此字符串 // eg: header["Authorization"] = "bears "+tokenString app.Get("/ping", myHandler) app.Run(iris.Addr("localhost:3001"))}
