JWT的原理及实际使用

JWT的原理及实际使用_第1张图片

                                                        艳艳耶✌️:个人主页

                                                        个人专栏 :《Spring与Mybatis集成整合》《Vue.js使用》

                ⛺️ 生活的理想,为了不断更新自己 ! 

 

目录

1.JWT是什么?

2. jwt工具类介绍

3. jwt集成spa项目


1.JWT是什么?

         JWT全称为JSON Web Token,是一种用于身份验证和授权的开放标准。它是一个紧凑且安全的方式,通过在不同系统之间传递可信任的信息来实现用户身份验证。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。


2. jwt工具类介绍

JWT工具类是一个用于生成、解析和验证JSON Web Token的辅助类。它提供了一系列方法来简化JWT的处理过程,使得开发者可以方便地在应用中使用JWT进行身份验证和授权。

JWT工具类通常包括以下功能:

  • 生成JWT:提供方法用于生成JWT,传入有效载荷和加密密钥等参数,生成符合规范的JWT字符串。
  • 解析JWT:提供方法用于解析JWT,将JWT字符串解析成头部和载荷的JSON对象,并对签名进行验证。
  • 验证JWT:提供方法用于验证JWT的有效性,包括验证签名是否正确、验证令牌是否过期等。
  • 获取载荷信息:提供方法用于获取JWT中的载荷信息,比如用户ID、角色等。
  • 刷新JWT:提供方法用于刷新JWT,生成新的JWT并返回给客户端,用于延长认证的有效期。
  • 配置参数:提供方法用于配置JWT的相关参数,比如加密算法、密钥、过期时间等。

使用JWT工具类可以简化JWT的处理逻辑,减少代码重复性,并增加系统的安全性。通过封装好的方法,开发者可以方便地在不同的场景中使用JWT进行身份验证和授权,提高开发效率。但需要注意,在使用JWT工具类时,应确保密钥的安全性,避免泄露导致令牌被篡改或伪造。

代码展示:

package com.sy.ssm.util;
 
import java.util.Date;
import java.util.Map;
import java.util.UUID;
 
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
 
import org.apache.commons.codec.binary.Base64;
 
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
 
/**
 * JWT验证过滤器:配置顺序 CorsFilte->JwtUtilsr-->StrutsPrepareAndExecuteFilter
 *
 */
public class JwtUtils {
	/**
	 * JWT_WEB_TTL:WEBAPP应用中token的有效时间,默认30分钟
	 */
	public static final long JWT_WEB_TTL = 30 * 60 * 1000;
 
	/**
	 * 将jwt令牌保存到header中的key
	 */
	public static final String JWT_HEADER_KEY = "jwt";
 
	// 指定签名的时候使用的签名算法,也就是header那部分,jwt已经将这部分内容封装好了。
	private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
	private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙
	private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key
 
	static {
		byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
		JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
	}
 
	private JwtUtils() {
	}
 
	/**
	 * 解密jwt,获得所有声明(包括标准和私有声明)
	 * 
	 * @param jwt
	 * @return
	 * @throws Exception
	 */
	public static Claims parseJwt(String jwt) {
		Claims claims = Jwts.parser()
				.setSigningKey(JWT_KEY)
				.parseClaimsJws(jwt)
				.getBody();
		return claims;
	}
 
	/**
	 * 创建JWT令牌,签发时间为当前时间
	 * 
	 * @param claims
	 *            创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
	 * @param ttlMillis
	 *            JWT的有效时间(单位毫秒),当前时间+有效时间=过期时间
	 * @return jwt令牌
	 */
	public static String createJwt(Map claims, 
			long ttlMillis) {
		// 生成JWT的时间,即签发时间 2021-10-30 10:02:00 -> 30 10:32:00
	
		long nowMillis = System.currentTimeMillis();
 
		
		//链式语法:
		// 下面就是在为payload添加各种标准声明和私有声明了
		// 这里其实就是new一个JwtBuilder,设置jwt的body
		JwtBuilder builder = Jwts.builder()
				// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
				.setClaims(claims)
				// 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
				// 可以在未登陆前作为身份标识使用
				.setId(UUID.randomUUID().toString().replace("-", ""))
				// iss(Issuser)签发者,写死
				.setIssuer("zking")
				// iat: jwt的签发时间
				.setIssuedAt(new Date(nowMillis))
				// 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
				// .setSubject("{}")
				// 设置签名使用的签名算法和签名使用的秘钥
				.signWith(SIGNATURE_ALGORITHM, JWT_KEY)
				// 设置JWT的过期时间
				.setExpiration(new Date(nowMillis + ttlMillis));
 
		return builder.compact();
	}
 
	/**
	 * 复制jwt,并重新设置签发时间(为当前时间)和失效时间
	 * 
	 * @param jwt
	 *            被复制的jwt令牌
	 * @param ttlMillis
	 *            jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间
	 * @return
	 */
	public static String copyJwt(String jwt, Long ttlMillis) {
		//解密JWT,获取所有的声明(私有和标准)
		//old
		Claims claims = parseJwt(jwt);
 
		// 生成JWT的时间,即签发时间
		long nowMillis = System.currentTimeMillis();
 
		// 下面就是在为payload添加各种标准声明和私有声明了
		// 这里其实就是new一个JwtBuilder,设置jwt的body
		JwtBuilder builder = Jwts.builder()
				// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
				.setClaims(claims)
				// 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
				// 可以在未登陆前作为身份标识使用
				//.setId(UUID.randomUUID().toString().replace("-", ""))
				// iss(Issuser)签发者,写死
				// .setIssuer("zking")
				// iat: jwt的签发时间
				.setIssuedAt(new Date(nowMillis))
				// 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
				// .setSubject("{}")
				// 设置签名使用的签名算法和签名使用的秘钥
				.signWith(SIGNATURE_ALGORITHM, JWT_KEY)
				// 设置JWT的过期时间
				.setExpiration(new Date(nowMillis + ttlMillis));
		return builder.compact();
	}
}
 
 
 
 

3. jwt集成spa项目

后台代码:

 用户登录方法,放开用户信息生成jwt串保存到响应头中

 代码展示:

package com.sy.ssm.controller;

import com.sy.ssm.service.IUserService;
import com.sy.ssm.util.JsonResponseBody;
import com.sy.ssm.util.PageBean;
import com.sy.ssm.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sy.ssm.jwt.*;

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService userService;

    @RequestMapping("/userLogin")
    @ResponseBody
    public JsonResponseBody userLogin(UserVo userVo, HttpServletResponse response){
        if(userVo.getUsername().equals("admin")&&userVo.getPassword().equals("123")){
            //私有要求claim
//            Map json=new HashMap();
//            json.put("username", userVo.getUsername());
            //生成JWT,并设置到response响应头中
//            String jwt=JwtUtils.createJwt(json, JwtUtils.JWT_WEB_TTL);
//            response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
            return new JsonResponseBody<>("用户登陆成功!",true,0,null);
        }else{
            return new JsonResponseBody<>("用户名或密码错误!",false,0,null);
        }
    }

    @RequestMapping("/userRegister")
    @ResponseBody
    public JsonResponseBody userRegister(UserVo userVo, HttpServletResponse response){
        //状态新注册默认为0
        userVo.setStatus("0");
        //因为ID为String类型需要手动设置,当然可以根据自己的需要改为Int类型
        userVo.setId("15");
        int i = userService.insertSelective(userVo);
        if(i>0){
            return new JsonResponseBody<>("用户注册完成!快去登入吧!",true,0,null);
        }else{
            return new JsonResponseBody<>("用户注册失败!重新输入。",false,0,null);
        }
    }

    @RequestMapping("/queryUserPager")
    @ResponseBody
    public JsonResponseBody>>
            queryUserPager(UserVo userVo, HttpServletRequest request){
        try {
            PageBean pageBean=new PageBean();
            pageBean.setRequest(request);
            List> users = userService.queryUserPager(userVo, pageBean);
            return new JsonResponseBody<>("OK",true,pageBean.getTotal(),users);
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResponseBody<>("分页查询用户信息失败!",false,0,null);
        }

    }
}

关闭JwtFilter中的OFF开关,开启jwt验证

代码展示:

package com.sy.ssm.jwt;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import io.jsonwebtoken.Claims;

/**
 * * JWT验证过滤器,配置顺序 :CorsFilter-->JwtFilter-->struts2中央控制器
 * 
 * @author Administrator
 *
 */

public class JwtFilter implements Filter {

	// 排除的URL,一般为登陆的URL(请改成自己登陆的URL)
	private static String EXCLUDE = "^/user/userLogin?.*$";

	private static Pattern PATTERN = Pattern.compile(EXCLUDE);

	private boolean OFF = true;// true关闭jwt令牌验证功能

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse resp = (HttpServletResponse) response;
		//获取当前请求路径。只有登录的请求路径不进行校验之外,其他的URL请求路径必须进行JWT令牌校验
		//http://localhost:8080/ssh2/bookAction_queryBookPager.action
		//req.getServletPath()==/bookAction_queryBookPager.action
		String path = req.getServletPath();
		if (OFF || isExcludeUrl(path)) {// 登陆直接放行
				chain.doFilter(request, response);
				return;
		}

		// 从客户端请求头中获得令牌并验证
		//token=头.载荷.签名
		String jwt = req.getHeader(JwtUtils.JWT_HEADER_KEY);
		Claims claims = this.validateJwtToken(jwt);
		//在这里请各位大哥大姐从JWT令牌中提取payload中的声明部分
		//从声明部分中获取私有声明
		//获取私有声明中的User对象 -> Modules
		Boolean flag=false;
		if (null == claims) {
			// resp.setCharacterEncoding("UTF-8");
			resp.sendError(403, "JWT令牌已过期或已失效");
			return;
		} else {
			
			//1.获取已经解析后的payload(私有声明)
			//2.从私有声明中当前用户所对应的权限集合List或者List
			//3.循环权限(Module[id,url])
			// OK,放行请求 chain.doFilter(request, response);
			// NO,发送错误信息的JSON
			// ObjectMapper mapper=new ObjectMapper()
			// mapper.writeValue(response.getOutputStream(),json)
			
			String newJwt = JwtUtils.copyJwt(jwt, JwtUtils.JWT_WEB_TTL);
			resp.setHeader(JwtUtils.JWT_HEADER_KEY, newJwt);
			chain.doFilter(request, response);
		}
	}

	/**
	 * 验证jwt令牌,验证通过返回声明(包括公有和私有),返回null则表示验证失败
	 */
	private Claims validateJwtToken(String jwt) {
		Claims claims = null;
		try {
			if (null != jwt) {
				//该解析方法会验证:1)是否过期 2)签名是否成功
				claims = JwtUtils.parseJwt(jwt);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return claims;
	}

	/**
	 * 是否为排除的URL
	 * 
	 * @param path
	 * @return
	 */
	private boolean isExcludeUrl(String path) {
		Matcher matcher = PATTERN.matcher(path);
		return matcher.matches();
	}

	// public static void main(String[] args) {
	// String path = "/sys/userAction_doLogin.action?username=zs&password=123";
	// Matcher matcher = PATTERN.matcher(path);
	// boolean b = matcher.matches();
	// System.out.println(b);
	// }

}

web.xml中要配置corsfilter,允许jwt使用请求头及响应头


  Archetype Created Web Application
 
  
  
  
    contextConfigLocation
    classpath:spring.xml
  
  
  
    org.springframework.web.context.ContextLoaderListener
  
 
  
  
    encodingFilter
    org.springframework.web.filter.CharacterEncodingFilter
    true
    
      encoding
      UTF-8
    
  
  
    encodingFilter
    /*
  
 
  
  
    corsFilter
    com.zking.ssm.util.CorsFilter
  
  
    corsFilter
    /*
  
 
  
  
    jwtFilter
    com.zking.ssm.jwt.JwtFilter
  
  
    jwtFilter
    /*
  
 
  
  
    SpringMVC
    org.springframework.web.servlet.DispatcherServlet
    
    
      contextConfigLocation
      /WEB-INF/spring-mvc.xml
    
    1
    
    true
  
  
    SpringMVC
    /
  

前端代码:

state.js

export default {
  eduName: '米西米西花不垃圾',
  jwt: ''
}

mutations.js

export default {
  // type(事件类型): 其值为setEduName
  // payload:官方给它还取了一个高大上的名字:载荷,其实就是一个保存要传递参数的容器
  setEduName: (state, payload) => {
    state.eduName = payload.eduName;
  },
  setJwt: (state, payload) => {
    state.jwt = payload.jwt;
  }
}

getters.js

export default{
  getedName:(state)=>{
    return state.edName;
  },
  getJwt:(state)=>{
    return state.jwt;
  }
}

在spa项目中找到main.js文件 : 

/* eslint-disable no-new */
window.mm = new Vue({
  el: '#app',
  router,
  store,
  data() {
    return {
      bus: new Vue()
    }
  },
  components: {
    App
  },
  template: ''
})

在spa项目中找到http.js文件 ( src/api/http.js)进行编写请求拦截器 & 响应拦截器  :

// 请求拦截器
axios.interceptors.request.use(function(config) {
  let jwt = window.mm.$store.getters.getJwt;
  if(jwt){
    config.headers['jwt'] = jwt;
  }
  return config;
}, function(error) {
  return Promise.reject(error);
});
 
// 响应拦截器
axios.interceptors.response.use(function(response) {
  //将响应头中的jwt字符串放入state.js中
  let jwt = response.headers['jwt'];
  if (jwt) {
    window.mm.$store.commit('setJwt', {
      jwt: jwt
    });
  }
  return response;
}, function(error) {
  return Promise.reject(error);
});

效果展示:

                                             今日分享到此结束!!!  JWT的原理及实际使用_第2张图片

你可能感兴趣的:(vue.js,html,idea,jwt,JWT)