JWT详解

书欣 Java经验 发布时间:2023-01-09 11:19:20 阅读数:12373 1
下文笔者讲述JWT的相关简介说明,如下所示

JWT简介

JWT(Json Web Token)
   是使用json的方式
   作为web应用中的令牌
   用于在各方之间安全地将信息作为json对象创数
   在数据的传输过程中还可以完成数据的加密、签名等相关处理
 
JWT特点:
  1.跨语言:支持主流语言
  2.自包含:包含必要的所有信息,如用户信息和签名等
  3.易传递:很方便通过HTTP头部传递

以前的Session认证

Session 原始认证方式
原Session认证方式

原始认证方式的缺点

  1.用户量越多,服务器的开销越大
  2.cookie本身不安全
  3.在集群的情况下
      用户必须在同一服务器或通过session共享机制实现认证
  4.前后端分离增加了复杂性
      如:代理、路由网关

JWT认证模式简介

JWT认证流程

JWT认证流程图
jwt认证流程

JWT认证流程说明

首先,前端通过Web表单将自己的用户名和密码发送到后端。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探
后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个形同xxx.yyy.zzz字符串的JWT(Token)
后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可
前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
后端检查JWT是否存在,如存在验证其有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)
验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果

JWT优点

1.简单(Compact):可使用URL,POST参数或者在HTTP header中发送,数据量小,传输速度快
2.自包含(Self-contained):负载中包含用户所需要的信息,减少多次查询数据库-可减少数据库的压力
3.使用Token使用JSON加密的形式保存在客户端,即JWT与语言无关,所有web都支持
4.服务器端无需存储会话信息,所以JWT非常适用于分布式及微服务

JWT结构

JWT是一个Token
  它通常由 标头(header)、有效载荷(payload)、签名(signature)三部分组成

2.Header
  标头通常由两部分组成:
     令牌的类型(即JWT)和所使用的算法
     它会使用Base64编码组成JWT结构的第一部分
  注意事项:
    Base64是一种编码
    {
     "alg": "HS256",
     "typ": "JWT"
    }

3.payload
  令牌的第二部分是有效负载
    其中包含声明
      生命是有关实体(通常是用户和其他数据的声明)
  它使用Base64编码组成了JWT结构的第二部分
  注意事项:
     不要存放敏感的信息(如:password)
   {
    "id": "0008",
    "usename": "maomao",
    "admin":"true"
   }
 
4.signature
   前面的两部分都是Base64进行编码
    即前端可以解开知道里面的信息
    signature需要使用编码后的Header和payload以及自己提供的一个密钥(即盐值)
    使用header中指定的算法(HS256)进行签名

注意事项:
    签名的功能用于确保JWT没被篡改过
如: 
    HMASH256(base64urlEncode(header) + "." + base64urlEncode(payload),secret)
    即验证:
	   header + payload + 盐值 == signature数据
 

JWT中常见异常信息

AlgorithmMismatchException:
    算法不匹配异常(即使用的算法不一致)
SignatureVerificationException:
    签名不一致异常(即使用的签名不一致)
InvalidClaimException:
    失效的payload异常(即数据被篡改)
TokenExpiredException:
    令牌过期异常

Java版JWT示例

导入JWT依赖

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

编写JWT工具类

package com.java265.jwt.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;

/**
 * @description JWT token 工具类
 * 包含token的生成、验证、获取数据等...
 */
public class JWTUtils {

    // 私有签名 (盐)
    private static final String SECRET = "token!xie@yang";

    // 默认token存活时间
    private static final Integer DEFAULT_DAY = 7;

    /**
     * 获取默认时间的token数据
     *
     * @param map 数据,请勿传入用户敏感信息
     * @return token令牌
     */
    public static String getToken(Map<String, String> map) {
        return getToken(map, DEFAULT_DAY, SECRET);
    }

    /**
     * 获取指定时间的token数据
     *
     * @param map 数据,请勿传入用户敏感信息
     * @param day 指定 token 存活时间 (天)
     * @return token令牌
     */
    public static String getToken(Map<String, String> map, Integer day) {
        return getToken(map, day, SECRET);
    }

    /**
     * 获取指定签名的 token
     *
     * @param map    数据,请勿传入用户敏感信息
     * @param secret 签名
     * @return token令牌
     */
    public static String getToken(Map<String, String> map, String secret) {
        return getToken(map, DEFAULT_DAY, secret);
    }


    /**
     * 自定义token令牌
     *
     * @param map    数据
     * @param day    token 存活时间 (天)
     * @param secret 签名
     * @return token令牌
     */
    public static String getToken(Map<String, String> map, Integer day, String secret) {
        JWTCreator.Builder builder = JWT.create();

        map.forEach((key, value) -> {
            builder.withClaim(key, value); // 保存数据
        });

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE, day);

        return builder.withExpiresAt(instance.getTime()) // 设置token过期时间
                .sign(Algorithm.HMAC256(secret)); // 设置签名
    }

    /**
     * 验证默认签名的 token
     *
     * @param token 令牌
     */
    public static void verify(String token) {
        verify(token, SECRET);
    }

    /**
     * 验证指定签名的token
     *
     * @param token  令牌
     * @param secret 自定义签名
     */
    public static void verify(String token, String secret) {
        JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
    }


    /**
     * 获取指定签名 DecodedJWT 数据
     *
     * @param token  令牌
     * @param secret 签名
     * @return DecodedJWT
     */
    public static DecodedJWT getDecodedJWT(String token, String secret) {
        return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
    }

    /**
     * 获取默认签名 DecodedJWT 数据
     *
     * @param token 令牌
     * @return DecodedJWT
     */
    public static DecodedJWT getDecodedJWT(String token) {
        return getDecodedJWT(token, SECRET);
    }

    /**
     * 获取默认签名的单个数据
     *
     * @param token 令牌
     * @param claim 声明名
     * @param slazz 声明类型
     * @param <T>   方法类型
     * @return 数据<T>
     */
    public static <T> T getClaim(String token, String claim, Class<T> slazz) {
        return getClaim(token, SECRET, claim, slazz);
    }

    /**
     * 获取指定签名的单个数据
     *
     * @param token  令牌
     * @param secret 签名
     * @param claim  声明名
     * @param slazz  声明类型
     * @param <T>    方法类型
     * @return 数据<T>
     */
    public static <T> T getClaim(String token, String secret, String claim, Class<T> slazz) {
        return getDecodedJWT(token, secret).getClaim(claim).as(slazz);
    }

    /**
     * 获取默认所有数据
     *
     * @param token 令牌
     * @return Map<String, Claim>
     */
    public static Map<String, Claim> getClamins(String token) {
        return getClamins(token, SECRET);
    }

    /**
     * 过去指定签名所有数据
     *
     * @param token  令牌
     * @param secret 签名
     * @return Map<String, Claim>
     */
    public static Map<String, Claim> getClamins(String token, String secret) {
        return getDecodedJWT(token, secret).getClaims();
    }

}

编写拦截器

package com.java265.jwt.interceptors;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.java265.jwt.utils.JWTUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/** 
 * @description 拦截器
 */
public class JwtInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        Map<String, Object> map = new HashMap<>();
        try {
            JWTUtils.verify(token);
            return true;
        } catch (AlgorithmMismatchException | SignatureVerificationException | InvalidClaimException e) {
            map.put("msg","请勿修改数据");
            map.put("state","4001");
        } catch (TokenExpiredException e) {
            map.put("msg", "登录已过期");
            map.put("state", "4002");
        } catch (Exception e) {
            map.put("msg", "请先登录");
            map.put("state", "4003");
        }
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;

    }
}

注册拦截器

package com.java265.jwt.config;

import com.java265.jwt.interceptors.JwtInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

 
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JwtInterceptor())
                .addPathPatterns("/user/index")
                .excludePathPatterns("/user/login");
    }
}
JWT项目结构图

测试--未登录
jwt未登录测试
登录-获取token
jwt获取token
再次访问index页面,带上token
jwt认证成功
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaJingYan/202301/16732357895317.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者