- 作者简介:一名后端开发人员,每天分享后端开发以及人工智能相关技术,行业前沿信息,面试宝典。
- 座右铭:未来是不可确定的,慢慢来是最快的。
- 个人主页:极客李华-CSDN博客
- 合作方式:私聊+
- 这个专栏内容:BAT等大厂常见后端java开发面试题详细讲解,更新数目100道常见大厂java后端开发面试题。
- 我的CSDN社区:https://bbs.csdn.net/forums/99eb3042821a4432868bb5bfc4d513a8
- 微信公众号,抖音,b站等平台统一叫做:极客李华,加入微信公众号领取各种编程资料,加入抖音,b站学习面试技巧,职业规划
如果大家觉得有用的话,可以关注我下面的微信公众号,极客李华,我会在里面更新更多行业资讯,企业面试内容,编程资源,如何写出可以让大厂面试官眼前一亮的简历等内容,让大家更好学习编程,我的抖音,B站也叫极客李华。大家喜欢也可以关注一下
在当今的互联网时代,身份验证和授权是保护应用程序和保护用户数据的关键。而 JSON Web Token (简称 JWT)是一种用于身份验证和授权的开放标准,广泛应用于web应用程序和API中。本文将深入介绍 JWT,包括其组成、工作原理以及常见的应用场景。
1. 什么是 JSON Web Token (JWT)?
JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式来在各方之间安全地传输信息。它是一个基于 JSON 格式的令牌,由三个部分组成:头部(Header)、载荷(Payload)、签名(Signature)。其中,每一部分都使用 Base64 编码,形成一个使用点进行分隔的字符串。
2. JWT 的组成
3. JWT 的工作原理
JWT 工作原理如下:
4. JWT 的应用场景
JWT 是一种灵活而强大的工具,可用于多种应用场景,包括:
# 1.引入依赖
com.auth0 java-jwt 3.4.0
# 2.生成token
// 创建一个 Calendar 实例,用于设置过期时间 Calendar instance = Calendar.getInstance(); // 在当前时间的基础上增加90秒 instance.add(Calendar.SECOND, 90); // 生成令牌 String token = JWT.create() .withClaim("username", "张三") // 设置自定义用户名 .withExpiresAt(instance.getTime()) // 设置过期时间为 Calendar 实例的时间 .sign(Algorithm.HMAC256("token!Q2W#E$RW")); // 使用 HMAC256 签名算法和密钥进行签名 // 输出令牌 System.out.println(token);
注释解释:
创建一个 Calendar 实例,表示当前时间,该实例用于设置过期时间。
将实例的时间增加90秒,作为令牌的过期时间。
使用 JWT.create() 方法创建一个 JWT 实例,用于生成令牌。
使用 withClaim("username", "张三") 方法设置自定义声明,在这里设置了用户名。
使用 withExpiresAt(instance.getTime()) 方法设置过期时间,将 instance 实例的时间设置为令牌的过期时间。
使用 sign(Algorithm.HMAC256("token!Q2W#E$RW")) 方法进行签名处理。使用 HMAC256 算法,并提供密钥进行签名。密钥字符串 "token!Q2W#E$RW" 在真正的系统中应该是保密且足够复杂的。
将生成的令牌存储在 token 变量中。
使用 System.out.println(token) 将令牌内容输出到控制台。
- 生成结果 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsicGhvbmUiLCIxNDMyMzIzNDEzNCJdLCJleHAiOjE1OTU3Mzk0NDIsInVzZXJuYW1lIjoi5byg5LiJIn0.aHmE3RNqvAjFr_dvyn_sD2VJ46P7EGiS5OBMO_TI5jg
# 3.根据令牌和签名解析数据
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("token!Q2W#E$RW")).build(); DecodedJWT decodedJWT = jwtVerifier.verify(token); System.out.println("用户名: " + decodedJWT.getClaim("username").asString()); System.out.println("过期时间: "+decodedJWT.getExpiresAt());
## 常见异常信息 - SignatureVerificationException: 签名不一致异常 - TokenExpiredException: 令牌过期异常 - AlgorithmMismatchException: 算法不匹配异常 - InvalidClaimException: 失效的payload异常
/** * JWTUtils 类用于生成和验证 JWT 令牌,以及获取令牌中的 payload。 */ public class JWTUtils { private static String TOKEN = "token!Q@W3e4r"; // 定义密钥 /** * 生成 JWT 令牌 * @param map 传入的 Payload 数据 * @return 返回生成的令牌 */ public static String getToken(Mapmap){ JWTCreator.Builder builder = JWT.create(); // 遍历传入的 Payload 数据,并添加到 Builder 中 map.forEach((k,v)->{ builder.withClaim(k,v); }); Calendar instance = Calendar.getInstance(); instance.add(Calendar.SECOND,7); // 设置过期时间为 7 秒后 builder.withExpiresAt(instance.getTime()); // 使用 HMAC256 签名算法进行签名,并返回令牌字符串 return builder.sign(Algorithm.HMAC256(TOKEN)).toString(); } /** * 验证 JWT 令牌 * @param token 要验证的令牌字符串 */ public static void verify(String token){ // 创建一个 JWTVerifier 实例,使用相同的密钥构建 JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token); } /** * 获取令牌中的 Payload 数据 * @param token 要解析的令牌字符串 * @return 解码后的令牌对象(DecodedJWT) */ public static DecodedJWT getToken(String token){ // 创建一个 JWTVerifier 实例,使用相同的密钥构建,并对令牌进行验证和解码 return JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token); } }
注释解释:
org.springframework.boot spring-boot-starter-web com.baomidou mybatis-plus-boot-starter 3.4.2 org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine com.baomidou mybatis-plus-boot-starter 3.4.2 com.auth0 java-jwt 3.4.0 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.3 org.projectlombok lombok 1.18.12 com.alibaba druid 1.1.19 mysql mysql-connector-java 8.0.32
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(80) DEFAULT NULL COMMENT '用户名', `password` varchar(40) DEFAULT NULL COMMENT '用户密码', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/** ** *
* * @author jakelihua * @since 2023-08-14 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("user") public class User implements Serializable { private static final long serialVersionUID = 1L; /** * 主键 */ @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * 用户名 */ private String name; /** * 用户密码 */ private String password; }
/** * InterceptorConfig 是一个配置类,用于添加拦截器。 * 在这个类中,我们可以配置需要拦截的接口路径以及排除不需要拦截的接口路径。 * 在这个例子中,我们添加了JWTInterceptor拦截器来对请求进行token验证, * 并设置了"/user/test"接口需要进行验证,而"/user/login"接口则被排除在验证之外,即所有用户都放行登录接口。 */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { /** * 添加拦截器配置 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JWTInterceptor()) .addPathPatterns("/user/test") // 对"/user/test"接口进行token验证 .excludePathPatterns("/user/login"); // 所有用户都放行登录接口 } }
@Data @NoArgsConstructor @AllArgsConstructor public class Result{ private int code; private String message; private T data; public Result(T data) { this.code = 200; this.message = "success"; this.data = data; } public Result(T data, boolean success, String message) { if (success) { this.code = 200; this.message = "success"; } else { this.code = 500; // 自定义错误状态码(示例为500) this.message = message; } this.data = data; } public Result(int code, String message) { this.code = code; this.message = message; this.data = null; } /** * 返回执行失败的结果(默认状态码为500) * * @param message 提示信息 * @return 失败的结果对象 */ public static Result fail(String message) { return new Result<>(500, message); } /** * 返回执行失败的结果(自定义状态码和提示信息) * * @param code 状态码 * @param message 提示信息 * @return 失败的结果对象 */ public static Result fail(int code, String message) { return new Result<>(code, message); } }
public class JWTUtils { private static final String SING = "!Q@W3e4r%T^Y"; /** * 生成token header.payload.sing */ public static String getToken(Mapmap){ Calendar instance = Calendar.getInstance(); instance.add(Calendar.DATE,7);//默认7天过期 //创建jwt builder JWTCreator.Builder builder = JWT.create(); //payload map.forEach((k,v)->{ builder.withClaim(k,v); }); String token = builder.withExpiresAt(instance.getTime())//指定令牌过期时间 .sign(Algorithm.HMAC256(SING));//sign return token; } /** * 验证token 合法性 * */ public static DecodedJWT verify(String token){ return JWT.require(Algorithm.HMAC256(SING)).build().verify(token); } // /** // * 获取token信息方法 // */ // public static DecodedJWT getTokenInfo(String token){ // DecodedJWT verify = JWT.require(Algorithm.HMAC256(SING)).build().verify(token); // return verify; // } }
/** * JWTInterceptor是一个拦截器,用于验证请求头中的JWT令牌是否有效。 * 当有请求进入时,该拦截器会首先从请求头中获取令牌,并尝试验证其有效性。 * 如果令牌验证成功,则放行请求;否则,拦截请求并返回相应的错误信息。 */ public class JWTInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 创建一个Map对象,用于存储响应信息 Mapmap = new HashMap<>(); // 从请求头中获取令牌 String token = request.getHeader("token"); try { JWTUtils.verify(token); // 验证令牌的有效性 return true; // 放行请求 } catch (SignatureVerificationException e) { e.printStackTrace(); map.put("msg", "无效签名!"); } catch (TokenExpiredException e) { e.printStackTrace(); map.put("msg", "token过期!"); } catch (AlgorithmMismatchException e) { e.printStackTrace(); map.put("msg", "token算法不一致!"); } catch (Exception e) { e.printStackTrace(); map.put("msg", "token无效!!"); } map.put("state", false); // 设置状态为false // 将Map转化为JSON字符串(使用Jackson库) String json = new ObjectMapper().writeValueAsString(map); response.setContentType("application/json;charset=UTF-8"); // 设置响应的Content-Type response.getWriter().println(json); // 将JSON字符串写入响应中 return false; // 不放行请求 } }
/** ** Mapper 接口 *
* * @author jakelihua * @since 2023-08-14 */ @Mapper public interface UserMapper extends BaseMapper{ @Select("select * from user where name = #{name} and password = #{password}") User login(@Param("name") String name, @Param("password") String password); }
/** ** 服务类 *
* * @author jakelihua * @since 2023-08-14 */ public interface IUserService extends IService{ User login(User user);//登录接口 }
/** ** 服务实现类 *
* * @author jakelihua * @since 2023-08-14 */ @Service public class UserServiceImpl extends ServiceImplimplements IUserService { @Autowired private UserMapper userMapper; @Override public User login(User user) { User userDB = userMapper.login(user.getName(), user.getPassword()); System.out.println(userDB); if (userDB != null){ return userDB; } throw new RuntimeException("登录失败~~"); } }
/** ** 前端控制器 *
* * @author jakelihua * @since 2023-08-14 */ @Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private IUserService userService; @GetMapping("/login") public Result
# 9.编写测试接口
@PostMapping("/test/test") public Maptest(String token) { Map map = new HashMap<>(); try { JWTUtils.verify(token); map.put("msg", "验证通过~~~"); map.put("state", true); } catch (TokenExpiredException e) { map.put("state", false); map.put("msg", "Token已经过期!!!"); } catch (SignatureVerificationException e){ map.put("state", false); map.put("msg", "签名错误!!!"); } catch (AlgorithmMismatchException e){ map.put("state", false); map.put("msg", "加密算法不匹配!!!"); } catch (Exception e) { e.printStackTrace(); map.put("state", false); map.put("msg", "无效token~~"); } return map; }
如果大家觉得有用的话,可以关注我下面的微信公众号,极客李华,我会在里面更新更多行业资讯,企业面试内容,编程资源,如何写出可以让大厂面试官眼前一亮的简历等内容,让大家更好学习编程,我的抖音,B站也叫极客李华。大家喜欢也可以关注一下