Spring Boot整合JWT实现用户认证

之前初学了一下Spring Boot和JWT的内容,写了几篇小文章,但是杂乱无章,就重新整理了一下自己学习的东西,尽量写的足够详细,给像我一样刚刚接触这个内容的新手一个参考。这里附上代码的Github源码地址 ,参考的文献也附在这里[^footnote1][^footnote2]

初探JWT

  1. 什么是JWT JWT(Json Web Token),是一种工具,格式为XXXX.XXXX.XXXX的字符串,JWT以一种安全的方式在用户和服务器之间传递存放在JWT中的不敏感信息。
  2. 为什么要用JWT 设想这样一个场景,在我们登录一个网站之后,再把网页或者浏览器关闭,下一次打开网页的时候可能显示的还是登录的状态,不需要再次进行登录操作,通过JWT就可以实现这样一个用户认证的功能。当然使用Session可以实现这个功能,但是使用Session的同时也会增加服务器的存储压力,而JWT是将存储的压力分布到各个客户端机器上,从而减轻服务器的压力。
  3. JWT长什么样 JWT由3个子字符串组成,分别为Header,Payload以及Signature,结合JWT的格式即:Header.Payload.Signature。(Claim是描述Json的信息的一个Json,将Claim转码之后生成Payload)。
    • Header Header是由以下这个格式的Json通过Base64编码(编码不是加密,是可以通过反编码的方式获取到这个原来的Json,所以JWT中存放的一般是不敏感的信息)生成的字符串,Header中存放的内容是说明编码对象是一个JWT以及使用“SHA-256”的算法进行加密(加密用于生成Signature)
      {
      "typ":"JWT",
      "alg":"HS256"
      }
    • Claim Claim是一个Json,Claim中存放的内容是JWT自身的标准属性,所有的标准属性都是可选的,可以自行添加,比如:JWT的签发者、JWT的接收者、JWT的持续时间等;同时Claim中也可以存放一些自定义的属性,这个自定义的属性就是在用户认证中用于标明用户身份的一个属性,比如用户存放在数据库中的id,为了安全起见,一般不会将用户名及密码这类敏感的信息存放在Claim中。将Claim通过Base64转码之后生成的一串字符串称作Payload。
      {
          "iss":"Issuer —— 用于说明该JWT是由谁签发的",
          "sub":"Subject —— 用于说明该JWT面向的对象",
          "aud":"Audience —— 用于说明该JWT发送给的用户",
          "exp":"Expiration Time —— 数字类型,说明该JWT过期的时间",
          "nbf":"Not Before —— 数字类型,说明在该时间之前JWT不能被接受与处理",
          "iat":"Issued At —— 数字类型,说明该JWT何时被签发",
          "jti":"JWT ID —— 说明标明JWT的唯一ID",
          "user-definde1":"自定义属性举例",
          "user-definde2":"自定义属性举例"
      }
    • Signature Signature是由Header和Payload组合而成,将Header和Claim这两个Json分别使用Base64方式进行编码,生成字符串Header和Payload,然后将Header和Payload以Header.Payload的格式组合在一起形成一个字符串,然后使用上面定义好的加密算法和一个密匙(这个密匙存放在服务器上,用于进行验证)对这个字符串进行加密,形成一个新的字符串,这个字符串就是Signature。
    • 总结 这里写图片描述
  4. JWT实现认证的原理 服务器在生成一个JWT之后会将这个JWT会以Authorization : Bearer JWT 键值对的形式存放在cookies里面发送到客户端机器,在客户端再次访问收到JWT保护的资源URL链接的时候,服务器会获取到cookies中存放的JWT信息,首先将Header进行反编码获取到加密的算法,在通过存放在服务器上的密匙对Header.Payload 这个字符串进行加密,比对JWT中的Signature和实际加密出来的结果是否一致,如果一致那么说明该JWT是合法有效的,认证成功,否则认证失败。

JWT实现用户认证的流程图

这里写图片描述

JWT的代码实现

这里的代码实现使用的是Spring Boot(版本号:1.5.10)框架,以及Apache Ignite(版本号:2.3.0)数据库(有关Ignite和Spring Boot的整合可以查看之前写过的一篇文章 在Spring Boot上部署ignite数据库的小例子

这里写图片描述

}

- ` JwtFilter` 类
        这个类声明了一个JWT过滤器类,从Http请求中提取JWT的信息,并使用了"secretkey"这个密匙对JWT进行验证

/**

}



## 代码功能测试
本例使用Postman对代码进行测试,这里并没有考虑到安全性传递的明文密码,实际上应该用SSL进行加密
1. 首先进行一个新的测试用户的注册,可以看到注册成功的提示返回
    ![这里写图片描述](http://img.blog.csdn.net/20180310132536199?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbHRsMTEyMzU4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
2. 再让该用户进行登录,可以看到登录成功之后返回的JWT字符串
![这里写图片描述](http://img.blog.csdn.net/20180310132824675?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbHRsMTEyMzU4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
3. 直接申请访问` /secure/users/user` ,这时候肯定是无法访问的,服务器返回500错误
![这里写图片描述](http://img.blog.csdn.net/20180310132926995?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbHRsMTEyMzU4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
4. 将获取到的JWT作为Authorization属性提交,申请访问` /secure/users/user` ,可以访问成功
![这里写图片描述](http://img.blog.csdn.net/20180310133041807?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbHRsMTEyMzU4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

---
[^footnote1]: John Wu: JSON Web Token-在Web应用间安全地传递信息[EB/OL].[2018-03-02].http://blog.leapoahead.com/2015/09/06/understanding-jwt/ 

[^footnote2]: Aboullaite Mohammed: Spring Boot token authentication using JWT[EB/OL]. [2018-03-02].https://aboullaite.me/spring-boot-token-authentication-using-jwt/ ↩