整合JWT
令牌组成
- 1.标头(Header)
- 2.有效载荷(Payload)
- 3.签名(Signature)
因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz
Header.Payload.Signature
jwt组成
Header
标头通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA。它会使用 Base64 编码组成 JWT 结构的第一部分。
- 注意:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。
{ "alg": "HS256", "typ": "JWT" }
Payload
不要在这里放密码;反编译Base64即可解码;
- 令牌的第二部分是有效负载,其中包含声明。声明是有关实体
- (通常是用户)和其他数据的声明。同样的,它会使用 Base64 编码组成 JWT 结构的第二部分
{ "username": "xzxadmin", "datetime": "2023-05-01 11:11:11", "role": "admin" }
Signature
- 前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过
- 如:
HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret);
整合JWT
<!--引入jwt--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
JWT帮助类
public class JwtUtils { //常量 public static final long time = 1000 * 60 * 60 * 24;//token过期时间 public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";//秘钥 //生成token字符串的方法 public static String getJwtToken(String username, String role){ String JwtToken = Jwts.builder() //头部 .setHeaderParam("typ","JWT") .setHeaderParam("alg","HS256") //载荷 .claim("username",username) .claim("role",role) .setSubject("jwt-user") //token过期时间:1小时 .setExpiration(new Date(System.currentTimeMillis()+time )) .setId(UUID.randomUUID().toString())//id字段 //签名 .signWith(SignatureAlgorithm.HS256,APP_SECRET)//签名加密算法和 //连接字符串(.); .compact(); return JwtToken; } /** * 判断token是否存在与有效 * @param jwtToken * @return */ public static boolean checkToken(String jwtToken) { if(StringUtils.isEmpty(jwtToken)) return false; try { Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 判断token是否存在与有效 * @param request * @return */ public static boolean checkToken(HttpServletRequest request) { try { String jwtToken = request.getHeader("token"); if(StringUtils.isEmpty(jwtToken)) return false; Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 根据token获取会员id * @param request * @return */ public static String getMemberIdByJwtToken(HttpServletRequest request) { String jwtToken = request.getHeader("token"); if(StringUtils.isEmpty(jwtToken)) return ""; Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken); Claims claims = claimsJws.getBody(); return (String)claims.get("id"); } }
整合JWT
导入依赖
<!--引入jwt--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> <!--引入mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--引入lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <!--引入druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.19</version> </dependency> <!--引入mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency>
配置文件
server.port=8989 spring.application.name=jwt spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jwt?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC&useSSL=false spring.datasource.username=root spring.datasource.password=1234 mybatis.type-aliases-package=com.zuhao.springbootjwt.entity mybatis.mapper-locations=classpath:com/zuhao/springbootjwt/mapper/*.xml logging.level.com.zuhao.springbootjwt.dao=debug
JWT拦截器
interceptor包下新建拦截器
@Component @Slf4j public class LoginCheckInterceptor implements HandlerInterceptor { //目标资源方法执行前执行。 返回true:放行 返回false:不放行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //1,先获取请求头 String token = request.getHeader("Authorization"); System.out.println("token:"+token); response.setContentType("application/json;charset = UTF-8"); ObjectMapper mapper = new ObjectMapper(); //2,判断请求头是否存在 if (token == null || "".equals(token)){ //请求头不存在或者请求头为空 log.info("...token不存在"); response.getWriter().write("result:缺少token"); return false; }else{ try { boolean isJwt = JwtUtils.checkToken(token); //解析不出错就是格式有效; /*从redis中查询token*/ //时间有效,通过;时间无效,过期; } catch (Exception e) { log.info("请求头不正确!!"); response.getWriter().write("result:token错误:"); return false; } } return true; } //==========下面与登录无关,不用写============== //目标资源方法执行后执行 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle ... "); } //视图渲染完毕后执行,最后执行 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion .... "); } }
config包下添加拦截器
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private LoginCheckInterceptor loginCheckInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //定义拦截对象 registry.addInterceptor(loginCheckInterceptor) .addPathPatterns("/**") .excludePathPatterns( "/users/login", "/users/login/**", "/swagger-ui/", "/swagger-ui/**", "/swagger-resources", "/swagger-resources/**", "/v3/**", "/users/hello" ); } }
Jwt的优缺点
JWT(JSON Web Token)是一种用于在各方之间安全传输信息的紧凑、可自包含的方式。它广泛应用于身份认证和授权。以下是JWT的优缺点:
优点
1. 自包含性
JWT包含所有必要的信息,包括用户身份、有效期、权限等,这些信息被编码后存储在令牌中。由于JWT是自包含的,服务器不需要存储会话信息,可以直接通过解析JWT来获取所需信息,减少了服务器的负担。
2. 跨域支持
JWT可以在不同的域之间传输,适用于分布式系统和微服务架构中的跨域认证。它可以通过HTTP头、URL参数或其他方式在不同的系统或应用之间传递。
3. 简洁和紧凑
JWT采用Base64编码,格式简洁,占用空间小,适合在URL、HTTP头中传输。这种紧凑性使其适合在网络带宽有限的场景下使用。
4. 可扩展性
JWT可以在标准字段之外,添加自定义字段,如用户角色、权限等,灵活性很强,可以根据具体需求扩展。
5. 无需服务器存储会话信息
由于JWT是自包含的,服务器不需要存储会话数据,减少了服务器的存储和管理开销。这对需要横向扩展的系统非常有利。
6. 支持多种签名和加密算法
JWT支持多种签名算法,如HMAC、RSA、ECDSA等,可以根据安全需求选择合适的算法进行签名和加密,确保数据的完整性和保密性。
7. 广泛支持
JWT被广泛支持,很多现代开发框架和库都提供了对JWT的内置支持,使得集成变得非常容易。
缺点
1. 过期处理
JWT一旦签发,通常有效期是固定的。在有效期内,如果需要提前吊销或更新权限,比较困难。通常需要实现黑名单机制来管理已失效的JWT,但这又增加了服务器的存储和管理负担。
2. 安全性问题
由于JWT包含了所有的用户信息并在客户端存储,如果没有合适的加密和签名机制,容易被攻击者窃取和篡改。尤其是在使用对称加密时,密钥管理需要非常小心,否则整个系统的安全性都会受到威胁。
3. 信息暴露风险
虽然JWT是编码过的,但它并不是加密的,任何人都可以解码JWT并查看其中的信息。如果在JWT中存储了敏感信息,容易导致信息泄露风险。
4. 体积问题
JWT虽然相对紧凑,但在包含了大量信息后,仍然会变得比较大。特别是如果在JWT中嵌入了大量数据,会影响传输效率和性能。
5. 有限的支持复杂会话管理
JWT不适合复杂的会话管理场景,比如需要频繁更新会话信息的情况。因为JWT是无状态的,每次更新都需要重新生成新的令牌。
6. 缺乏标准的撤销机制
JWT没有内置的撤销机制,一旦签发出去,无法单独吊销某个JWT。这与传统的基于会话的认证机制不同,传统机制可以在服务器端随时终止会话。
7. 认证和授权的混淆
JWT可以用作认证令牌和授权令牌,容易在实际应用中混淆两者的用途。使用不当可能导致权限控制不严格,增加安全隐患。
JWT因其自包含、跨域支持和无需存储会话信息等优势,成为现代Web应用中流行的身份认证方式。然而,它的安全性和有效期管理方面的缺陷,也要求开发者在实际应用中谨慎设计和实现,确保系统的安全和高效。
关注我,不迷路,共学习,同进步