• “Jwt认证在前后端分离架构中的应用与优化“



    在这里插入图片描述

    引言

    在当今互联网应用开发中,前后端分离架构已经成为一种主流的开发模式。而身份验证和授权是保证系统安全性的重要环节之一。JSON Web Token (JWT) 是一种轻量级的身份认证和授权机制,因其易于实现、跨平台支持等特点,在前后端分离架构中得到广泛应用。本文将介绍JWT的简介、使用JWT的工具类以及通过一个实际案例演示JWT的应用。

    1. JWT的简介

    1.1 什么是JWT

    JWT是一种基于JSON的开放标准(RFC 7519),定义了一种紧凑且自包含的方式来传递信息。它由三部分组成:头部(Header)负载(Payload)签名(Signature)。JWT可以被用于身份验证和授权,也可以传递其他业务相关的信息。

    1.2 JWT的优势

    • 简单轻便:JWT是一种轻量级的身份认证和授权机制,由于其使用JSON格式存储数据,整个Token相对较小。
    • 跨平台支持:JWT在不同编程语言和平台间都有良好的支持,使得其在前后端分离的项目中易于集成和使用。
    • 无状态:JWT本身是无状态的,服务端不需要存储Token相关信息,减轻了服务端的负担。

    2. JWT工具类

    2.1 JWT生成与解析

    在使用JWT时,我们可以借助现有的JWT工具类来简化操作。这些工具类通常提供了JWT的生成、解析以及校验等功能,极大地提高了开发效率。以下是一些常用的JWT工具类:

    JwtUtils

    package com.zking.ssm.jwt;
    
    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<String, Object> 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();
    	}
    }
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141

    JwtFilter

    package com.zking.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);
    	// }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    JwtDemo

    package com.zking.ssm.service.impl;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    import com.zking.ssm.jwt.JwtUtils;
    import io.jsonwebtoken.Claims;
    import org.junit.*;
    
    public class JwtDemo {
    
    	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    
    	@Test
    	public void test1() {// 生成JWT
    		
    		//JWT Token=Header.Payload.Signature
    		//头部.载荷.签名
    		//Payload=标准声明+私有声明+公有声明
    		
    		//定义私有声明
    		Map<String, Object> claims = new HashMap<String, Object>();
    		claims.put("username", "zss");
    		claims.put("age", 18);
    		
    	    //TTL:Time To Live
    		String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
    		System.out.println(jwt);
    
    		//获取Payload(包含标准和私有声明)
    		Claims parseJwt = JwtUtils.parseJwt(jwt);
    		for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
    			System.out.println(entry.getKey() + "=" + entry.getValue());
    		}
    		Date d1 = parseJwt.getIssuedAt();
    		Date d2 = parseJwt.getExpiration();
    		System.out.println("令牌签发时间:" + sdf.format(d1));
    		System.out.println("令牌过期时间:" + sdf.format(d2));
    	}
    
    	@Test
    	public void test2() {// 解析oldJwt
    		//io.jsonwebtoken.ExpiredJwtException:JWT过期异常
    		//io.jsonwebtoken.SignatureException:签名异常
    		//String oldJwt="eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTA3MTg2NzcsImlhdCI6MTU5MDcxNjg3NywiYWdlIjoxOCwianRpIjoiNDFmZjFiZGFkYzkxNDA3OGE4ZGUyNGRkZDEwYjU4N2IiLCJ1c2VybmFtZSI6InpzcyJ9.DdPvioX6kuhV6lEfD9QAN2eQSk_mO3dYkmDmTQsqa78";
    		//eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjE3MjcsImlhdCI6MTYzNTU1OTkyNywiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ9.dUR-9JUlyRdoYx-506SxXQ3gbHFCv0g5Zm8ZGzK1fzw
    		String newJwt="eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY2MjM0Njg3MSwiaWF0IjoxNjYyMzQ1MDcxLCJhZ2UiOjE4LCJqdGkiOiI4YjllNzc3YzFlMDM0MjViYThmMDVjNTFlMTU3NDQ1MiIsInVzZXJuYW1lIjoienNzIn0.UWpJxPxwJ09PKxE2SY5ME41W1Kv3jP5bZGKK-oNUDuM";
    		String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjE3MjcsImlhdCI6MTYzNTU1OTkyNywiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ9.dUR-9JUlyRdoYx-506SxXQ3gbHFCv0g5Zm8ZGzK1fzw";
    		Claims parseJwt = JwtUtils.parseJwt(newJwt);
    		for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
    			System.out.println(entry.getKey() + "=" + entry.getValue());
    		}
    		Date d1 = parseJwt.getIssuedAt();
    		Date d2 = parseJwt.getExpiration();
    		System.out.println("令牌签发时间:" + sdf.format(d1));
    		System.out.println("令牌过期时间:" + sdf.format(d2));
    	}
    
    	@Test
    	public void test3() {// 复制jwt,并延时30分钟
    		String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY2MjM0Njg3MSwiaWF0IjoxNjYyMzQ1MDcxLCJhZ2UiOjE4LCJqdGkiOiI4YjllNzc3YzFlMDM0MjViYThmMDVjNTFlMTU3NDQ1MiIsInVzZXJuYW1lIjoienNzIn0.UWpJxPxwJ09PKxE2SY5ME41W1Kv3jP5bZGKK-oNUDuM";
    		//String newJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDU3NTM2NTUsImlhdCI6MTYwNTc1MTg1NSwiYWdlIjoxOCwianRpIjoiYmNmN2Q1MzQ2YjE3NGU2MDk1MmIxYzQ3ZTlmMzQyZjgiLCJ1c2VybmFtZSI6InpzcyJ9.m1Qn84RxgbKCnsvrdbbAnj8l_5Jwovry8En0j4kCxhc";
    		//String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI5MDMzNjAsImlhdCI6MTU2MjkwMTU2MCwiYWdlIjoxOCwianRpIjoiZDVjMzE4Njg0MDcyNDgyZDg1MDE5ODVmMDY3OGQ4NjkiLCJ1c2VybmFtZSI6InpzcyJ9.XDDDRRq5jYq5EdEBHtPm7GcuBz4S0VhDTS1amRCdf48";
    		String newJwt = JwtUtils.copyJwt(oldJwt, JwtUtils.JWT_WEB_TTL);
    		System.out.println(newJwt);
    		Claims parseJwt = JwtUtils.parseJwt(newJwt);
    		for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
    			System.out.println(entry.getKey() + "=" + entry.getValue());
    		}
    		Date d1 = parseJwt.getIssuedAt();
    		Date d2 = parseJwt.getExpiration();
    		System.out.println("令牌签发时间:" + sdf.format(d1));
    		System.out.println("令牌过期时间:" + sdf.format(d2));
    	}
    
    	@Test
    	public void test4() {// 测试JWT的有效时间
    		Map<String, Object> claims = new HashMap<String, Object>();
    		claims.put("username", "zss");
    		String jwt = JwtUtils.createJwt(claims, 3 * 1000L);
    		System.out.println(jwt);
    		Claims parseJwt = JwtUtils.parseJwt(jwt);
    		Date d1 = parseJwt.getIssuedAt();
    		Date d2 = parseJwt.getExpiration();
    		System.out.println("令牌签发时间:" + sdf.format(d1));
    		System.out.println("令牌过期时间:" + sdf.format(d2));
    	}
    
    	@Test
    	public void test5() {// 三秒后再解析上面过期时间只有三秒的令牌,因为过期则会报错io.jsonwebtoken.ExpiredJwtException
    		//String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzU1NjMzODIsImlhdCI6MTYzNTU2MTU4MiwiYWdlIjoxOCwianRpIjoiN2RlYmIzM2JiZTg3NDBmODgzNDI5Njk0ZWE4NzcyMTgiLCJ1c2VybmFtZSI6InpzcyJ1.F4pZFCjWP6wlq8v_udfhOkNCpErF5QlL7DXJdzXTHqE";
    		String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ6a2luZyIsImV4cCI6MTY2MjM0Njg3MSwiaWF0IjoxNjYyMzQ1MDcxLCJhZ2UiOjE4LCJqdGkiOiI4YjllNzc3YzFlMDM0MjViYThmMDVjNTFlMTU3NDQ1MiIsInVzZXJuYW1lIjoienNzIn9.UWpJxPxwJ09PKxE2SY5ME41W1Kv3jP5bZGKK-oNUDuM";
    		Claims parseJwt = JwtUtils.parseJwt(oldJwt);
    		// 过期后解析就报错了,下面代码根本不会执行
    		Date d1 = parseJwt.getIssuedAt();
    		Date d2 = parseJwt.getExpiration();
    		System.out.println("令牌签发时间:" + sdf.format(d1));
    		System.out.println("令牌过期时间:" + sdf.format(d2));
    	}
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    text1测试结果
    在这里插入图片描述

    2.2 JWT与安全性

    在使用JWT时,我们需要注意安全性的问题,以防止JWT被篡改或盗用。以下是一些提升JWT安全性的建议:

    使用HTTPS协议传输JWT,确保传输过程中数据的机密性和完整性。
    对JWT进行签名,确保JWT的真实性和完整性。
    设置适当的过期时间,避免长时间有效的Token带来的潜在风险。

    3. JWT案例演示

    后台改动

    1.注释打开

        @RequestMapping("/userLogin")
        @ResponseBody
        public JsonResponseBody<?> userLogin(UserVo userVo, HttpServletResponse response){
            if(userVo.getUsername().equals("admin")&&userVo.getPassword().equals("123")){
    //            私有要求claim
                Map<String,Object> json=new HashMap<String,Object>();
                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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.设置jwt校验打开

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

    3.web.xml配置更改(CorsFilter2改CorsFilter)

      
      <filter>
        <filter-name>encodingFilterfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <async-supported>trueasync-supported>
        <init-param>
          <param-name>encodingparam-name>
          <param-value>UTF-8param-value>
        init-param>
      filter>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    CorsFilter

    package com.zking.ssm.util;
    
    import java.io.IOException;
    
    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;
    
    /**
     * 配置tomcat允许跨域访问
     * 
     * @author Administrator
     *
     */
    public class CorsFilter implements Filter {
    	@Override
    	public void init(FilterConfig filterConfig) throws ServletException {
    	}
    	@Override
    	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
    			throws IOException, ServletException {
    		HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
    		HttpServletRequest req = (HttpServletRequest) servletRequest;
    		// Access-Control-Allow-Origin就是我们需要设置的域名
    		// Access-Control-Allow-Headers跨域允许包含的头。
    		// Access-Control-Allow-Methods是允许的请求方式
    		httpResponse.setHeader("Access-Control-Allow-Origin", "*");// *,任何域名
    		httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");
    		
    		//允许客户端发一个新的请求头jwt
    		httpResponse.setHeader("Access-Control-Allow-Headers","responseType,Origin,X-Requested-With, Content-Type, Accept, jwt");
    		//允许客户端处理一个新的响应头jwt
    		httpResponse.setHeader("Access-Control-Expose-Headers", "jwt,Content-Disposition");
    		
    		//httpResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    		//httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");
    		
    		// axios的ajax会发两次请求,第一次提交方式为:option,直接返回即可
    		if ("OPTIONS".equals(req.getMethod())) {
    			return;
    		}
    		filterChain.doFilter(servletRequest, servletResponse);
    	}
    	@Override
    	public void destroy() {
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    前台改动

    state.js

    export default{
      eduName:'唱歌会跑调Y',
      jwt:''
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    mutations.js

    export default{
      setEduName:(state,payload)=>{
        state.eduName = payload.eduName
      },
      setJwt:(state,payload)=>{
        state.jwt = payload.jwt
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    getters.js

    export default{
      getEduName:(state)=>{
        return state.eduName;
      },
      getJwt:(state)=>{
        return state.jwt;
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    main.js
    (window.vm = new Vue)

    // The Vue build version to load with the `import` command
    // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    import Vue from 'vue'
    //开发环境下才会引入mockjs
    // process.env.MOCK && require('@/mock')
    // 新添加1
    import ElementUI from 'element-ui'
    // 新添加2,避免后期打包样式不同,要放在import App from './App';之前
    import 'element-ui/lib/theme-chalk/index.css'
    import App from './App'
    import router from './router'
    import store from './store'
    // 新添加3
    Vue.use(ElementUI)
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    import axios from '@/api/http'
    import VueAxios from 'vue-axios'
    
    Vue.use(VueAxios,axios)
    window.vm = new Vue({
      el: '#app',
      router,
      store,
      data(){
        return{
          Bus:new Vue()
        }
      },
      components: { App },
      template: '<App/>'
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    http.js

    /**
     * vue项目对axios的全局配置
     */
    import axios from 'axios'
    import qs from 'qs'
    
    //引入action模块,并添加至axios的类属性urls上
    import action from '@/api/action'
    axios.urls = action
    
    // axios默认配置
    axios.defaults.timeout = 10000; // 超时时间
    // axios.defaults.baseURL = 'http://localhost:8080/j2ee15'; // 默认地址
    axios.defaults.baseURL = action.SERVER;
    
    //整理数据
    // 只适用于 POST,PUT,PATCH,transformRequest` 允许在向服务器发送前,修改请求数据
    axios.defaults.transformRequest = function(data) {
      data = qs.stringify(data);
      return data;
    };
    
    
    // 请求拦截器
    axios.interceptors.request.use(function(config) {
      let jwt = window.vm.$store.getters.getJwt;
      if (jwt) {
        config.headers['jwt'] = jwt;
      }
    
      return config;
    }, function(error) {
      return Promise.reject(error);
    });
    
    // 响应拦截器
    axios.interceptors.response.use(function(response) {
      let jwt = response.headers['jwt'];
      if (jwt) {
        window.vm.$store.commit('setJwt', {
          jwt: jwt
        });
      }
    
    
      return response;
    }, function(error) {
      return Promise.reject(error);
    });
    
    // // 路由请求拦截
    // // http request 拦截器
    // axios.interceptors.request.use(
    // 	config => {
    // 		//config.data = JSON.stringify(config.data);
    // 		//config.headers['Content-Type'] = 'application/json;charset=UTF-8';
    // 		//config.headers['Token'] = 'abcxyz';
    // 		//判断是否存在ticket,如果存在的话,则每个http header都加上ticket
    // 		// if (cookie.get("token")) {
    // 		// 	//用户每次操作,都将cookie设置成2小时
    // 		// 	cookie.set("token", cookie.get("token"), 1 / 12)
    // 		// 	cookie.set("name", cookie.get("name"), 1 / 12)
    // 		// 	config.headers.token = cookie.get("token");
    // 		// 	config.headers.name = cookie.get("name");
    // 		// }
    // 		return config;
    // 	},
    // 	error => {
    // 		return Promise.reject(error.response);
    // 	});
    
    // // 路由响应拦截
    // // http response 拦截器
    // axios.interceptors.response.use(
    // 	response => {
    // 		if (response.data.resultCode == "404") {
    // 			console.log("response.data.resultCode是404")
    // 			// 返回 错误代码-1 清除ticket信息并跳转到登录页面
    // 			//      cookie.del("ticket")
    // 			//      window.location.href='http://login.com'
    // 			return
    // 		} else {
    // 			return response;
    // 		}
    // 	},
    // 	error => {
    // 		return Promise.reject(error.response) // 返回接口返回的错误信息
    // 	});
    
    
    
    export default axios;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    总结

    本文介绍了JWT的简介、工具类以及通过一个实际案例演示了JWT的应用。JWT作为一种轻量级的身份认证和授权机制,适用于前后分离架构中的应用场景。使用JWT可以简化身份验证和授权流程,并提升系统的安全性和可拓展性。在实际应用中,我们需要注意JWT的安全性,例如通过HTTPS传输、设置适当的过期时间等措施来加强保护。希望本文对您理解JWT的基本概念和应用有所帮助。

  • 相关阅读:
    golang工程——grpc 连接池简单实现
    postman做接口自动化测试
    开发实用篇——整合常用的第三方技术
    图像处理用什么神经网络,神经网络如何识别图像
    【算法设计与分析】— —实现最优载的贪心算法
    【pytorch源码分析--torch执行流程与编译原理】
    【离网逆变器】离网逆变器型号由一个高频DC-DC升压转换器与全桥PI控制电压源逆变器级联组成、逆变器使用带LC滤波器的SPWM调制(Simulink)
    【photoshop学习】用 Photoshop 做的 15 件创意事
    【算法】KMP算法——解决字符串匹配问题
    前端开发环境搭建 (version 2)
  • 原文地址:https://blog.csdn.net/2201_75869073/article/details/133787654