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

SpringBoot 结合 SpringSecurity 对于用户 登陆 和 登出 的设计思考

SpringBoot 结合 SpringSecurity 对于用户登陆和登出的设计

前题:最近自己在做一个项目,之前在网上看到或者学的基本都是使用Redis+Token来实现的安全检查机制。但是,后来在实际的开发中发现这确实是最普遍的基础做法,但是也没人说在实际的工作中是不这样的啊。首先就是一个安全的问题,重中之重。

JWT + Redis ?

JWT用来生成Token,然后在用户登陆后返回token存在Redis里。前端也要本地储存Token,对于其他API的调用就要每次用token进行验证。

  • 首先, JWT + Redis 这两者结合起来就是不太对的,我们之所以使用Token就是因为其的无状态性stateless,尤其是在当前的分布式系统中非常的重要。
  • 其次,就是JWT之所火也是因为其适合分布式的系统不用在后端储存任何的信息。因此我们这里再结合Redis其实就不符合JWT的设计了。
  • 再来,直接把Token返回给前端,前端对于Token的操作是不安全的。前端得到Token后可能需要保存到本地的localStorage,这直接的就导致了Token被恶意获取获取修改,比如常见的XSS攻击等。
  • 最后,这种方式需要在每次前端发起请求的时候都要手动的携带上Token也极其的不方便。

JWT + Cookie?

这里是我在GPT老师的帮助下获取的方式,应该算是比较安全和普遍的了。

  1. JWT 正常生成Token,但是在用户成功登陆后后端不直接把Token显示的返回给前端
  2. 可以通过利用HttpOnly 和 Secure 来保证Token的安全,就是在登陆成功后把Token通过这样的形式以Cookie的方式返回给前端。这样一来前端就不需要对Token进行任何额外的处理了。
  3. Cookie保存到本地的方式还可以再每次向其他API发起请求时自动的携带上Cookie token到后端,不用前端人员每次手动的操作。

但是这里面还有许多需要注意的点:

  • 比如前后端分离的要配置跨域访问CORS,允许凭证等。
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Beanpublic WebMvcConfigurer corsConfigurer() {return new WebMvcConfigurer() {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("http://localhost:3000").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").allowedHeaders("*").allowCredentials(true); // 这里一定要true}};}
}
  • 如果自己写了JWT Filter 还需要后端每次得到请求要解析下cookie获取token。
public String extractTokenFromCookies(HttpServletRequest request) {Cookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie cookie : cookies) {if (cookie.getName().equals("httpOnlyToken")) {return cookie.getValue(); // Return the token from the cookie}}}return null; // Return null if the token is not found
}
  • 前端也要写好每次请求携带凭证。
// 我这里是React的例子import axios from 'axios';// 创建一个 axios 实例
const axiosInstance = axios.create({baseURL: 'http://localhost:8080',withCredentials: true,  // 允许携带 cookieheaders: {'Content-Type': 'application/json',}
});

Login 部分的代码

@Override
public ResponseEntity<Result> login(User user, HttpServletResponse response) {// use SpringSecurity's AuthenticationManager to authenticate the userAuthentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));//pass the authenticationif (authentication.isAuthenticated()){LoginUser loginUser = (LoginUser) authentication.getPrincipal();// user infoMap<String, Object> userInfo = new HashMap<>();userInfo.put("roles", loginUser.getRoles());userInfo.put("username", loginUser.getUser().getUsername());userInfo.put("email", loginUser.getUser().getEmail());userInfo.put("permissions", loginUser.getPermissions());userInfo.put("userId", loginUser.getUser().getId());// Generate a tokenString token = jwtUtil.generateToken(userInfo);// 将 token 设置为 HttpOnly 和 Secure CookieCookie jwtCookie = new Cookie("httpOnlyToken", token);jwtCookie.setHttpOnly(true); // 设置 HttpOnly 防止 XSS 攻击jwtCookie.setSecure(true); // 设置 Secure 确保只在 HTTPS 中传输jwtCookie.setMaxAge(24 * 60 * 60); // 设置有效期为 1 天(单位:秒)jwtCookie.setPath("/"); // 适用于整个应用
//            jwtCookie.setDomain("localhost"); // 这个应该是你的域名,只有在这个域名下才能访问到这个 cookieresponse.addCookie(jwtCookie); // 将 cookie 添加到响应中// return the token and user info to the client// for stateless authentication, the server does not need to store the tokenMap<String, Object> responseData = new HashMap<>();responseData.put("user", userInfo);responseData.put("message", "Login successful");return ResponseEntity.ok(Result.success(responseData));}else {// If the authentication fails, return an error messagereturn ResponseEntity.status(403).body(Result.error(403,"Password or Username is incorrect"));}}
9.22 更新

今天又发现好像token里面不应该存太多的用户信息?

  • 首先,是因会加大网络传输的载体体积,正常cookie会变得很大。
  • 其次,还是处于安全因素的考虑。应该尽可能少的在Token里面放数据,比如只放用户id或者用户权限。

JwtFilter过滤器

之前的在用户登陆的时候返回的token包括了很多的用户信息,但是好像有点不安全。于是在之前的基础上又改进了下:

  • 返回仅包包含用户id的token
  • 在jtw过滤器里面:
    • 用户首次登陆的时候查询数据库加载用户的最新信息到springcontext里。
    • 但是,有个问题因为所有的API请求都要首先第一个经过JWTFilter的过滤,因此每次也就会涉及数据库的操作。这显然是不行的I/O消耗太高。因此就就引入了缓存Cache,第一次用户登陆后缓存用户信息到Redis里,设置过期时间,清除缓存等。这里就不细讲了,准备在spring专题下重写一篇文章。

Logout

既然都有login那必须要有logout,这里需要注意的就是Logout要记得清除Cookie 通过response。

@Override
public ResponseEntity<Result> logout(HttpServletRequest request, HttpServletResponse response) {// Extract token using utility methodString token = jwtUtil.extractTokenFromCookies(request);if (token != null) {// 让cookie实效!Cookie cookie = new Cookie("httpOnlyToken", null);cookie.setHttpOnly(true);cookie.setSecure(true);cookie.setPath("/");cookie.setMaxAge(0);response.addCookie(cookie);return ResponseEntity.ok(Result.success("Logout successful"));} else {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Result.error(400, "Token not found in cookies"));}}

总结:

这也是我个人在做项目时的思考和经验,希望对大家有所帮助!如有不对或者值得改进的地方希望有大佬也可以指出来,让我多学些学习。

最后,我项目的代码在 GitHub 上有兴趣的朋友可以去看看完整的代码逻辑。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • GPIO与MIO控制LED——ZYNQ学习笔记2
  • 计算机视觉方面的一些模块
  • 可靠传输是什么?是基于UDP实现的吗
  • 根据[国家统计局最新行政区规划]数据库代码
  • 【Redis】Redis 缓存设计:抗住百万并发量的最佳实践
  • oracle 11g写一个判断是否是身份证的函数,函数名称为:FUN_IS_IDENNO
  • C++二叉搜索树学习
  • MySQL篇(存储引擎)(持续更新迭代)
  • HTTP请求与响应相关知识点解读
  • Image matting入门
  • Android14请求动态申请存储权限
  • 双路创新深度学习!TCN-Transformer+LSTM多变量时间序列预测(Matlab)
  • 【学术会议:中国杭州,机器学习和计算机应用面临的新的挑战问题和研究方向】第五届机器学习与计算机应用国际学术会议(ICMLCA 2024)
  • go 读取excel
  • 04 面部表情识别:Pytorch实现表情识别-表情数据集训练代码
  • JavaScript-如何实现克隆(clone)函数
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 【笔记】你不知道的JS读书笔记——Promise
  • Android Volley源码解析
  • Map集合、散列表、红黑树介绍
  • spring + angular 实现导出excel
  • SQLServer插入数据
  • 程序员最讨厌的9句话,你可有补充?
  • 关于字符编码你应该知道的事情
  • 后端_ThinkPHP5
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 前端学习笔记之观察者模式
  • 与 ConTeXt MkIV 官方文档的接驳
  • 阿里云服务器购买完整流程
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • #绘制圆心_R语言——绘制一个诚意满满的圆 祝你2021圆圆满满
  • $NOIp2018$劝退记
  • (C++哈希表01)
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (附源码)计算机毕业设计大学生兼职系统
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (十八)三元表达式和列表解析
  • (四) 虚拟摄像头vivi体验
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • ****Linux下Mysql的安装和配置
  • ***原理与防范
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .bat批处理(一):@echo off
  • .chm格式文件如何阅读
  • .Mobi域名介绍
  • .NET Core Web APi类库如何内嵌运行?
  • .NET Core 成都线下面基会拉开序幕
  • .NET 的静态构造函数是否线程安全?答案是肯定的!
  • .NET设计模式(11):组合模式(Composite Pattern)
  • .pop ----remove 删除