JWT+SpringBoot实战
往期内容:JWT - 炒焖煎糖板栗 - 博客园 (cnblogs.com)
JWT可以理解为一个加密的字符串,里面由三部分组成:头部(Header)、负载(Payload)、签名(signature)
由base64加密后的header和payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了JWT字符串
往期介绍了JWT相关概念以及基本操作,接下来介绍如何在SpringBoot中整合JWT实现登陆注册

环境搭建
1、新建一个SpringBoot项目Jwt-Demo,引入项目后面需要用到的jar包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--引入mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--引入mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!--引入druid数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.1</version>
</dependency>
<!--引入lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>2.1.3</version>
</dependency>
<!--引入jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
</dependencies>
2、数据库结构

有一个JWT库,里面还有一个User表
3、配置文件application.properties
server.port=8989
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/JWT?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=12345678
#mybatis扫描的包
mybatis.type-aliases-package=com.ylc
#mapper文件路径
mybatis.mapper-locations=classpath:/**/*.xml
#开启sql打印日志 logging.level后面是mybatis对应的方法接口所在的包
logging.level.com.ylc.jwtdemo.dao=debug
4、Entity包下新建一个User类
import lombok.Data;
@Data
public class User {
private String username;
private String password;
private int id;
}
5、Dao包下新建一个UserDao
@Mapper
public interface UserDao {
User login(User user);
}
6、Service包下新建一个USerService
public interface UserService {
User login(User user);//登录接口
}
7、UseService的实现类UserServiceImp
import java.util.HashMap;
import java.util.Map;
@Service
public class UserServiceImpI implements UserService {
@Autowired
private UserDao userDao;
@Override
public User login(User user) {
User userdb=userDao.login(user);
if(userdb!=null)
{
Map<String,String> map=new HashMap<>();
map.put("name",userdb.getUsername());
return userdb;
}
throw new RuntimeException("登录失败");
}
}
8、controller包下新建一个UserController
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/login")
public Map<String,Object> login(User user)
{
log.info("用户名:"+user.getUsername());
log.info("密码:"+user.getPassword());
Map<String,Object> map=new HashMap<>();
try {
userService.login(user);
map.put("msg","登录成功");
map.put("code","200");
}
catch (Exception ex)
{
map.put("msg","登录失败");
}
return map;
}
}
9、在resource文件夹下新建一个Usermapper文件
<?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">
<!--namespace 指的是要配置的全限定类名-->
<mapper namespace="com.ylc.jwtdemo.dao.UserDao">
<select id="login" parameterType="com.ylc.jwtdemo.entity.User" resultType="com.ylc.jwtdemo.entity.User">
select *from user where username=#{username} and password=#{password}
</select>
</mapper>
10、JWT工具类JwtUtils
/**
* JWT工具类
* @author yanglingcong
* @date 2021/12/31 11:24 AM
*/
public class JwtUtils {
//鉴权 相当于私钥保存在服务器上
private static final String secret="##@$%@#S#WS";
/**
* 生成token
* @author yanglingcong
* @date 2021/12/31 11:23 AM
* @param map
* @return String
*/
public static String getToken(Map<String,String> map)
{
Calendar instance=Calendar.getInstance();
//默认七天过期
instance.add(Calendar.DATE,7);
//创建JWT
JWTCreator.Builder builder = JWT.create();
//payload
map.forEach((k,v)->{
builder.withClaim(k,v);
});
//指定令牌过期时间
builder.withExpiresAt(instance.getTime());
String token=builder.sign(Algorithm.HMAC256(secret));
return token;
}
/**
* 验证token
* @author yanglingcong
* @date 2021/12/31 11:26 AM
* @param token
*/
public static DecodedJWT verify(String token) {
return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
}
}
整个项目概览

测试验证是否能够连通数据库
访问:localhost:8989/user/login?username=ylc&password=123456

引入JWT
@Slf4j
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/login")
public Map<String,Object> login(User user)
{
log.info("用户名:"+user.getUsername());
log.info("密码:"+user.getPassword());
Map<String,Object> map=new HashMap<>();
try {
userService.login(user);
map.put("msg","登录成功");
map.put("code","200");
Map<String,String> payload=new HashMap<>();
payload.put("name",user.getUsername());
String token= JwtUtils.getToken(payload);
map.put("token",token);
}
catch (Exception ex)
{
map.put("msg","登录失败");
}
return map;
}
@PostMapping("/test/verity")
public Map<String,String> verityToken(String token)
{
Map<String, String> map=new HashMap<>();
log.info("token为"+token);
try {
DecodedJWT verify = JwtUtils.verify(token);
map.put("msg","验证成功");
map.put("state","true");
}
catch (Exception exception)
{
map.put("msg","验证失败");
exception.printStackTrace();
}
return map;
}
}
登录操作
访问:http://localhost:8989/user/login?username=ylc&password=123456

验证操作
访问:http://localhost:8989/test/verity

但是我们这样写在实际项目中是不合理的,把token生成的代码放在了Controller中,业务逻辑是不能放在Controller层中的。假如很多接口都需要token来进行验证保护,那每一个接口都需要添加这样一段代码,造成代码冗余。
程序优化
如果是web项目使用拦截器进行优化,如果是springcloud项目在网关层进行拦截,下面演示如何使用拦截器拦截
最好还把JWT生成token放在http请求头,这样就不需要把token当成参数传递了
新建一个拦截器JwtInterceptor
/**
* JWT拦截器
* @author yanglingcong
* @date 2021/12/31 12:39 PM
*/
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HashMap<String, String> map=new HashMap<>();
//从http请求头获取token
String token = request.getHeader("token");
try {
//如果验证成功放行请求
DecodedJWT verify = JwtUtils.verify(token);
return true;
}
catch (Exception exception)
{
map.put("msg","验证失败:"+exception);
}
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json:charset=UTF=8");
response.getWriter().println(json);
return false;
}
}
然后把拦截器注册到过滤器中,新建一个过滤器InterceptConfig
/**
* @author yanglingcong
*/
@Configuration
public class InterceptConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
registry.addInterceptor(new JwtInterceptor())
//拦截的路径 需要进行token验证的路径
.addPathPatterns("/test/verity")
//放行的路径
.excludePathPatterns("/user/login");
}
}
登录是不需要拦截的,其他请求如果有需要验证token就放入拦截器的路径
测试验证
在http请求头中放入token,会被拦截器拦截验证token的有效性

总结
这就是SpringBoot整合JWT实际项目一个大概的流程,但是细节方面secret(私钥)肯定每个用户都是不一样的,这里给写死了,而且私钥得保存在一个安全的地方。包括payload部分不能存放敏感的密码信息等等,还可以进行优化。
JWT+SpringBoot实战的更多相关文章
- SpringBoot实战 之 异常处理篇
在互联网时代,我们所开发的应用大多是直面用户的,程序中的任何一点小疏忽都可能导致用户的流失,而程序出现异常往往又是不可避免的,那该如何减少程序异常对用户体验的影响呢?其实方法很简单,对异常进行捕获,然 ...
- apollo客户端springboot实战(四)
1. apollo客户端springboot实战(四) 1.1. 前言 经过前几张入门学习,基本已经完成了apollo环境的搭建和简单客户端例子,但我们现在流行的通常是springboot的客户端 ...
- 千锋很火的SpringBoot实战开发教程视频
springboot是什么? Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员 ...
- SpringBoot实战(四)获取接口请求中的参数(@PathVariable,@RequestParam,@RequestBody)
上一篇SpringBoot实战(二)Restful风格API接口中写了一个控制器,获取了前端请求的参数,现在我们就参数的获取与校验做一个介绍: 一:获取参数 SpringBoot提供的获取参数注解包括 ...
- SpringBoot实战(二)Restful风格API接口
在上一篇SpringBoot实战(一)HelloWorld的基础上,编写一个Restful风格的API接口: 1.根据MVC原则,创建一个简单的目录结构,包括controller和entity,分别创 ...
- SpringBoot实战之异常处理篇
在互联网时代,我们所开发的应用大多是直面用户的,程序中的任何一点小疏忽都可能导致用户的流失,而程序出现异常往往又是不可避免的,那该如何减少程序异常对用户体验的影响呢?其实方法很简单,对异常进行捕获,然 ...
- Docker深入浅出系列 | 单机Nginx+Springboot实战
目录 Nginx+Springboot实战 前期准备 实战目标 实战步骤 创建Docker网络 搭建Mysql容器 搭建额度服务集群 搭建Nginx服务 验证额度服务 附录 Nginx+Springb ...
- Shiro (Shiro + JWT + SpringBoot应用)
Shiro (Shiro + JWT + SpringBoot应用) 目录 Shiro (Shiro + JWT + SpringBoot应用) 1.Shiro的简介 2.Shiro + JWT + ...
- springboot实战开发全套教程,让开发像搭积木一样简单!Github星标已上10W+!
前言 先说一下,这份教程在github上面星标已上10W,下面我会一一给大家举例出来全部内容,原链接后面我会发出来!首先我讲一下接下来我们会讲到的知识和技术,对比讲解了多种同类技术的使用手日区别,大家 ...
随机推荐
- 求最长子序列(非连续)的STL方法 - 洛谷P1020 [NOIP1999 普及组] 导弹拦截
先给出例题:P1020 [NOIP1999 普及组] 导弹拦截 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 大佬题解:P1020 [NOIP1999 普及组] 导弹拦截 - 洛谷 ...
- midi的一些概念,包括一些音乐的概念
参考:http://www.yueqixuexi.com/jita/20180918205363.html https://blog.csdn.net/meicheng777/article/deta ...
- 创建Oracle数据库实例
创建Oracle数据库实例 转自oracle数据库创建实例 数据库已经安装完成,可以正常登陆查看用户等操作. system用户只能用normal身份登陆em.除非你对它授予了sysdba的系统权限或者 ...
- Kotlin 学习(2)
属性和字段 1.声明属性 Kotlin中可以使用var关键字声明可变属性,或者用val关键字声明只读属性,属性的类型在后面,变量名在前面,中间加冒号和空格. public class Address ...
- Linux学习 - fdisk分区
一.fdisk命令分区过程 系统一旦重启,分区将消失 1 添加新硬盘 直接在虚拟机上添加 2 查看新硬盘 fdisk -l 3 分区 fdisk /dev/sdb fdisk进入/dev/sdb硬件设 ...
- Servlet(1):Servlet介绍
一. Servlet介绍 Servlet 是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生 ...
- DP笔记
这是一篇蒟蒻被大佬踩爆后写的笔记 套路 0.贪心(废话)(排序...) 1.dp预处理出要用的东西 2.两头同时dp 3.化简题目中本质相同的东西 转化模型 4.数学计算优化 5.分析题目数据考虑该从 ...
- numpy基础教程--where函数的使用
在numpy中,where函数是一个三元运算符,函数原型为where(condition, x, y),意思是当条件成立的时候,将矩阵的值设置为x,否则设置为y 一个很简单的应用就是,在一个矩阵当中, ...
- C语言实现鼠标绘图
使用C语言+EGE图形库(Easy Graphics Engine).思路是通过不断绘制直线来实现鼠标绘图的功能,前一个时刻鼠标的坐标作为直线的起点,现在时刻的坐标作为终点(严格意义是线段而不是直线) ...
- 试工具_ab
目录 一.简介 二.例子 三.参数 一.简介 ab命令会创建多个并发访问线程,模拟多个访问者同时对某一URL地址进行访问.它的测试目标是基于URL的. 1.ab每次只能测试一个URL,适合做重复压力测 ...