JWT笔记

JWT的结构

# 1 令牌组成
- 1.标头(Header)
- 2.有效载荷(Payload)
- 3.签名(Signature)
- 因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz    Header.Payload.Signature
# 2 Header
- 标头通常由两部分组成:令牌的类型和所使用的签名算法,例如HMAC SHA256或ESA,它会使用Base64编码组成 JWT 结构的第一部分

- 注意:Base64是一种编码,也就是说,它可以被翻译回原来的样子,它不是一种加密过程
{
    "alg": "HS256",
    "typ": "JWT"
}
# 3 Payload
- 令牌的第二部分是有效负载,其中包含声明,声明是有关实体和其他数据的声明.同样的,它会使用Base64编码组成 JWT 结构的第二部分
- 不要在Payload里放入敏感信息!!
{
    "sub":"1234567890"
    "name":"Lebron Jordan"
    "admin": true
}
# 4 Signature
- 前面两部分是使用Base64进行编码的,即前端可以解开知道里面的信息, Signature 需要使用编码后的header和payload以及我们提供的一个密钥,然后使用header中指定的签名算法(Hs256)进行签名,签名的作用是保证JWT没有被篡改过
- 如 :
	HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret);
	
# 签名目的
- 最后一步签名的过程,实际上是对头部以及负载内容进行签名,防止内容被篡改.如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成的新的JWT,那么服务器会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的.如果要对新的头部和负载进行签名,在不知道服务器加密时使用的密钥的话,得出来的签名是不一样的.

# 信息安全问题
- Base64编码可逆,信息容易暴露

- 因此,在JWT中,不应该在负载里面加入任何敏感的信息.
# 5 放在一起
- 输出是三个由点分割的Base64-URL字符串,可以在HTML和HTTP环境中轻松传递这些字符串,与基于XML标准相比,它更紧凑.
- 简洁
- 可以通过URL,POST 参数或者在HTTP header发送,因为数据量小,传输速度快
- 自包含(Self-contained)  负载中包含了所有用户所需要的信息,避免了多次查询数据库

JWT的创建与使用

引入jwt依赖

        <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.3</version>
        </dependency>

创建

    void createJWT() {
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,100);
        String sign = JWT.create()
                .withClaim("userId", 21)//Payload
                .withClaim("username", "longda")
                .withExpiresAt(instance.getTime())  //指定令牌过期时间
                .sign(Algorithm.HMAC256("!@HKGfhjk"));//Signature
        System.out.println(sign);
    }

使用

 void TestSign(){
        //创建验证对象
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256("!@HKGfhjk")).build();

        DecodedJWT verify = verifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTc1NzE0NTksInVzZXJJZCI6MjEsInVzZXJuYW1lIjoibG9uZ2RhIn0.8zljCZy6Vg64spyMdY6JHDudImNmmFb8i33vMZ39dv4");
//对应生成的令牌
        System.out.println(verify.getClaim("userId").asInt());
        System.out.println(verify.getClaim("username").asString());

    }

封装JWT工具类

public class JWTUtil {

    private static final String Signature = "!@HKGfhjk";


    /**
     * 生成token
     * @param map
     * @return
     */
    public static String getToken(Map<String ,String> map){
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE,7);  //默认七天过期

        //创建jwt builder
        JWTCreator.Builder builder = JWT.create();
        //payload
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        //指定builder过期时间
        String token = builder.withExpiresAt(instance.getTime())
                .sign(Algorithm.HMAC256(Signature));//签名
        return token;
    }

    /**
     * 验证token  合法性
     */
    public static DecodedJWT vertify(String token){
        return JWT.require(Algorithm.HMAC256(Signature)).build().verify(token);
    }
//    /**
//     * 获取token信息方法
//     */
//    public static DecodedJWT getTokenInfo(String token){
//        DecodedJWT verify = JWT.require(Algorithm.HMAC256(Signature)).build().verify(token);
//        return verify;
//    }
}

业务中使用JWT

新建JWT拦截器

public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String ,Object> map = new HashMap<>();
        //获取请求头中的令牌
        String token = request.getHeader("token");
        try {
            DecodedJWT vertify = JWTUtil.vertify(token);
            return true;   //放行请求
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg","无效签名!");
        }catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","token过期!");
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","token算法不一致!");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg","token无效!");
        }
        map.put("state",false);//设置状态
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;
    }
}

配置拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/**")     //其他接口token验证
                .excludePathPatterns("/user/**");       //用户验证放行
    }
}

完成业务代码

    @PostMapping("/user/test")
    public Map<String ,Object> test(HttpServletRequest request){
        Map<String, Object> map = new HashMap<>();
        //处理业务逻辑
        String token = request.getHeader("token");
        DecodedJWT vertify = JWTUtil.vertify(token);
        log.info(vertify.getClaim("id").asString());
        log.info(vertify.getClaim("name").asString());
        map.put("state",true);
        map.put("msg","required success");
        return map;
    }

使用拦截器处理JWT的token验证可以减少代码的冗余

posted @ 2020-08-20 23:31  longda666  阅读(1)  评论(0)    收藏  举报