0


SpringBoot整合JWT

SpringBoot整合JWT

引言:

在当今的互联网时代,身份验证和授权是保护应用程序和保护用户数据的关键。而 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 的组成

  • 头部(Header):头部通常由两部分组成,即令牌的类型(通常使用 “JWT”)和所使用的算法(如 HMAC SHA256 或 RSA)。
  • 载荷(Payload):载荷包含有关声明或实体的声明。载荷可以包含例如用户名、用户ID、角色等相关信息。此外,JWT 还可以包含其他自定义的声明。
  • 签名(Signature):签名部分用于验证令牌的真实性,并确保它未被篡改。签名是通过将头部、载荷和一个秘密密钥进行加密生成的。

3. JWT 的工作原理
JWT 工作原理如下:

  • 客户端通过身份验证成功后,服务器将生成一个 JWT。
  • 服务器将 JWT 发送给客户端,并存储在客户端(通常是在 Cookie 或本地存储中)。
  • 客户端在每次请求时将 JWT 添加到请求的头部或参数中。
  • 服务器接收到请求后使用相同的密钥来验证 JWT 的真实性和完整性。
  • 如果验证成功,服务器根据 JWT 中的声明完成对用户的身份验证和授权操作。

4. JWT 的应用场景
JWT 是一种灵活而强大的工具,可用于多种应用场景,包括:

  • 用户认证:通过将用户信息存储在 JWT 中,实现用户身份验证和提供访问权限。
  • 单点登录:当用户在不同的应用程序之间切换时,只需使用 JWT 进行一次身份验证即可访问多个应用程序。
  • API 授权:通过在每个请求中添加 JWT,可以轻松地实现对 API 的授权访问,从而提高安全性。

引入JWT

# 1.引入依赖
<!--引入jwt--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>
# 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);

注释解释:

  1. 创建一个 Calendar 实例,表示当前时间,该实例用于设置过期时间。
  2. 将实例的时间增加90秒,作为令牌的过期时间。
  3. 使用 JWT.create() 方法创建一个 JWT 实例,用于生成令牌。
  4. 使用 withClaim("username", "张三") 方法设置自定义声明,在这里设置了用户名。
  5. 使用 withExpiresAt(instance.getTime()) 方法设置过期时间,将 instance 实例的时间设置为令牌的过期时间。
  6. 使用 sign(Algorithm.HMAC256("token!Q2W#E$RW")) 方法进行签名处理。使用 HMAC256 算法,并提供密钥进行签名。密钥字符串 "token!Q2W#E$RW" 在真正的系统中应该是保密且足够复杂的。
  7. 将生成的令牌存储在 token 变量中。
  8. 使用 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。
 */publicclassJWTUtils{privatestaticStringTOKEN="token!Q@W3e4r";// 定义密钥/**
     * 生成 JWT 令牌
     * @param map 传入的 Payload 数据
     * @return 返回生成的令牌
     */publicstaticStringgetToken(Map<String,String> map){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 要验证的令牌字符串
     */publicstaticvoidverify(String token){// 创建一个 JWTVerifier 实例,使用相同的密钥构建JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token);}/**
     * 获取令牌中的 Payload 数据
     * @param token 要解析的令牌字符串
     * @return 解码后的令牌对象(DecodedJWT)
     */publicstaticDecodedJWTgetToken(String token){// 创建一个 JWTVerifier 实例,使用相同的密钥构建,并对令牌进行验证和解码returnJWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token);}}

注释解释:

  1. TOKEN 是用来生成和验证 JWT 令牌所使用的密钥。
  2. getToken() 方法用于生成 JWT 令牌,接收一个 Map 类型的参数作为 Payload 数据,并返回生成的令牌字符串。
  3. 遍历传入的 Payload 数据,将每个键值对添加到 JWT 的 Builder 实例中。
  4. 获取当前时间,并在此基础上增加7秒,作为令牌的过期时间。
  5. 使用 Builder 设置令牌的过期时间。
  6. 使用 HMAC256 签名算法和密钥对令牌进行签名,并将其转换成字符串返回。
  7. verify() 方法用于验证 JWT 令牌,接收令牌字符串作为参数。创建一个 JWTVerifier 实例,使用相同的密钥进行构建,并对令牌进行验证。
  8. getToken() 方法用于获取令牌中的 Payload 数据。接收要解析的令牌字符串作为参数。创建一个 JWTVerifier 实例,使用相同的密钥构建,并对令牌进行验证和解码。返回解码后的令牌对象(DecodedJWT)。

整合springboot

项目结构

在这里插入图片描述

导入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><!--引入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>8.0.32</version></dependency>

数据库准备

DROPTABLEIFEXISTS`user`;CREATETABLE`user`(`id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',`name`varchar(80)DEFAULTNULLCOMMENT'用户名',`password`varchar(40)DEFAULTNULLCOMMENT'用户密码',PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=2DEFAULTCHARSET=utf8;

在这里插入图片描述

后端代码编写

Bean
User
/**
 * <p>
 * 
 * </p>
 *
 * @author jakelihua
 * @since 2023-08-14
 */@Data@EqualsAndHashCode(callSuper =false)@Accessors(chain =true)@TableName("user")publicclassUserimplementsSerializable{privatestaticfinallong serialVersionUID =1L;/**
     * 主键
     */@TableId(value ="id", type =IdType.AUTO)privateInteger id;/**
     * 用户名
     */privateString name;/**
     * 用户密码
     */privateString password;}
Config
InterceptorConfig
/**
 * InterceptorConfig 是一个配置类,用于添加拦截器。
 * 在这个类中,我们可以配置需要拦截的接口路径以及排除不需要拦截的接口路径。
 * 在这个例子中,我们添加了JWTInterceptor拦截器来对请求进行token验证,
 * 并设置了"/user/test"接口需要进行验证,而"/user/login"接口则被排除在验证之外,即所有用户都放行登录接口。
 */@ConfigurationpublicclassInterceptorConfigimplementsWebMvcConfigurer{/**
     * 添加拦截器配置
     */@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(newJWTInterceptor()).addPathPatterns("/user/test")// 对"/user/test"接口进行token验证.excludePathPatterns("/user/login");// 所有用户都放行登录接口}}
Result
@Data@NoArgsConstructor@AllArgsConstructorpublicclassResult<T>{privateint code;privateString message;privateT data;publicResult(T data){this.code =200;this.message ="success";this.data = data;}publicResult(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;}publicResult(int code,String message){this.code = code;this.message = message;this.data =null;}/**
     * 返回执行失败的结果(默认状态码为500)
     *
     * @param message 提示信息
     * @return 失败的结果对象
     */publicstatic<T>Result<T>fail(String message){returnnewResult<>(500, message);}/**
     * 返回执行失败的结果(自定义状态码和提示信息)
     *
     * @param code    状态码
     * @param message 提示信息
     * @return 失败的结果对象
     */publicstatic<T>Result<T>fail(int code,String message){returnnewResult<>(code, message);}}
Utils
JWTUtils
publicclassJWTUtils{privatestaticfinalStringSING="!Q@W3e4r%T^Y";/**
     * 生成token  header.payload.sing
     */publicstaticStringgetToken(Map<String,String> map){Calendar instance =Calendar.getInstance();
        instance.add(Calendar.DATE,7);//默认7天过期//创建jwt builderJWTCreator.Builder builder =JWT.create();//payload
        map.forEach((k,v)->{
            builder.withClaim(k,v);});String token = builder.withExpiresAt(instance.getTime())//指定令牌过期时间.sign(Algorithm.HMAC256(SING));//signreturn token;}/**
     * 验证token 合法性
     *
     */publicstaticDecodedJWTverify(String token){returnJWT.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;//    }}
Interceptor
JWTInterceptor
/**
 * JWTInterceptor是一个拦截器,用于验证请求头中的JWT令牌是否有效。
 * 当有请求进入时,该拦截器会首先从请求头中获取令牌,并尝试验证其有效性。
 * 如果令牌验证成功,则放行请求;否则,拦截请求并返回相应的错误信息。
 */publicclassJWTInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{// 创建一个Map对象,用于存储响应信息Map<String,Object> map =newHashMap<>();// 从请求头中获取令牌String token = request.getHeader("token");try{JWTUtils.verify(token);// 验证令牌的有效性returntrue;// 放行请求}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 =newObjectMapper().writeValueAsString(map);

        response.setContentType("application/json;charset=UTF-8");// 设置响应的Content-Type
        response.getWriter().println(json);// 将JSON字符串写入响应中returnfalse;// 不放行请求}}
Mapper
UserMapper
/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author jakelihua
 * @since 2023-08-14
 */@MapperpublicinterfaceUserMapperextendsBaseMapper<User>{@Select("select * from user where name = #{name} and password = #{password}")Userlogin(@Param("name")String name,@Param("password")String password);}
Service
IUserService
/**
 * <p>
 *  服务类
 * </p>
 *
 * @author jakelihua
 * @since 2023-08-14
 */publicinterfaceIUserServiceextendsIService<User>{Userlogin(User user);//登录接口}
UserServiceImpl
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author jakelihua
 * @since 2023-08-14
 */@ServicepublicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{@AutowiredprivateUserMapper userMapper;@OverridepublicUserlogin(User user){User userDB = userMapper.login(user.getName(), user.getPassword());System.out.println(userDB);if(userDB !=null){return userDB;}thrownewRuntimeException("登录失败~~");}}
Controller
UserController
/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author jakelihua
 * @since 2023-08-14
 */@Slf4j@RestController@RequestMapping("/user")publicclassUserController{@AutowiredprivateIUserService userService;@GetMapping("/login")publicResult<Map<String,Object>>login(User user){// 打印用户名和密码
        log.info("用户名: [{}]", user.getName());
        log.info("密码: [{}]", user.getPassword());// 创建结果对象Result<Map<String,Object>> result;try{// 调用userService的login方法进行用户认证User userDB = userService.login(user);// 获取用户ID和用户名,并将其放入payloadMap<String,String> payload =newHashMap<>();
            payload.put("id", userDB.getId().toString());
            payload.put("name", userDB.getName());// 生成JWT的令牌String token =JWTUtils.getToken(payload);// 构造成功的结果对象
            result =newResult<>(200,"认证成功");
            result.setData(newHashMap<>());
            result.getData().put("token", token);// 响应token}catch(Exception e){// 构造失败的结果对象
            result =Result.fail(500, e.getMessage());}return result;}@PostMapping("/test")publicResult<Map<String,Object>>test(HttpServletRequest request){// 创建结果对象Result<Map<String,Object>> result;try{Map<String,Object> map =newHashMap<>();// 处理自己的业务逻辑// 从请求头中获取tokenString token = request.getHeader("token");// 校验并解析tokenDecodedJWT verify =JWTUtils.verify(token);// 打印解析出的用户id和用户名
            log.info("用户id: [{}]", verify.getClaim("id").asString());
            log.info("用户name: [{}]", verify.getClaim("name").asString());// 构造成功的结果对象
            result =newResult<>(200,"请求成功!");
            result.setData(map);}catch(Exception e){// 构造失败的结果对象
            result =Result.fail(500, e.getMessage());}return result;}}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

# 9.编写测试接口
@PostMapping("/test/test")publicMap<String,Object>test(String token){Map<String,Object> map =newHashMap<>();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;}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

标签: spring boot java 后端

本文转载自: https://blog.csdn.net/qq_51447496/article/details/132216505
版权归原作者 极客李华 所有, 如有侵权,请联系我们删除。

“SpringBoot整合JWT”的评论:

还没有评论