浅谈json web token及应用

Json Web Token (JWT),是一个很是轻巧的规范,这个规范容许在网络应用环境间客户端和服务器间较安全的传递信息。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT通常被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。html

image

在web应用中,咱们提供的API接口,经过GET或者POST方式调用,在调用过程当中,就存在着接口认证及数据的安全性问题。例如以下问题:前端

一、请求来自哪里,身份是否合法?java

二、请求参数是否被篡改?python

三、客户端请求的惟一性,是否为重复请求攻击(RepeatAttack)?git

传统的Session认证方式

在传统的web应用中,服务端成功相应请求者,返回正常的response依赖于服务端经过一种存储机制把每一个用户通过认证以后的会话信息(session)记录服务器端,通常记录到内存、磁盘、数据库中,这种方式在请求量和用户量增多的时候无疑会增大服务端的开销;若是是记录在内存中,那每次请求都分发登到该机器上才能受权获取资源,那在分布式系统中就存在着问题;由于是基于Cookie的,若是Cookie被截获,攻击者会盗用身份信息进行发送恶意请求,也就是“跨站请求伪造”(CSRF)。github

基于token的认证方式

客户端用用户名和密码通过服务器认证以后,服务器会签发一个token返回给客户端,客户端存储token(通常存在请求头里),而且在以后的请求里附带此token,服务器每次会解签名token,验证经过则返回资源。另外服务端要支持CORS跨来源资源共享)策略,服务器处理完请求以后,会再返回结果中加上Access-Control-Allow-Origin。web

jwt的生成

token是接口的令牌,比如去衙门办事,“衙门口朝南开,有理无钱莫进来”。没有令牌就别想办事。token的验证方法不少,也生成了不少标准,jwt就是一种基于json的RFC 7519。该标准由三部分组成:面试

  • header算法

  • payloadspring

  • signature

header和payload通过base64编码后用点拼接起来。signature是把header和payload编码和拼接后通过加密算法加密,加密时还要一个密码,这个密码保存在服务器端。大体示意图以下:

image

Header:

head由两部分组成,一个是token类型,一个是使用的算法,以下类型为jwt,使用的算法是HS256。固然,还有HS38四、HS512算法。

{
 "typ": "JWT",
 "alg": "HS256"
}

将以上json进行base64编码,固然编码前将json去格式化,如图:

image

生成的编码为:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

用go语言实现:

package main

import (
 "fmt"
 "encoding/base64"
)

func main() {

 head1 := `{"typ":"JWT","alg":"HS256"}`

 fmt.Println(base64.StdEncoding.EncodeToString([]byte(head1)))
}

Payload:

payload 里面是 token 的具体内容,这些内容里面有一些是标准字段,咱们也能够添加自定义内容。以下:

{
   "iss": "smallsoup",
   "iat": 1528902195,
   "exp": 1528988638,
   "aud": "www.smallsoup.com",
   "sub": "smallsoup@qq.com",
   "userId": "0418"
}

这里面的前五个字段都是由JWT的标准所定义的,在jwt标准中均可以找到。

  • iss: 该JWT的签发者

  • sub: 该JWT所面向的用户

  • aud: 接收该JWT的一方

  • exp(expires): 何时过时,这里是一个Unix时间戳

  • iat(issued at): 在何时签发的

最后一个userId表示了用户信息,为自定义字段,咱们也能够定义角色等其余字段。以上的json去格式化后的base64编码以下:

eyJpc3MiOiJzbWFsbHNvdXAiLCJpYXQiOjE1Mjg5MDIxOTUsImV4cCI6MTUyODk4ODYzOCwiYXVkIjoid3d3LnNtYWxsc291cC5jb20iLCJzdWIiOiJzbWFsbHNvdXBAcXEuY29tIiwidXNlcklkIjoiMDQxOCJ9

image

Signature:

JWT 的最后一部分是 Signature ,这部份内容有三个部分,先是用 Base64 编码的 header.payload ,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个至关因而一个密码,这个密码秘密地存储在服务端。

  • header

  • payload

  • secret

假设这里secret为mysecret,则用go语言实现代码以下:

package main

import (
 "fmt"
 "encoding/base64"
 "crypto/hmac"
 "crypto/sha256"
 "strings"
)

func main() {

 head1 := `{"typ":"JWT","alg":"HS256"}`

 head1Base64 := base64.StdEncoding.EncodeToString([]byte(head1))

 payload1 := `{"iss":"smallsoup","iat":1528902195,"exp":1528988638,"aud":"www.smallsoup.com","sub":"smallsoup@qq.com","userId":"0418"}`

 payload1Base64 := base64.StdEncoding.EncodeToString([]byte(payload1))

 encodedstring := head1Base64 + "." + payload1Base64

 hash := hmac.New(sha256.New, []byte("mysecret"))
 hash.Write([]byte(encodedstring))

 signature := strings.TrimRight(base64.URLEncoding.EncodeToString(hash.Sum(nil)), "=")

 fmt.Printf(signature)
}

运行结果为:

fjjbA93FTcE71hz_cyIzCUFYdTdyl9hA0w7Pa0ltduc

最后这个在服务端生成而且要发送给客户端的 Token 看起来像这样:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzbWFsbHNvdXAiLCJpYXQiOjE1Mjg5MDIxOTUsImV4cCI6MTUyODk4ODYzOCwiYXVkIjoid3d3LnNtYWxsc291cC5jb20iLCJzdWIiOiJzbWFsbHNvdXBAcXEuY29tIiwidXNlcklkIjoiMDQxOCJ9.fjjbA93FTcE71hz_cyIzCUFYdTdyl9hA0w7Pa0ltduc

实际上https://jwt.io/这个网站提供了这个能力,以及各类语言的生成token和解密token的库。

image

go语言生成token和解析token:

下面是go语言版的生成token和解析token的案例:

package main

import (
 "github.com/dgrijalva/jwt-go"
 "fmt"
)

func main() {

 hmacSampleSecret := []byte("mysecret")

 // Create a new token object, specifying signing method and the claims
 // you would like it to contain.
 token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
     "iss": "smallsoup",
     "iat": 1528902195,
     "exp": 1528988638,
     "aud": "www.smallsoup.com",
     "sub": "smallsoup@qq.com",
     "userId": "0418",
 })

 // Sign and get the complete encoded token as a string using the secret
 tokenString, err := token.SignedString(hmacSampleSecret)

 fmt.Println(tokenString, err)

 token, err = jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
   // Don't forget to validate the alg is what you expect:
   if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
     return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
   }

   // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
   return hmacSampleSecret, nil
 })

 if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
   fmt.Println(claims)
 } else {
   fmt.Println(err)
 }
}

具体能够了解github上如下代码的实现。

go get github.com/dgrijalva/jwt-go


本公众号免费**提供csdn下载服务,海量IT学习资源,**若是你准备入IT坑,励志成为优秀的程序猿,那么这些资源很适合你,包括但不限于java、go、python、springcloud、elk、嵌入式 、大数据、面试资料、前端 等资源。同时咱们组建了一个技术交流群,里面有不少大佬,会不定时分享技术文章,若是你想来一块儿学习提升,能够公众号后台回复【2】,免费邀请加技术交流群互相学习提升,会不按期分享编程IT相关资源。


扫码关注,精彩内容第一时间推给你

image

原文出处:https://www.cnblogs.com/liabio/p/11696077.html

相关文章
相关标签/搜索