微服务安全与权限-JWT[01 介绍与简单使用]

237 阅读4分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

JWT

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

Json Web Token ,通过JSON形式作为Web应用的令牌,多方交互时需要通过这个令牌进行授权才能访问。 也可以对信息进行加密来信息交换防止被篡改。

为什么使用它

传统的Session认证

认证通过,将用户信息存入服务器的Session中,并将sessionid存放到浏览器的cookie当中。

当用户再次请求,会携带cookie,通过sessionid找到服务器中自己的 session信息。来进行验证是否登录。

20200912195917

Session的弊端

  • 每个用户认证都会在服务器存放对应的session,随着用户增多,服务器开销增大

  • session存放在一台服务器中,当下次请求时,还需要访问同一台服务器才能成功,这样就会限制分布式的能力,减弱了负载均衡

  • cookie被拦截,容易造成伪造cookie攻击

  • 前后端分离情况下更加复杂:

    • 用户一次请求要转发多次,每次都要携带sessionid,每次都要验证,增加服务器的负担

基于JWT

20200912201158

认证流程

1.前端通过Web表单将自己的用户名和密码发送到后端的接口。一般使用post请求或者SSL加密传输

2.后端核对用户名和密码成功后,将用户的id等信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成JWT。 ** token的格式**:head.payload.singurature

3.后端将JWT作为登录成功的结果返回给前端,前端将结果保存在浏览器中localStorage,退出登录时前端删除JWT

4.前端每次请求将JWT放入HTTP Header中的Authorization位,解决伪造攻击

5.后端检查JWT是否过期,检查签名是否正确,检查Token是否发送给自己。

6.验证通过,后端使用JWT中的用户信息来进行业务操作,并返回结果

优点

  • 简介:可以通过post参数或者http header发送,数据量小,传输速度快
  • 自包含:负载中包含了所有用户所需的信息,避免多次查询数据库
  • 以JSON加密的形式保存在客户端,因此JWT是跨语言,任何web形式都支持
  • 不需要在服务端保存会话信息,特别适用于分布式微服务

结构

1.令牌组成

token 的格式:xxxx.yyyy..zzzz

内容为:header.payload.singnature

  • header标头

    • 标头通常由两部分组成:令牌的类型和所使用的签名算法

    • 经过Base64编码后就会变成JWT格式的第一部分xxxx

      {
         //使用的加密方式为HS256
             "alg" : "HS256",
             //使用的令牌类型为JWT
             "typ" : "JWT" 
      }
      
  • Payload有效载荷

    • 自包含,包含声明。声明是有关实体(通常是用户)和其他数据的声明
    • 同样会Bse64编码处理,变成JWT格式的第二部分yyyy
  • 由于Base64不是加密,而是编码,所以如果被拦截通过解码就能获得全部用户信息,官方建议不要放置用户的敏感信息如密码

      {
           "sub" : "123456",
           "name" : "yzy",
           "admin" : true
      }
    
  • Singature签名

    • 签名需要使用编码后的header和payload以及后端提供的一个密钥,通过header指定的加密算法进行签名,作用是保证JWT没有被篡改

      • header指定了加密算法:HS256
      • 已知编码后的header和payload:xxxx.yyyy
      • 后端提供密钥 secret
      • 进行加密Signatrue = HMACSHA256(xxxx+"."+yyyy,secret)
    • 后端如何进行验证:获得JWT中的xxxx.yyyy与后端自己的密钥进行加密然后和JWT的zzzz进行对比是否相同

      • 当JWT被篡改时,无论xxxx,yyyy,zzzz的哪一部分发生改变,都不能验证通过

编码前

20200913110218

编码后

20200913110303

使用

1.导入依赖

<dependency>
           <groupId>com.auth0</groupId>
           <artifactId>java-jwt</artifactId>
           <version>3.4.0</version>
       </dependency>

2.手动模拟一个token的生成

Calendar instance = Calendar.getInstance();
       instance.add(Calendar.SECOND,20);           //设置20秒的时间
​
       String token = JWT.create().withClaim("username", "yy")   //设置payload的内容.可以有多个
              .withClaim("id", "yy")
              .withExpiresAt(instance.getTime()) //指定令牌过期时间
              .sign(Algorithm.HMAC256("1231231"));//设置签名
​
       System.out.println(token);

20200913140910

3.令牌的认证

JWTVerifier verifier = JWT.require(Algorithm.HMAC256("1231231")).build();   //创建一个相同算法的解密对象
       /*模拟前端的话就是将前端传过来的token让解密对象进行解码*/
       //解码token获得一个包含信息的对象
       DecodedJWT verify =verifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6Inl5MSIsImV4cCI6MTU5OTk3OTM4OCwidXNlcm5hbWUiOiJ5eSJ9.iQnJ4JnwEGWDEPYzrk-f_Rq8LCqPetm7kYYeAdoUweY");
       //对象中可以获取载体中的信息,此时是对象名,如果想要获得具体信息,可以.asString
       System.out.println(verify.getClaim("username"));
       System.out.println(verify.getClaim("id"));

异常

20200913143027

异常的次序:

  • 1.算法匹配(与生成JWT的算法不同)
  • 2.验证签名(与生成JWT的签名不同)
  • 3.过期时间(超时)
  • 4.失效的JWT(JWT信息不对)