JWT全面解读和详细使用步骤

 更新时间:2021年12月02日 11:36:04   作者:丁可乐  
这篇文章全面解读了JWT规范和详细使用步骤,文中通过示例代码介绍的非常详细。对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

定义:JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案

JWT官网

由于HTTP协议是无状态的,这意味着如果我们想判定一个接口是否被认证后访问,就需要借助cookie或者session会话机制进行判定,但是由于现在的系统架构大部分都不止一台服务器,此时又要借助数据库或者全局缓存 做存储,这种方案显然受限太多。

那么我们可不可以让认证令牌的发布者自己去识别这个令牌是不是我曾经发布的令牌呢(JWT核心思想),这是JWT最大的优点也是最大的缺点,优点是简单快捷、不需要依赖任何第三方操作就能实现身份认证,缺点就是对于任何拥有用户发布令牌的请求都会认证通过。

JWT的数据结构

正常的JWT数据结构应该如下

它是一个很长的字符串,中间用点(.)分隔成三个部分

JWT的三个部分依次: Header - 头部 、Payload - 负载 、Signature(签名)

即:Header.Payload.Signature

Header

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

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

alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

  1. iss (issuer):签发人
  2. exp (expiration time):过期时间
  3. sub (subject):主题
  4. aud (audience):受众
  5. nbf (Not Before):生效时间
  6. iat (Issued At):签发时间
  7. jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段

{
"sub": "1234567890",
"name": "John Doe",
"age": "19"
}

注意:JWT默认是明文展示,任何人都可以读取到,所以此处不要放私密信息

这个 JSON 对象也要使用 Base64URL 算法转成字符串。

Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

  HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

Base64URL

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法

JWT的实现

Maven依赖

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

JWT签名发布和验证代码

public class TokenUtil {
	//Token的过期时间
	private static final long EXPIRE_TIME = 30 * 60 * 1000;
	//Token的私钥
	private static final String TOKEN_SECRET = "jytoken_secret";
	
	
	/**
	 * 生成签名,30分钟过期
	 * @param **userInfo**	用户信息 用户姓名
	* @param **other** 用户其他信息 用户id
	* @return
	 */
	public static String sign(String userInfo, String other) {
	    try {
	    	// 设置过期时间
	    	Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
	    	//私钥和加密算法
	        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
	        // 设置头部信息
	        Map<String, Object> header = new HashMap<>(2);
	        header.put("Type", "Jwt");
	        header.put("alg", "HS256");
	        // 返回token字符串
	        return JWT.create()
	                .withHeader(header)
	                .withClaim("userInfo", userInfo)
	                .withClaim("other", other)
	                .withExpiresAt(date)
	                .sign(algorithm);
	    } catch (Exception e) {
	        e.printStackTrace();
	        return null;
	    }
	}
	
	/**
	 * 生成签名,30分钟过期
	 * @param **userInfo**	用户信息 用户姓名
	* @param **other** 用户其他信息 用户id
	* @return
	 */
	public static String sign(String userInfo, String other,long expire) {
	    try {
	    	// 设置过期时间
	    	Date date = new Date(System.currentTimeMillis() + expire);
	    	//私钥和加密算法
	        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
	        // 设置头部信息
	        Map<String, Object> header = new HashMap<>(2);
	        header.put("Type", "Jwt");
	        header.put("alg", "HS256");
	        // 返回token字符串
	        return JWT.create()
	                .withHeader(header)
	                .withClaim("userInfo", userInfo)
	                .withClaim("other", other)
	                .withExpiresAt(date)
	                .sign(algorithm);
	    } catch (Exception e) {
	        e.printStackTrace();
	        return null;
	    }
	}
	
	
	/**
	 * 检验token是否正确
	 * @param **token**
	* @return
	 */
	public static boolean verify(String token){
	    try {
	        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
	        JWTVerifier verifier = JWT.require(algorithm).build();
	        verifier.verify(token);//未验证通过会抛出异常
	        return true;
	    } catch (Exception e){
	        return false;
	    }
	}
	
	/**
	 * 从token中获取info信息
	 * @param **token**
	* @return
	 */
	public static String getUserName(String token,String info){
	    try {
	        DecodedJWT jwt = JWT.decode(token);
	        return jwt.getClaim(info).asString();
	    } catch (JWTDecodeException e){
	        e.printStackTrace();
	    }
	    return null;
	}
	
}

拦截器配置无需认证的请求

@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport  {
   
	@Autowired
	private TokenHandler tokenHandler;
    


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        String checkLogin = "/pushlogin/checkIsCanLogin";
        String login = "/pushlogin/login";
        String getVerifyCode = "/common/send";
        String verfifyMethod = "/common/validationCode";
        excludePath.add(checkLogin);
        excludePath.add(login);
        excludePath.add(getVerifyCode);
        excludePath.add(verfifyMethod);
        registry.addInterceptor(tokenHandler).excludePathPatterns(excludePath);
    }
}

Token统一拦截器代码

@Component
@Slf4j
public class TokenHandler implements HandlerInterceptor{
	
		@Override
	   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  throws Exception {
	 
	        String token = request.getHeader("Authentication");
	        if (token != null){
	            boolean result = TokenUtil.verify(token);
	            if(result){
	                log.info("通过拦截器");
	                return true;
	            }
	        }
	        log.info("认证失败");
	        
	        return false;
	   }
	
}

用户登录时验证用户信息后,返回Token信息

 	@Override
    public UserDTO selectIsExistUserInfo(String phone) {
        //TODO 伪代码 验证用户信息 
        UserDTO info = 查询用户信息
        if (info != null) {
            String token = TokenUtil.sign(info.getUsername(), info.getUserId(), 6 * 60 * 60 * 1000);
            info.setToken(token);
        }
        return info;
    }

到此这篇关于JWT全面解读和详细使用步骤的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java反射概念与使用实例代码

    Java反射概念与使用实例代码

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,下面这篇文章主要给大家介绍了关于Java反射概念与使用的相关资料,需要的朋友可以参考下
    2021-11-11
  • Java基础之不简单的数组

    Java基础之不简单的数组

    数组(Array)是有序的元素序列。 若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量
    2021-09-09
  • 解决@Cacheable在同一个类中方法调用不起作用的问题

    解决@Cacheable在同一个类中方法调用不起作用的问题

    这篇文章主要介绍了解决@Cacheable在同一个类中方法调用不起作用的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java设计模式之观察者模式解析

    Java设计模式之观察者模式解析

    这篇文章主要介绍了Java设计模式之观察者模式解析,观察者模式,又被称为发布/订阅模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己,需要的朋友可以参考下
    2023-09-09
  • MybatisPlus自动填充时间的配置类实现

    MybatisPlus自动填充时间的配置类实现

    本文介绍了如何在MyBatis-Plus中实现自动填充时间的功能,通过实现MetaObjectHandler接口,重写insertFill()和updateFill()方法,分别在插入和更新时填充创建时间和更新时间,感兴趣的可以了解一下
    2024-12-12
  • SpringBoot项目多层级多环境yml设计详解

    SpringBoot项目多层级多环境yml设计详解

    这篇文章主要为大家介绍了SpringBoot项目多层级多环境yml设计详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • maven打包插件的使用(maven-compiler-plugin、maven-dependency-plugin、maven-jar-plugin、maven-resources-plugin)

    maven打包插件的使用(maven-compiler-plugin、maven-dependency-plugin、m

    本文主要介绍了maven打包插件的使用(maven-compiler-plugin、maven-dependency-plugin、maven-jar-plugin、maven-resources-plugin),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • SpringBoot ApplicationContext接口深入分析

    SpringBoot ApplicationContext接口深入分析

    ApplicationContext是Spring应用程序中的中央接口,由于继承了多个组件,使得ApplicationContext拥有了许多Spring的核心功能,如获取bean组件,注册监听事件,加载资源文件等
    2022-11-11
  • Spring MVC数据绑定方式

    Spring MVC数据绑定方式

    这篇文章主要介绍了Spring MVC数据绑定方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • Java使用Lua实现动态扩展和脚本自动升级

    Java使用Lua实现动态扩展和脚本自动升级

    Lua是一种轻量级的脚本语言,常用于游戏开发和嵌入式系统中,这篇文章主要介绍了Java如何调用Lua实现动态扩展和脚本自动升级,感兴趣的可以学习下
    2023-08-08

最新评论