jwt整合springboot
JWT(JSON Web Token)整合Spring Boot主要是用于实现基于token的身份验证和授权,在这种机制中,服务器会为经过身份验证的用户生成一个签名的token,用户在后续的请求中需要携带这个token来证明自己的身份。
以下是JWT整合Spring Boot的主要功能:
- 用户认证:用户首次登录时,服务器会验证用户的凭证(如用户名和密码)。如果凭证有效,服务器会生成一个JWT并返回给用户。
- 请求授权:当用户进行后续的请求时,需要在请求头中携带JWT。服务器会验证这个JWT,如果验证通过,服务器就会处理用户的请求。否则,服务器会拒绝请求。
- 无状态:由于JWT自身包含了用户的身份信息,服务器不需要保存用户的session信息,这使得应用更易于扩展。
- 跨域认证:JWT是自包含的,它不依赖于特定的认证方案。无论用户处于哪个域,只要用户的请求中包含了有效的JWT,服务器就能验证用户的身份。
在Spring Boot中整合JWT(JSON Web Token)主要涉及以下几个步骤:
1.添加依赖:
在您的pom.xml或build.gradle文件中,添加JWT和Spring Security的依赖。
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.2</version>
</dependency>
2.编写JwtUtils,提高代码重用率:
这是一个名为JwtUtils
的工具类,它包含两个静态方法:createToken
和verifyToken
。这个类用于创建和验证JSON Web Tokens (JWTs)。
createToken
方法接受一个包含JWT声明的Map作为参数,然后使用这些声明和一个预定义的密钥来创建一个新的JWT。创建的JWT将在一段预定义的时间后过期。
verifyToken
方法接受一个JWT作为参数,然后使用预定义的密钥来验证这个JWT。如果JWT是有效的,那么这个方法将返回一个DecodedJWT
对象,否则它将抛出一个异常。
完整代码如下:
package com.example.jwtdemo.demos.jwt.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
// JwtUtils类用于创建和验证JWT
public class JwtUtils {
// 过期时间单位:秒,默认半小时过期
private static final long EXPIRATION = 60 * 30L * 100;
// 密钥
private static final String SECRET = "lwh";
// createToken方法用于创建JWT
public static String createToken(Map<String, String> claimMap) {
// 打印过期时间
System.out.println(EXPIRATION);
// 打印当前时间戳加上设定的毫秒数
System.out.println(new Date().getTime()+EXPIRATION);
// 创建过期时间对象
Date expiration = new Date(new Date().getTime()+EXPIRATION);
// 创建一个新的HashMap来存储Jwt头部信息
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
// 创建JWT的构建器
JWTCreator.Builder builder = JWT.create();
// 打印创建Token的参数claimMap
System.out.println("创建Token的参数claimMap:" + claimMap);
// 使用Lambda表达式创建payload
claimMap.forEach(builder::withClaim);
// 打印过期时间
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(expiration));
// 返回创建的JWT
return builder.withHeader(map)
.withExpiresAt(expiration)
.sign(Algorithm.HMAC256(SECRET));
}
// verifyToken方法用于验证JWT
public static DecodedJWT verifyToken(String token) {
// 验证JWT
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
// 打印验证Token的函数得到的Token
System.out.println("验证Token的函数得到的Token:" + verify.getToken());
// 打印验证Token的函数得到的Header
System.out.println("验证Token的函数得到的Header:" + verify.getHeader());
// 返回验证的JWT
return verify;
}
}
3.编写过滤器:
这是一个名为JwtInterceptor
的类,它实现了Spring框架的HandlerInterceptor
接口。这个类主要用于拦截HTTP请求,验证请求头中的JWT(JSON Web Token),并根据验证结果决定是否放行请求。
以下是对这个类中各个部分的详细介绍:
- preHandle方法:这个方法在请求被处理之前被调用。它首先从请求头中获取JWT,然后尝试验证这个JWT。如果JWT验证成功,那么这个方法将返回
true
,请求将被放行。如果JWT验证失败,那么这个方法将返回false
,请求将被拦截,同时向客户端发送一个包含错误信息的JSON响应。 - afterCompletion方法:这个方法在请求被处理之后被调用,无论处理过程中是否发生异常。在这个方法中,我们调用了父类
HandlerInterceptor
的afterCompletion
方法,但没有进行任何其他操作。 - postHandle方法:这个方法在请求被处理之后,但在视图被渲染之前被调用。在这个方法中,我们调用了父类
HandlerInterceptor
的postHandle
方法,但没有进行任何其他操作。
这个类的主要作用是提供JWT的验证功能,它可以被用于保护需要认证的API端点。
完整代码如下:
package com.example.jwtdemo.demos.jwt.utils;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {
Map<String,Object> map = new HashMap<>();
//获取请求头中的令牌
String token = request.getHeader("token");
System.out.println("拦截器中的token是"+token);
try {
//验证令牌
JwtUtils.verifyToken(token);
//验证成功,放行请求
return true;
}catch (SignatureVerificationException e){
e.printStackTrace();
map.put("msg","无效签名");
}catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","令牌已过期");
}catch (AlgorithmMismatchException e){
e.printStackTrace();
map.put("msg","令牌算法不匹配");
}catch (Exception e){
e.printStackTrace();
map.put("msg","令牌验证失败");
}
//设置状态
map.put("statle",false);
//将map转化为json
String json = new ObjectMapper().writeValueAsString(map);
//相应json数据
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return false;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
}
4.配置过滤器:
这是一个名为JwtConfig
的配置类,它实现了Spring框架的WebMvcConfigurer
接口。这个类主要用于配置Spring MVC的拦截器。
以下是对这个类中各个部分的详细介绍:
- @Configuration注解:这个注解表明这个类是一个配置类,它会被Spring容器特别对待。Spring容器会在启动时自动扫描到这个类,并根据这个类中的配置信息来配置Spring应用程序。
- WebMvcConfigurer接口:这个接口提供了一种方式来自定义Spring MVC的配置。通过实现这个接口,您可以覆盖它的方法来添加自定义的配置。
- addInterceptors方法:这个方法是
WebMvcConfigurer
接口的一个方法,它用于添加自定义的拦截器。在这个方法中,我们创建了一个新的JwtInterceptor
实例,并将它添加到了拦截器注册表中。 - addPathPatterns方法:这个方法用于设置拦截器需要拦截的URL路径模式。在这个例子中,我们设置了
"/**"
,这意味着拦截器将拦截所有的URL路径。 - excludePathPatterns方法:这个方法用于设置拦截器需要排除的URL路径模式。在这个例子中,我们设置了
"/user/login"
,这意味着拦截器将不会拦截到这个URL路径。
这个类的主要作用是配置JWT的拦截器,它可以被用于保护需要认证的API端点。
完整代码如下:
// 导入相关的类和接口
import com.example.jwtdemo.demos.jwt.utils.JwtInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// 使用@Configuration注解标记这个类是一个配置类
@Configuration
/**
* JwtConfig类实现了WebMvcConfigurer接口,用于配置Spring MVC的拦截器。
*/
public class JwtConfig implements WebMvcConfigurer {
// 覆盖WebMvcConfigurer接口的addInterceptors方法
@Override
/**
* addInterceptors方法用于添加自定义的拦截器。
* @param registry 拦截器注册表。
*/
public void addInterceptors(InterceptorRegistry registry) {
// 创建一个新的JwtInterceptor实例,并将它添加到拦截器注册表中
registry.addInterceptor(new JwtInterceptor())
// 设置拦截器需要拦截的URL路径模式
.addPathPatterns("/**")
// 设置拦截器需要排除的URL路径模式
.excludePathPatterns("/user/login");
}
}
5.编写properties.yaml:
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///interface?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
6.编写实体类:
package com.example.jwtdemo.demos.jwt.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private int id;
private String username;
private String password;
}
7.使用mybatis Plus(mybatis略过此步骤):
7.1引入依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
7.2配置文件(有不用动):
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///interface?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
8.编写XML(mybatis Plus略过此步骤):
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 用户配置信息 -->
<mapper namespace="com.example.jwtdemo.demos.jwt.mapper.UserMapper">
<!--User login(User user)-->
<select id="login" resultType="com.example.jwtdemo.demos.jwt.entity.User">
select * from user where username = #{username} and password = #{password}
</select>
</mapper>
9.编写mapper:
package com.example.jwtdemo.demos.jwt.mapper;
import com.example.jwtdemo.demos.jwt.entity.User;
import org.springframework.stereotype.Component;
@Component
public interface UserMapper {
User login(User user);
}
10.编写Service:
package com.example.jwtdemo.demos.jwt.service;
import com.example.jwtdemo.demos.jwt.entity.User;
public interface UserService {
User login(User user);
}
11.编写Service实现:
package com.example.jwtdemo.demos.jwt.service.impl;
import com.example.jwtdemo.demos.jwt.entity.User;
import com.example.jwtdemo.demos.jwt.mapper.UserMapper;
import com.example.jwtdemo.demos.jwt.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserServiceImpl implements UserService {
@Resource
UserMapper userMapper;
@Override
public User login(User user) {
return userMapper.login(user);
}
}
12.编写控制器:
这是一个名为UserHandler
的控制器类,它处理与用户相关的HTTP请求。这个类使用了Spring的@RestController
注解,这意味着它的所有方法都会返回一个对象,这个对象会被自动转换为JSON格式的响应。
以下是对这个类中各个部分的详细介绍:
- @RestController注解:这个注解表明这个类是一个控制器,它的所有方法都会返回一个对象,这个对象会被自动转换为JSON格式的响应。
- @RequestMapping注解:这个注解用于映射HTTP请求到特定的处理方法。在这个例子中,我们设置了
"/user"
,这意味着这个控制器将处理所有以"/user"
开头的URL路径。 - @Slf4j注解:这个注解是Lombok库的一部分,它在类中自动创建一个SLF4J(Simple Logging Facade for Java)的日志对象。这个日志对象可以用于记录日志信息。
- login方法:这个方法处理
"/login"
的GET请求。它接受一个User
对象作为参数,然后调用userService
的login
方法来验证用户的用户名和密码。如果用户登录成功,那么这个方法将创建一个新的JWT,并将它添加到响应数据中。 - test方法:这个方法处理
"/other"
的POST请求。它从请求头中获取JWT,然后使用JwtUtils
的verifyToken
方法来验证这个JWT。如果JWT验证成功,那么这个方法将返回一个包含成功消息的响应。
完整代码如下:
// 导入相关的类和接口
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.jwtdemo.demos.jwt.entity.User;
import com.example.jwtdemo.demos.jwt.service.UserService;
import com.example.jwtdemo.demos.jwt.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
// 使用@RestController注解标记这个类是一个控制器
@RestController
// 使用@RequestMapping注解设置这个控制器处理的URL路径
@RequestMapping("/user")
// 使用@Slf4j注解自动创建一个SLF4J的日志对象
@Slf4j
/**
* UserHandler类是一个控制器,它处理与用户相关的HTTP请求。
*/
public class UserHandler {
// 使用@Resource注解注入UserService
@Resource
UserService userService;
// 使用@GetMapping注解来处理HTTP GET请求
@GetMapping("/login")
/**
* login方法处理"/login"的GET请求。
* @param user 包含用户信息的User对象。
* @return 返回一个包含响应数据的Map。
*/
public Map<String, Object> login(User user) {
// 记录用户的用户名和密码
log.info("用户名: [{}], 密码: [{}]", user.getUsername(), user.getPassword());
// 创建一个新的HashMap来存储响应数据
Map<String, Object> map = new HashMap<>();
try {
// 创建一个新的HashMap来存储JWT的payload
Map<String, String> payload = new HashMap<>();
// 调用userService的login方法来验证用户的用户名和密码
User login = userService.login(user);
// 打印登录用户的信息
System.out.println("用户信息:" + login);
// 如果用户登录成功
if (null != login) {
// 将用户的id,用户名和密码添加到JWT的payload中
payload.put("id", user.getId() + "");
payload.put("name", user.getUsername());
payload.put("password", user.getPassword());
// 使用JwtUtils的createToken方法来生成JWT
String token = JwtUtils.createToken(payload);
// 将登录状态设置为true
map.put("state", true);
// 设置响应消息为"登录成功"
map.put("msg", "登录成功");
// 将生成的JWT添加到响应数据中
map.put("token", token);
}
}
catch (Exception e){
// 如果在登录过程中发生异常
// 将登录状态设置为false
map.put("state", false);
// 设置响应消息为异常的消息
map.put("msg", e.getMessage());
}
// 返回响应数据
return map;
}
@PostMapping("/other")
/**
* test方法处理"/other"的POST请求。
* @param request HttpServletRequest对象,包含了请求的信息。
* @return 返回一个包含响应数据的Map。
*/
public Map<String, Object> test(HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
//获取请求头里的token
String token = request.getHeader("token");
DecodedJWT verify = JwtUtils.verifyToken(token);
log.info("[ID]:[{}], [Name]: [{}]",verify.getClaim("id").asString(),verify.getClaim("name").asString());
map.put("state", true);
map.put("msg", "请求成功");
return map;
}
}
13.数据库表结构:
14.测试:
14.1:登录测试:
{
"msg": "登录成功",
"state": true,
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEiLCJuYW1lIjoid2FuZyIsImlkIjoiMCIsImV4cCI6MTcwMzIzNTQ5M30.KFRpZ6C81LOXXEuv7QJ3BTV14-_K4aHIeYqvf-tUeP0"
}
14.2请求其他接口:
{
"msg": "请求成功",
"state": true
}
15.更换名称:
这是一个名为MyConfig
的配置类,它实现了Spring框架的WebMvcConfigurer
接口。这个类主要用于配置Spring MVC的CORS(跨源资源共享)。
以下是对这个类中各个部分的详细介绍:
- @Configuration注解:这个注解表明这个类是一个配置类,它会被Spring容器特别对待。Spring容器会在启动时自动扫描到这个类,并根据这个类中的配置信息来配置Spring应用程序。
- WebMvcConfigurer接口:这个接口提供了一种方式来自定义Spring MVC的配置。通过实现这个接口,您可以覆盖它的方法来添加自定义的配置。
- addCorsMappings方法:这个方法是
WebMvcConfigurer
接口的一个方法,它用于添加CORS映射。在这个方法中,我们添加了一个新的CORS映射,允许所有的URL路径,允许来自http://example.com
的跨域请求,允许GET、POST、PUT和DELETE方法,以及允许"MyCustomToken"、"Content-Type"和"X-Requested-With"请求头。
这个类的主要作用是配置CORS,它可以被用于允许来自特定源的跨域请求。
完整代码如下:
// 导入相关的类和接口
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// 使用@Configuration注解标记这个类是一个配置类
@Configuration
/**
* MyConfig类实现了WebMvcConfigurer接口,用于配置Spring MVC的CORS。
*/
public class MyConfig implements WebMvcConfigurer {
// 覆盖WebMvcConfigurer接口的addCorsMappings方法
@Override
/**
* addCorsMappings方法用于添加CORS映射。
* @param registry CORS注册表。
*/
public void addCorsMappings(CorsRegistry registry) {
// 添加一个新的CORS映射
registry.addMapping("/**")
// 设置允许跨域的域名
.allowedOrigins("[1](http://example.com)") // 允许跨域的域名
// 设置允许的请求方法
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
// 设置允许的请求头
.allowedHeaders("MyCustomToken", "Content-Type", "X-Requested-With"); // 允许的请求头
}
}