JWT

Posted chenjiahao9527

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JWT相关的知识,希望对你有一定的参考价值。

  JWT是什么

   JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案

 

 

 JWT的工作原理

 

   1. 是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,示例如下:

 

      "UserName": "Chongchong","Role": "Admin","Expire": "2018-08-08 20:15:56"

 

 

 

   2. 之后,当用户与服务器通信时,客户在请求中发回JSON对象

 

    

 

   3. 为了防止用户篡改数据,服务器将在生成对象时添加签名,并对发回的数据进行验证

 

 

 JWT组成

 

   一个JWT实际上就是一个字符串,它由三部分组成:头部(Header)、载荷(Payload)与签名(signature)

 

   JWT结构原理图:见资料“JWT的数据结构.jpg”

 

技术图片

 

 

JWT实际结构:eyJhbGciOiJIUzI1NiJ9.

                eyJzdWIiOiJ7fSIsImlzcyI6InpraW5nIiwiZXhwIjoxNTYyODUwMjM3LCJpYXQiOjE1NjI4NDg0MzcsImp0aSI6ImM5OWEyMzRmMDc4NzQyZWE4YjlmYThlYmYzY2VhNjBlIiwidXNlcm5hbWUiOiJ6c3MifQ.

                WUfqhFTeGzUZCpCfz5eeEpBXBZ8-lYg1htp-t7wD3I4

 

   它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。

   写成一行,就是下面的样子:Header.Payload.Signature

技术图片

定义过滤器验证:JwtFilter

 

  JwtFilter:

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

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

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

	private boolean OFF = false;// 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;
		String path = req.getServletPath();
		if (OFF || isExcludeUrl(path)) // 登陆直接放行
			chain.doFilter(request, response);
			return;
		

		// 从客户端请求头中获得令牌并验证
		String jwt = req.getHeader(JwtUtils.JWT_HEADER_KEY);
		Claims claims = this.validateJwtToken(jwt);
		if (null == claims) 
			// resp.setCharacterEncoding("UTF-8");
			resp.sendError(403, "JWT令牌已过期或已失效");
			return;
		 else 
			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) 
				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);
	// 


  工具类JwtUtils:

/**
 * 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那部分,jjwt已经将这部分内容封装好了。
	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<String, Object> claims, long ttlMillis) 
		// 生成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();
	

	/**
	 * 复制jwt,并重新设置签发时间(为当前时间)和失效时间
	 * 
	 * @param jwt
	 *            被复制的jwt令牌
	 * @param ttlMillis
	 *            jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间
	 * @return
	 */
	public static String copyJwt(String jwt, Long ttlMillis) 
		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();
	

 将生产令牌传进action:

public class UserAction extends BaseAction implements ModelDriven<User>

	private UserBiz userBiz;
	private User user = new User();

	public UserBiz getUserBiz() 
		return userBiz;
	

	public void setUserBiz(UserBiz userBiz) 
		this.userBiz = userBiz;
	
	 
	public String login() 
		ObjectMapper om = new ObjectMapper();
		JsonData jsonData = null;
		try 
			if(StringUtils.isBlank(user.getUname()) || StringUtils.isBlank(user.getPwd())) 
				jsonData = new JsonData(0, "用户或者密码为空", user);
			else 
				User u = this.userBiz.login(user);
				Map<String, Object> claims = new HashMap<String, Object>();
				claims.put("uname",user.getUname());
				claims.put("pwd", user.getPwd());
				String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
				response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
				jsonData = new JsonData(1, "登录成功", u);
			
		 catch (Exception e) 
			e.printStackTrace();
			jsonData = new JsonData(0, "用户或者密码错误", user);
		finally 
			try 
				ResponseUtil.write(response, om.writeValueAsString(jsonData));
			 catch (Exception e) 
				e.printStackTrace();
			
		
		
		return null;
	
	
	public String getAsyncData() 
		ObjectMapper om = new ObjectMapper();
		try 
			Thread.sleep(6000);
			ResponseUtil.write(response, om.writeValueAsString("http://www.javaxl.com"));
		 catch (Exception e) 
			e.printStackTrace();
		
		return null;
	

	@Override
	public User getModel() 
		return user;
	

  然后在Vuex中定义变量Jwt通过mutations和gette返回jwt:

  state

export default 
 jwt:null

  mutations

export default 
  setJwt: (state, payload) => 
    state.jwt = payload.jwt;
  

  gette

export default 
 getJwt: (state) => 
    return state.jwt;

  

  在nodeJs前端服务器中设置拦截器也就是https.js

// 请求拦截器
axios.interceptors.request.use(function(config) 
	var jwt = window.vm.$store.getters.getJwt;
	config.headers[‘jwt‘] = jwt;
	return config;
, function(error) 
	return Promise.reject(error);
);

// 响应拦截器
axios.interceptors.response.use(function(response) 
	// debugger;
	var jwt = response.headers[‘jwt‘];
	if(jwt)
		window.vm.$store.commit(‘setJwt‘,jwt:jwt);
	
	return response;
, function(error) 
	return Promise.reject(error);
);

  结果技术图片

 

不登录是访问不了主页面的

  

  

 

以上是关于JWT的主要内容,如果未能解决你的问题,请参考以下文章

JWT实战教程

JWT实战教程

整合JWT

Shiro+JWT 实现权限管理--JWT

Shiro+JWT 实现权限管理--JWT

什么是JWT