当前位置: 首页 > news >正文

在Spring boot中 使用JWT和过滤器实现登录认证

在Spring boot中 使用JWT和过滤器实现登录认证

一、登录获得JWT

  1. 在navicat中运行如下sql,准备一张user表
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (`id` int(11) NOT NULL,`username` varchar(50) NOT NULL,`password` varchar(32) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES ('1', 'root', '123', '1');
  1. 导入pom.xml坐标
		<!-- JWT依赖坐标--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><!-- 解析JSON--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.15.2</version></dependency>
  1. 在工utils包下创建一个用于生成和解析JWT 令牌的工具类
public class JwtUtils {private static String signKey = "baisepengyuyan";//签名密钥(这个可以自定义)private static Long expire = 86400000L; //有效时间 1 天 = 24 小时 * 60 分钟 * 60 秒 * 1000 毫秒 = 86,400,000 milliseconds/*生成JWT令牌*/public static String generateJwt(Map<String, Object> claims){String jwt = Jwts.builder().addClaims(claims)//自定义信息(有效载荷).signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部).setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间.compact();return jwt;}/*解析JWT令牌 */public static Claims parseJWT(String jwt){Claims claims = Jwts.parser().setSigningKey(signKey)//指定签名密钥.parseClaimsJws(jwt)//指定令牌Token.getBody();return claims;		//返回令牌中 payload 中存储的内容(登录用户的信息)}
}
  1. 在pojo包下创建user类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private int id;private String name;private String username;private String password;
}
  1. 在mapper包下添加 UserMapper接口
@Mapper
public interface UserMapper {@Select("select id ,username ,password from t_user where username = #{username} and password= #{password}")User getLoginUser(User user);
}
  1. 在service包下添加 UserService类
@Service
public class UserService {@AutowiredUserMapper userMapper;public User login(User user) {User loginUser = userMapper.getLoginUser(user);if (loginUser == null )return null;return user;}
}
  1. 在utils包下添加统一响应结果封装类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {private Integer code ;//1 成功 , 0 失败private String msg; //提示信息private Object data; //数据 datapublic static Result success(Object data){return new Result(1, "success", data);}public static Result success(){return new Result(1, "success", null);}public static Result error(String msg){return new Result(0, msg, null);}@Overridepublic String toString() {return "Result{" +"code=" + code +", msg='" + msg + '\'' +", data=" + data +'}';}
}
  1. 在controller包下添加LoginController类
@RestController
@Slf4j
public class LoginController {//依赖业务层对象@Autowiredprivate UserService userService;@PostMapping("/login")public Result login(@RequestBody User user) {//调用业务层:登录功能User loginEmp = userService.login(user);//判断:登录用户是否存在if(loginEmp !=null ){//自定义信息Map<String , Object> claims = new HashMap<>();claims.put("id", loginEmp.getId());claims.put("username",loginEmp.getUsername());claims.put("name",loginEmp.getName());//使用JWT工具类,生成身份令牌String token = JwtUtils.generateJwt(claims);return Result.success(token);}return Result.error("用户名或密码错误");}
}

这样登录获取token的接口就写好了,接下来我们来用post工具来测一下这个登录接口

这里我使用的工具是Apifox

在这里插入图片描述

这样我们就拿到了 这个token (这个token的有效期是一天, 有效时间的长短可以在JWT工具类那里设置)

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpudWxsLCJpZCI6MCwidXNlcm5hbWUiOiJtYWkiLCJleHAiOjE2OTg2NTg1OTh9.aFDGnKvMOjgpZjdgXcQfaDM1ONupOvdwYtYNxPAaN6s

这样我们就将这个登录获取JWT的功能开发好了,但是, 现在我们仍然可以访问别的请求路径,不被拦截,所以我们要引入过滤器.

二、引入过滤器

​ 引入过滤器的就是为了,限制为未登录的用户。不让他们访问除了登录以外的资源。

那现在我们写一个测试用例,用于测试, 看看没引入过滤器和引入过滤器的区别

  • 在LoginController类下添加这个方法
    @GetMapping("/getResource")public Result getUser(){return Result.success("成功访问/getResource");}

添加完这个方法后 我们尝试访问 http://localhost:8080/getResource

在这里插入图片描述

这是我们可以看到用户在未登录的情况下, 仍然可以正常访问我们的资源, 这是我们不希望看到的.

那么我们开始引入过滤器

  1. 在启动类上添加 @ServletComponentScan注解
@ServletComponentScan
  1. 创建一个filter包 ,在这个包下创建 LoginCheckFilter类
@Slf4j
@WebFilter(urlPatterns = "/*") //拦截所有请求
public class LoginCheckFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {//前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;//创建json解析对象ObjectMapper objectMapper = new ObjectMapper();//1.获取请求urlString url = request.getRequestURL().toString();log.info("请求路径:{}", url); //请求路径:http://localhost:8080/login//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行if(url.contains("/login")){chain.doFilter(request, response);//放行请求return;//结束当前方法的执行}//3.获取请求头中的令牌(token)String token = request.getHeader("Authorization");log.info("从请求头中获取的令牌:{}",token);//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)if(!StringUtils.hasLength(token)){log.info("Token不存在");Result responseResult = Result.error("NOT_LOGIN");//把Result对象转换为JSON格式字符串String json = objectMapper.writeValueAsString(responseResult);response.setContentType("application/json;charset=utf-8");//响应response.getWriter().write(json);return;}//5.解析token,如果解析失败,返回错误结果(未登录)try {if (token.startsWith("Bearer ")) {token = token.substring(7);log.info("切除Bearer 令牌:{}",token);}JwtUtils.parseJWT(token);}catch (Exception e){log.info("令牌解析失败!");Result responseResult = Result.error("NOT_LOGIN");//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)String json = objectMapper.writeValueAsString(responseResult);response.setContentType("application/json;charset=utf-8");//响应response.getWriter().write(json);return;}//6.放行chain.doFilter(request, response);}
}

阅读代码我们可以发现, 我们拦截了所有请求,唯独给登录请求放行

现在我们再来尝试访问

image-20231029182028350

但是新的问题来了 , 我们要怎么访问这个资源呢? 对, 使用刚刚我们登录后返回的JWT令牌

我们在每次请求时都在HTTP的请求头中携带JWT令牌过去

访问 http://localhost:8080/login 获得令牌

拿到令牌后 我们访问http://localhost:8080/getResource

在请求中 添加Authorization请求头 值为 Bearer + 空格 + JWT

image-20231029183124021

我们可以看到 在实际的请求总 我们多谢了一个请求头 请求头和请求参数是不一样的!!!

image-20231029184529157

到此 我们已经完成了一个Spring Boot整合JWT和过滤器 完成登录验证的功能了.

相关文章:

  • 【年终特惠】全流程HEC-RAS 1D/2D水动力与水环境模拟技术案例实践及拓展应用
  • 9.Python3-注释
  • 20年经典传承 | 性能圣典!火焰图发明者Brendan Gregg“神作”
  • Linux cp命令:复制文件和目录
  • 【git】git使用教程
  • [架构之路-245/创业之路-76]:目标系统 - 纵向分层 - 企业信息化的呈现形态:常见企业信息化软件系统 - 企业资源管理计划ERP
  • 第16章_变量、流程控制与游标
  • CCS3列表和超链接样式
  • jenkins工具系列 —— 删除Jenkins JOB后清理workspace
  • SurfaceFliger与Vsync信号如何建立链接?
  • Docker 部署spring-boot项目(超详细 包括Docker详解、Docker常用指令整理等)
  • 十月听书笔记
  • 表格识别软件:科技革新引领行业先锋,颠覆性发展前景广阔
  • leetcode 275 H指数 2
  • 系统架构设计师-第16章-嵌入式系统架构设计理论与实践-软考学习笔记
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • [Vue CLI 3] 配置解析之 css.extract
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • android图片蒙层
  • Angular6错误 Service: No provider for Renderer2
  • AngularJS指令开发(1)——参数详解
  • CentOS6 编译安装 redis-3.2.3
  • css布局,左右固定中间自适应实现
  • extract-text-webpack-plugin用法
  • iOS编译提示和导航提示
  • Java小白进阶笔记(3)-初级面向对象
  • Python - 闭包Closure
  • 初探 Vue 生命周期和钩子函数
  • 基于web的全景—— Pannellum小试
  • 前端面试题总结
  • 如何在GitHub上创建个人博客
  • 实习面试笔记
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 小而合理的前端理论:rscss和rsjs
  • 携程小程序初体验
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • 进程与线程(三)——进程/线程间通信
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • # 安徽锐锋科技IDMS系统简介
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • (C++20) consteval立即函数
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (function(){})()的分步解析
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (五)IO流之ByteArrayInput/OutputStream
  • .net framework 4.0中如何 输出 form 的name属性。
  • .NET 的程序集加载上下文
  • .NET框架
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • :如何用SQL脚本保存存储过程返回的结果集
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
  • [20161214]如何确定dbid.txt
  • [2018][note]用于超快偏振开关和动态光束分裂的all-optical有源THz超表——