Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

1 BCrypt密码加密

一.准备工作

  1. 任何应用考虑到安全,绝不能明文的方式保存密码。密码应该通过哈希算法进行加密。有很多标准的算法比如SHA或者MD5,结合salt(盐)是一个不错的选择。 Spring Security提供了BCryptPasswordEncoder类,实现Spring的PasswordEncoder接口使用BCrypt强哈希方法来加密密码。
  2. tensquare_user工程的pom引入依赖
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  3. 添加配置类
    我们在添加了spring security依赖后,所有的地址都被spring security所控制了,我们目前只是需要用到BCrypt密码加密的部分,所以我们要添加一个配置类,配置为所有地址都可以匿名访问。
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  4. 修改tensquare_user工程的Application, 配置bean
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

二.管理员密码加密

  1. 新增管理员密码加密
    修改tensquare_user工程的AdminService
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  2. 管理员登陆密码校验
    1. AdminDao增加方法定义
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    2. AdminService增加方法
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    3. AdminController增加方法
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

三.用户密码加密

  1. 用户注册密码加密
    1. 修改tensquare_user工程的UserService 类,引入BCryptPasswordEncoder
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    2. 修改tensquare_user工程的UserService 类的add方法,添加密码加密的逻辑
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  2. 用户登陆密码判断
    1. 修改tensquare_user工程的UserDao接口,增加方法定义
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    2. 修改tensquare_user工程的UserService 类,增加方法
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    3. 修改tensquare_user工程的UserController类,增加login方法
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

2 常见的认证机制

一.HTTP Basic Auth

  1. HTTP Basic Auth简单点说明就是每次请求API时都提供用户的username和password,简言之,Basic Auth是配合RESTful API 使用的最简单的认证方式,只需提供用户名密码即可,但由于有把用户名密码暴露给第三方客户端的风险,在生产环境下被使用的越来越少。因此,在开发对外开放的RESTful API时,尽量避免采用HTTP BasicAuth

二.Cookie Auth

  1. Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效

三.OAuth

  1. OAuth(开放授权)是一个开放的授权标准,允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
  2. OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的第三方系统(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容
  3. 这种基于OAuth的认证机制适用于个人消费者类的互联网产品,如社交类APP等应用,但是不太适合拥有自有认证权限管理的企业应用。

四.Token Auth

  1. 使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是
    这样的:
    1. 客户端使用用户名跟密码请求登录
    2. 服务端收到请求,去验证用户名与密码
    3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
    4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里
    5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
    6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

3 基于JWT的Token认证机制实现

一.什么是JWT

  1. JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

二.JWT组成

  1. 一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
  2. 头部(Header)
    头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    在头部指明了签名算法是HS256算法。 我们进行BASE64编码http://base64.xpcha.com/,编码后的字符串如下:
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  3. 载荷(playload)
    载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
    1. 标准中注册的声明(建议但不强制使用)
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    2. 公共的声明
      公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
    3. 私有的声明
      私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
  4. 签证(signature)
    jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

4 Java的JJWT实现JWT

一.什么是JJWT

  1. JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(ApacheLicense,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。

二.JJWT快速入门

  1. token的创建
    1. 创建maven工程,引入依赖
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    2. 创建类CreateJwtTest,用于生成token
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
      setIssuedAt用于设置签发时间
      signWith用于设置签名秘钥
    3. 测试运行,输出如下:
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  2. token的解析
    我们刚才已经创建了token ,在web应用中这个操作是由服务端进行然后发给客户端,客户端在下次向服务端发送请求时需要携带这个token(这就好像是拿着一张门票一样),那服务端接到这个token 应该解析出token中的信息(例如用户id),根据这些信息查询数据库返回相应的结果。
    创建ParseJwtTest
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  3. token过期校验
    有很多时候,我们并不希望签发的token是永久生效的,所以我们可以为token添加一个过期时间。
    创建CreateJwtTest2
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    setExpiration 方法用于设置过期时间
    修改ParseJwtTest
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    测试运行,当未过期时可以正常读取,当过期时会引发io.jsonwebtoken.ExpiredJwtException异常。
  4. 自定义claims
    我们刚才的例子只是存储了id和subject两个信息,如果你想存储更多的信息(例如角色)可以定义自定义claims 创建CreateJwtTest3
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    修改ParseJwtTest
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

5 十次方微服务鉴权

一.JWT工具类编写

  1. tensquare_common工程引入依赖(考虑到工具类的通用性)
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  2. 修改tensquare_common工程,创建util.JwtUtil
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  3. 修改tensquare_user工程的application.yml, 添加配置
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

二.管理员登陆后台签发token

  1. 配置bean .修改tensquare_user工程Application类
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  2. 修改AdminController的login方法
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

三.删除用户功能鉴权

  1. 需求:删除用户,必须拥有管理员权限,否则不能删除。
    前后端约定:前端请求微服务时需要添加头信息Authorization ,内容为Bearer+空格+token
  2. 修改UserController的findAll方法 ,判断请求中的头信息,提取token并验证权限。
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

四.使用拦截器方式实现token鉴权

  1. 如果我们每个方法都去写一段代码,冗余度太高,不利于维护,那如何做使我们的代码看起来更清爽呢?我们可以将这段代码放入拦截器去实现
  2. 添加拦截器
    Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:
    分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面)
    在preHandle中,可以进行编码、安全控制等处理;
    在postHandle中,有机会修改ModelAndView;
    在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。
    1. 创建拦截器类。创建 com.tensquare.user.filter.JwtFilter
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    2. 配置拦截器类,创建com.tensquare.user.ApplicationConfig
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  3. 拦截器验证token
    1. 修改拦截器类 JwtFilter
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
    2. 修改UserController的delete方法
      Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

6 发布信息验证Token

一.用户登陆签发 JWT

  1. 修改UserController,引入JwtUtil 修改login方法 ,返回token,昵称,头像等信息
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT

二.发布问题

  1. 修改tensquare_qa工程的QaApplication,增加bean
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  2. tensquare_qa工程配置文件application.yml增加配置:
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT
  3. 增加拦截器类 (参考上面的拦截器代码)
  4. 增加配置类ApplicationConfig (参考上面的配置类代码)
  5. 修改ProblemController的add方法
    Java项目 (SpringBoot+SpringCloud) 十次方:后端(六) 密码加密与微服务鉴权JWT