springboot整合JWT
一、封装工具类
public class JWTUtils {
private static String SECRET = "token!Q@W#E$R";
/**
* 生产token
*/
public static String getToken(Map<String, String> map) {
JWTCreator.Builder builder = JWT.create();
//payload
map.forEach((k, v) -> {
builder.withClaim(k, v);
});
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 7); //默认7天过期
builder.withExpiresAt(instance.getTime());//指定令牌的过期时间
String token = builder.sign(Algorithm.HMAC256(SECRET));//签名
return token;
}
/**
* 验证token
*/
public static DecodedJWT verify(String token) {
//如果有任何验证异常,此处都会抛出异常
DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
return decodedJWT;
}
}
二、搭建环境
1、数据库
就是一个表,三个字段
创建一个springboot项目,省略了。。。。
2、导入依赖包
mysql,lombok,mybatis-plus,jwt
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
3、数据库配置文件
#数据库连接配置
spring.datasource.username=root
spring.datasource.password=123456
#mysql5~8 驱动不同driver-class-name 8需要增加时区的配置serverTimezone=UTC
#useSSL=false 安全连接
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus_study_one?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
4、封装JWT工具类
public class JWTUtils {
private static String SECRET = "token!Q@W#E$R";
/**
* 生产token
*/
public static String getToken(Map<String, String> map) {
JWTCreator.Builder builder = JWT.create();
//payload
map.forEach((k, v) -> {
builder.withClaim(k, v);
});
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 7); //默认7天过期
builder.withExpiresAt(instance.getTime());//指定令牌的过期时间
String token = builder.sign(Algorithm.HMAC256(SECRET));//签名
return token;
}
/**
* 验证token
*/
public static DecodedJWT verify(String token) {
//如果有任何验证异常,此处都会抛出异常
DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
return decodedJWT;
}
}
5、实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Admin {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private String password;
}
6、service
public interface AdminService extends IService<Admin> {
/**
* 登录接口
*
* @param admin 表单中的user
* @return 数据库中查询到的User
*/
Admin login(Admin admin);
}
7、servicelmpl
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements AdminService {
@Autowired
private AdminMapper adminMapper;
@Override
public Admin login(Admin admin) {
QueryWrapper<Admin> wrapper = new QueryWrapper<>();
wrapper.eq("name",admin.getName());
wrapper.eq("password",admin.getPassword());
System.out.println(admin.getName());
System.out.println(admin.getPassword());
//若出现多个结果使用list或map
Admin adminDB = adminMapper.selectOne(wrapper); //查询一个数据,若出现多个结果使用list或map
if (adminDB != null) {
return adminDB;
}
throw new RuntimeException("认证失败");
}
}
8、mapper
@Repository
public interface AdminMapper extends BaseMapper<Admin> {
}
完成的目录结构如下:
三、生成token
写一下controller
@RestController
public class AdminController {
@Autowired
private AdminService adminService;
@GetMapping("/user/login")
public Map<String, Object> login(Admin admin) {
Map<String, Object> map = new HashMap<>();
try {
Admin adminDB = adminService.login(admin);
Map<String, String> payload = new HashMap<>();
payload.put("id", Integer.toString(adminDB.getId()));
payload.put("name", adminDB.getName());
String token = JWTUtils.getToken(payload);
map.put("state", true);
map.put("msg", "登录成功");
map.put("token", token);
return map;
} catch (Exception e) {
e.printStackTrace();
map.put("state", false);
map.put("msg", e.getMessage());
map.put("token", "");
}
return map;
}
}
使用postman,输入正确的账号密码,token创建成功
输入错误的账号密码,则失败
四、验证token
在admincontroller加上一个验证方法
@PostMapping("/user/test")
public Map<String, Object> test(String token) {
Map<String,Object> map = new HashMap<>();
System.out.println(token);
try {
DecodedJWT verify = JWTUtils.verify(token); //验证令牌
map.put("state",true);
map.put("msg","请求成功");
return map;
} 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);
return map;
}
生成一个token后验证,成功
把token修改之后,验证,失败
五、拦截器
通过上面的代码可以发现,是不是以后每写一个接口,都要先写一堆的验证代码验证一下啊?就非常的冗余,不合理,所以要解决掉这个问题。使用拦截器。
5.1 定义拦截器
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//获取请求头中的令牌
String token = request.getHeader("token");
System.out.println(token);
Map<String, Object> map = new HashMap<>();
try {
JWTUtils.verify(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 (InvalidClaimException e) {
e.printStackTrace();
map.put("msg", "失效的payload");
} catch (Exception e) {
e.printStackTrace();
map.put("msg", "token无效");
}
map.put("state", false);
//响应到前台: 将map转为json
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
5.2 注册拦截器
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/user/test") //这个是拦截的路径
.excludePathPatterns("/user/login") //这个是不拦截的路径
;
}
}
5.3 获取JWT中的payload
修改一下admincontroller
@PostMapping("/user/test")
public Map<String, Object> test(HttpServletRequest request) {
String token = request.getHeader("token");
DecodedJWT verify = JWTUtils.verify(token);
String id = verify.getClaim("id").asString();
String name = verify.getClaim("name").asString();
System.out.println(id);
System.out.println(name);
// 业务逻辑
Map<String, Object> map = new HashMap<>();
map.put("state", true);
map.put("msg", "请求成功");
return map;
}
添加修改完之后,整体是这样的
重启一下项目,验证一下效果
生成token之后,在头部加入token,OK没有问题