初识JWT

JWT

1. 简介

  1. 介绍

JWT:JSON Web Token,通过数字签名的方式以JSON对象为载体,在不同的服务终端之间安全的传输信息

  1. 作用

JWT最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含JWT,系统在没错处理用户的请求之前,都要先进行JWT安全校验,通过之后在进行处理

  1. 组成

JWT由三部分组成,以"."进行拼接

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

三部分:

  • Header

    jwt的头部承载两部分信息:

    • 声明类型,这里是jwt

    • 声明加密的算法 通常直接使用 HMAC SHA256

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

    然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    
  • Payload

    载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

    声明(claims)

    • 标准中注册的声明

      iss: jwt签发者

      sub: jwt所面向的用户

      aud: 接收jwt的一方

      exp: jwt的过期时间,这个过期时间必须要大于签发时间

      nbf: 定义在什么时间之前,该jwt都是不可用的.

      iat: jwt的签发时间

      jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

      java代码对应的声明
      setIssuer: sets the iss (Issuer) Claim
      setSubject: sets the sub (Subject) Claim
      setAudience: sets the aud (Audience) Claim
      setExpiration: sets the exp (Expiration Time) Claim
      setNotBefore: sets the nbf (Not Before) Claim
      setIssuedAt: sets the iat (Issued At) Claim
      setId: sets the jti (JWT ID) Claim
      
    • 公共的声明

    • 私有的声明

      {
        "sub": "1234567890",
        "name": "John Doe",
        "admin": true
      }
      
  • Signature

    jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

    • header (base64后的)
    • payload (base64后的)
    • secret

    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

    // javascript
    var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
    
    var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    

2. 使用

  1. 依赖

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    
  2. 测试

    public class Test {
        private long time = 1000 * 60 * 60 * 24;
        //    签名
        private String signatrue = "admin";
    
        //    加密
        @org.junit.Test
            public void jwt() {
    
            //   获取jwtbuilder对象
            JwtBuilder builder = Jwts.builder();
            //        设置Header
            String token = builder.setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                //               设置 payload
                .claim("username", "tzeao")
                .claim("role", "admin")
                //             主题
                .setSubject("admin-test")
                //                过期时间
                .setExpiration(new Date(System.currentTimeMillis() + time))
                //        id
                .setId(UUID.randomUUID().toString())
                //                设置Signature  两个参数分别是签名算法和自定义的签名Key(盐)
                .signWith(SignatureAlgorithm.HS256, signatrue)
                //                全部拼接起来
                .compact();
    
            System.out.println(token);
    
        }
    
        //    解密
        @org.junit.Test
            public void parse() {
            String tonke = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InR6ZWFvIiwicm9sZSI6ImFkbWluIiwic3ViIjoiYWRtaW4tdGVzdCIsImV4cCI6MTYyODcwMjc3OSwianRpIjoiOTA2MWQ4MGQtYjY0Ni00NzUyLWEyODctN2MzYTBjYzUyYmYyIn0.vCSbCfDrnduxHxViOOLZxs98ZjhMhm6hwvq_9MEKlFI";
            JwtParser parser = Jwts.parser();
            //        通过签名来解密
            Jws<Claims> jws = parser.setSigningKey(signatrue).parseClaimsJws(tonke);
            //        这个 === payload
            Claims body = jws.getBody();
            Object o = body.get("username");
    
            JwsHeader header = jws.getHeader();
            String signature = jws.getSignature();
    
            System.out.println(signature);
            System.out.println(header+o.toString());
    
        }
    }
    
  3. 可以使用这些实例直接添加Header header = Jwts.header();Claims claims = Jwts.claims();
    

3. SpringBoot 整合JWT

  1. 创建实体类

    @Datapublic class User {    private String username;    private String password;    private String token;}
    
  2. 控制器

    @RestControllerpublic class UserController {    private final String USERNAME = "admin";    private final String PASSWORD = "123123";// 返回给前端的token    @GetMapping("/login")    public User login(User user) {        if (USERNAME.equals(user.getUsername()) && PASSWORD.equals(user.getPassword())) {//            添加token            user.setToken(JWTUtil.craterToken());            return user;        }        return null;    }}
    
  3. JWT工具类

    public class JWTUtil {    private static final long time = 1000 * 60 * 60 * 24;    //    签名(盐)    private static final String signature = "!@#$#$%^%&$@#$@#$!#hjhh$!$!#$";    public static String craterToken() {        JwtBuilder builder = Jwts.builder();        String token = builder.setHeaderParam("typ", "JWT")                .setHeaderParam("alg", "HS256")                .claim("username", "admin")                .claim("role", "admin")                .setSubject("admin-test")                .setExpiration(new Date(System.currentTimeMillis() + time))                .setId(UUID.randomUUID().toString())                .signWith(SignatureAlgorithm.HS256, signature)                .compact();        return token;    }    //    校验token    public static boolean checkToken(String token) {        if (token == null) {            return false;        }        try {            Jws<Claims> jws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);        } catch (ExpiredJwtException e) {            return false;        }        return true;    }}
    
  4. 解决跨域问题

    @Configurationpublic class CrosConfig implements WebMvcConfigurer {    @Override    public void addCorsMappings(CorsRegistry registry) {        registry.addMapping("/**")                .allowedOriginPatterns("*")                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")                .allowCredentials(true)                .maxAge(3600)                .allowedHeaders("*");    }}
    
  5. 再创建一个项目编写前端

    链接: https://pan.baidu.com/s/1U91Y3MGbykBJxCdWY0FIyg 提取码: mhwh

  6. 注意 — 密钥的长度是有要求的16字节以上,不够就会报65509

  7. 控制器不使用@Controller,要使用@RestController 否则会报

    Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)

  8. 前端传token过来,进行验证

     @GetMapping("/checkToken")    public Boolean checkToken(HttpServletRequest request) {        //  通过前端传过来的请求头,取出token        String token = request.getHeader("token");        return JWTUtil.checkToken(token);    }
    

重新登录会重新创建token

posted @ 2022-03-30 19:04  Tzeao  阅读(185)  评论(0)    收藏  举报