SaToken+SpringBoot+Redis前后端分离登录认证

目录

  • 前言
  • 一、创建工程项目🎍
    • 1.1 创建后端工程
    • 1.2 创建前端工程
  • 二、业务代码🎊
    • 后端代码
    • 前端代码
  • 三、测试
  • 参考资料

前言

Sa-Token 是一款 Java 语言的权限认证框架,提供了灵活、高效、易用的权限认证和会话管理功能。它是 SpringBoot、Spring MVC、Servlet 等 Java 技术体系下的轻量级权限认证组件,可以帮助开发者快速实现用户认证、授权和会话管理等功能。

logo

Sa-Token官方文档

功能结构图

img

认证流程图

sa-token-rz

框架特性

image-20240523135615846

一、创建工程项目🎍

1.1 创建后端工程

引入依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.13</version><relativePath/> <!-- lookup parent from repository -->
</parent><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.38.0</version>
</dependency><dependency><groupId>cn.dev33</groupId><artifactId>sa-token-redis-fastjson2</artifactId><version>1.37.0</version>
</dependency>
<!--无论使用哪种序列化方式,你都必须为项目提供一个 Redis 实例化方案-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>

注:如果你使用的是 SpringBoot 3.x,只需要将 sa-token-spring-boot-starter 修改为 sa-token-spring-boot3-starter 即可。

基础配置

server:port: 8081
spring:redis:database: 1host: 127.0.0.1port: 6379password:timeout: 5000jedis:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0
#Sa-token相关配置(与官网一致)
sa-token: # token 名称(同时也是 cookie 名称)token-name: satoken# token 有效期(单位:秒) 默认30天,-1 代表永久有效timeout: 2592000# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结active-timeout: -1# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)is-concurrent: true# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)is-share: true# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)token-style: uuid# 是否输出操作日志 is-log: true

1.2 创建前端工程

本项目使用的vue3,创建一个vue项目,引入下方依赖即可

"dependencies": {"axios": "^1.6.8","element-plus": "^2.7.3","pinia": "^2.1.7","qs": "^6.12.1","vue": "^3.2.37","vue-router": "^4.2.5"
},

二、业务代码🎊

后端代码

User.java

@Data
public class User{//id@TableId(type = IdType.ASSIGN_ID)private Long id;//用户名private String username;//密码private String password;//账户是否锁住(1被锁0未被锁)private Integer isLocked;//账户是否被删除(1删除0未被删除)@TableLogicprivate Integer isDelete;//创建时间@TableField(fill = FieldFill.INSERT)private Date createTime;//更新时间@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;
}

UserDTO.java

@EqualsAndHashCode(callSuper = true)
@Data
public class UserDTO extends User {//记住我private boolean rememberMe;//角色列表private List<Role> roleList;//登录的设备private String device;//图形验证码private String code;//图形验证码的keyprivate String codeKey;}

UserController.java

注意:代码中定义了一些常量,可以自行替代。

@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {@AutowiredUserService userService;@AutowiredStringRedisTemplate stringRedisTemplate;@PostMapping("/doLogin")public SaResult doLogin(@RequestBody UserDto userDto) {//从redis中取生成的验证码String validateCode = stringRedisTemplate.opsForValue().get("code:validate:" + userDto.getCodeKey());if (userDto.getCode().equals(validateCode)) {//验证码正确才进行用户验证,先验证了用户存不存在,再验证了密码是否正确if (userService.login(userDto.getUsername(), userDto.getPassword())) {StpUtil.login(userDto.getUsername(), new SaLoginModel()//实现‘记住我’功能.setIsLastingCookie(userDto.isRememberMe())//设置登录设备,主要用于实现同端互斥登录,此处没有实现该功能,可以不用管.setDevice("PC"));//验证成功就以json的形式返回tokenHashMap<String, Object> resultMap = new HashMap<>();resultMap.put("token", StpUtil.getTokenValue());return SaResult.ok(ResponseCode.LOGIN_SUCCESS.getMessage()).setCode(ResponseCode.LOGIN_SUCCESS.getCode()).setData(resultMap);} else {return SaResult.error(ResponseCode.USERNAME_PASSWORD_ERROR.getMessage()).setCode(ResponseCode.USERNAME_PASSWORD_ERROR.getCode());}} else {return SaResult.error(ResponseCode.VALIDATE_CODE_ERROR.getMessage()).setCode(ResponseCode.VALIDATE_CODE_ERROR.getCode());}}}

StpUtils.login:

  1. 检查此账号是否之前已有登录;
  2. 为账号生成 Token 凭证与 Session 会话;
  3. 记录 Token 活跃时间;
  4. 通知全局侦听器,xx 账号登录成功;
  5. Token 注入到请求上下文;
  6. 等等其它工作……

SaResult:这个也是一个由Satoken封装的结果响应类,还是挺好用的。

UserService.java

public interface UserService {boolean login(String username, String password);
}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService{@AutowiredUserDao userDao;@Overridepublic boolean login(String username, String password) {User user = userDao.selectOne(new QueryWrapper<User>().eq("username", username));//数据库中的密码进行了加密BCrypt也是Satoken提供的一个工具类return user != null && BCrypt.checkpw(password, user.getPassword());}
}

UserDao.java

/*** (User)表数据库访问层** @author yzk* @since 2024-05-15 16:44:29*/
public interface UserDao extends BaseMapper<User> {}

SaAuthenticationConfigure.java

@Configuration
public class SaAuthenticationConfigure implements WebMvcConfigurer {// 注册 Sa-Token 拦截器,打开注解式鉴权功能 @Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,打开注解式鉴权功能registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))//所有接口都会检查是否登录了.addPathPatterns("/**")//以下接口不检查,直接放行.excludePathPatterns("/user/doLogin")}}

SaTokenFilter.java

@Configuration
public class SaTokenFilter implements WebMvcConfigurer {/*** 注册 [Sa-Token 全局过滤器] */@Beanpublic SaServletFilter getSaServletFilter() {return new SaServletFilter()// 指定 [拦截路由] 与 [放行路由].addInclude("/**").addExclude("/favicon.ico")// 认证函数: 每次请求执行 .setAuth(obj -> {SaManager.getLog().debug("----- 请求path={}  提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());// ...})// 异常处理函数:每次认证函数发生异常时执行此函数 .setError(e -> {return SaResult.error(e.getMessage());})// 前置函数:在每次认证函数之前执行.setBeforeAuth(obj -> {SaHolder.getResponse()// ---------- 设置跨域响应头 ----------// 允许指定域访问跨域资源.setHeader("Access-Control-Allow-Origin", "*")// 允许所有请求方式.setHeader("Access-Control-Allow-Methods", "*")// 允许的header参数.setHeader("Access-Control-Allow-Headers", "*")// 有效时间.setHeader("Access-Control-Max-Age", "3600");// 如果是预检请求,则立即返回到前端 SaRouter.match(SaHttpMethod.OPTIONS).free(r -> System.out.println("--------OPTIONS预检请求,不做处理")).back();});}}

注意这个过滤器!!!!!!!!!!务必配置这个过滤器,用SaToken时,后端使用@CrossOrigin进行了跨域的配置,但是!前端发起请求还是会报跨域问题,而且后端会报'未读取到有效token',是因为前端发起请求时,先发起了预检请求,SaToken拦截了预检请求,预检请求的headers中没有token,所以一直报错。

前端代码

此处删减了部分代码,只保留了登录登录相关的代码

login.vue

<template><div class="login-container"><h2>登录</h2><form @submit.prevent="handleSubmit"><div class="form-group"><label for="username">用户名:</label><el-input type="text" id="username" v-model="user.username" placeholder="请输入用户名" required/></div><div class="form-group"><label for="password">密码:</label><el-input type="password" id="password" v-model="user.password" placeholder="请输入密码" required/></div><div class="form-group"><label>验证码</label><el-row :gutter="15"><el-col :span="14"><el-input v-model="user.code" style="margin-top: 7px" class="codeInput"placeholder="请输入验证码"></el-input></el-col><el-col :span="10"><el-image @click="changeImage" style="width: 159px; height: 39px" :src="imageUrl"/></el-col></el-row></div><div class="form-group"><el-checkbox v-model="user.rememberMe" label="记住我" size="large"/></div><button type="submit">登录</button><button type="submit" @click="toRegister">注册</button></form><div class="social-login"><button @click="toGitee"><span class="social-icon"></span>使用Gitee登录</button></div></div>
</template><script setup>
import {h, onMounted, ref} from 'vue';
import {generatorLoginCode, toGiteeOauth, userLogin} from "@/api/userApi";
import {useRouter} from "vue-router";
import {ElCheckbox, ElCol, ElImage, ElInput, ElNotification, ElRow} from "element-plus";const router = useRouter()
const user = ref({username: "",password: "",rememberMe: false,code: "",codeKey: ""
})
const handleSubmit = () => {userLogin(JSON.stringify(user.value)).then((resp) => {if (resp.code === 2000) {//取出token并存入localStoragelocalStorage.setItem("token", resp.data.token)ElNotification({title: '提示',message: h('info', {style: 'color: teal'}, resp.msg),duration: 3000})router.push("/main")} else {if (resp.code === 5005) {ElNotification({title: '提示',message: h('error', {style: 'color: red'}, resp.msg),duration: 3000})changeImage()}}})
};
const userLogin = (data) => {return requests({url: "/user/doLogin",data: data,method: "POST",})
}
</script><style scoped></style>

前端点击登录按钮时,发后端接口发起请求,请求成功后,取出响应结果中的token存入localStorage。

requests.js

import axios from 'axios';
import {router} from "@/router";axios.defaults.crossDomain = trueexport const requests = axios.create({baseURL: 'http://localhost:8081',timeout: 10000, // 请求超时时间headers: {'Content-Type': 'application/json','Access-Control-Allow-Origin': '*'}
});// 请求拦截器
requests.interceptors.request.use((config) => {// 在发送请求之前做些什么const token = localStorage.getItem('token'); // 假设token存储在localStorage中if (token) {config.headers.Token = `Bearer ${token}`; // 添加token到请求头}return config;},(error) => {// 对请求错误做些什么return Promise.reject(error);}
);// 响应拦截器
requests.interceptors.response.use((response) => {console.log(response.data.code)const code=response.data.codeif (code === 5006 ||code===5007|| code===5008) {//返回登录界面router.push('/').then(r =>{location.reload()} )// router.go(0)//删除当前localStorage中的tokenlocalStorage.removeItem("token")}return response.data;},(error) => {// 对响应错误做点什么return Promise.reject(error);}
);

注意请求拦截器,每次发起请求时,都会从localStorage中取出token,并将其放入headers中

三、测试

集成了redis后,记得先开启redis服务,在登录时,框架会自动保存数据

  1. 开启前后端服务

    image-20240523133544450

    本文的代码是从完整项目中抽出的,上述代码只有登录功能

    image-20240523133614619

输入用户名和密码后,进入主界面

image-20240523134210128

此时,登录的信息已经自动的被框架自动保存进redis

image-20240523134056069

IDEA控制台打印出相应的信息

image-20240523134140772

参考资料

Sa-Token

使用 Sa-Token 的全局过滤器解决跨域问题(三种方式全版) - 掘金 (juejin.cn)

Sa-Token功能结构图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/839465.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为大咖说 | 企业应用AI大模型的“道、法、术” ——道:认知篇

本文作者&#xff1a;郑岩&#xff08;华为云AI变革首席专家&#xff09;全文约3313字&#xff0c;阅读约需8分钟&#xff0c;请仔细看看哦~ 前阵子&#xff0c;我在公司内部发了一篇Sora的科普贴&#xff0c;本来只是个简单的技术总结&#xff0c;但让我意外的是&#xff0c;…

vue.js对接海康威视摄像头web开发包

一、登录海康开放平台下载web开发包&#xff0c;下载需要先登录海康账号&#xff0c;没有的需先注册一个。 这里的appkey、ip、port、secret 和cameraIndexCodeasd是自己去申请的 appkey: "****", ip: "****", port: **, secret: "****", //必填…

torch配置时出现问题

torch配置时出现如下问题&#xff1a; 可能原因&#xff1a; 1、下载的whl文件中python版本与本机上的python版本不匹配&#xff1b; 2、上图中的文件是64位的&#xff0c;而本机python是32位的&#xff0c;也无法匹配&#xff1b; 3、cuda的版本不匹配。

AI在线免费音乐生成工具:suno、udio、stableaudio

文生音乐 1、suno https://app.suno.ai/ 2、udio https://www.udio.com/ 3、stableaudio https://stableaudio.com/live https://www.stableaudio.com/?utm_campaignstableaudio_promo&utm_mediumcta_button&utm_sourcestability_ai 4、其他MusicGen https://…

新书推荐:6.1 if语句

计算机语言和人类语言类似&#xff0c;人类语言是为了解决人与人之间交流的问题&#xff0c;而计算机语言是为了解决程序员与计算机之间交流的问题。程序员编写的程序就是计算机的控制指令&#xff0c;控制计算机的运行。借助于编译工具&#xff0c;可以将各种不同的编程语言的…

Springboo基础入门

一、SpringBoot概述 SpringBoot的诞生背景可以追溯到对传统Spring框架的改进需求&#xff0c;在过去&#xff0c;使用spring框架构建应用程序需要大量的配置和设置&#xff0c;这无形中层架了开发者的工作量并且&#xff0c;肯能造成一些配置上的错误&#xff0c;因此springboo…

【EI会议】2024年测绘、地质与遥感国际会议(SGRS 2024)

2024年测绘、地质与遥感国际会议 2024 International Conference on Surveying, Geology, and Remote Sensing 【1】会议简介 2024年测绘、地质与遥感国际会议&#xff0c;将于不久后隆重召开&#xff0c;这将是一场汇聚全球顶尖专家学者、探讨测绘、地质与遥感领域最新技术、发…

vue3项目+TypeScript前端项目 ———— elemnet-plus,svg图标配置,sass,mock数据

一.集成element-plus 官网地址 安装 pnpm install element-plus 引入 // main.ts import { createApp } from vue import ElementPlus from element-plus import element-plus/dist/index.css import App from ./App.vueconst app createApp(App)app.use(ElementPlus) app.…

Element表格之表头合并、行合并和列合并

el-table合并表头handerMethod_el-table表头合并-CSDN博客 Element表格之表头合并、行合并和列合并_element表格表头合并-CSDN博客 一、合并表头 话不多说&#xff0c;先看效果图&#xff1a; 表格结构如上&#xff0c;其中:header-cell-style对表头做了一些处理。 headFirst…

HTTPS:安全网络通信的基石

在数字化时代&#xff0c;网络通信的安全变得至关重要。HTTPS&#xff08;超文本传输安全协议&#xff09;是一种用于保护网络通信的协议&#xff0c;它通过加密技术确保数据传输的安全性和完整性。下面我们就来了解一下HTTPS。 一、HTTPS是什么&#xff1f; HTTPS是HTTP&…

【el-table 实现自定义单选】

el-table 实现自定义单选 示例图片代码 示例图片 代码 row-click"singleElection"<el-table-columnalign"center"label"选择"><template slot-scope"scope"><el-radio:key"scope.row.id"v-model"templa…

ChannelHandlerContext——ChannelHandler和ChannelPipeline之二

目录 ChannelHandlerContext接口 使用ChannelHandlerContext ChannelHandler和ChannelHandlerContext的高级用法 异常处理 处理入站异常 处理出站异常 本文继上文《ChannelHandler和ChannelPipeline之一》&#xff0c;接着讲ChannelHandlerContext接口。 ChannelHandlerC…

需求响应+配网重构!含高比例新能源和用户需求响应的配电网重构程序代码!

前言 配电网重构作为配电网优化运行的手段之一&#xff0c;通过改变配电网的拓扑结构&#xff0c;以达到降低网损、改善电压分布、提升系统的可靠性与经济性等目的。近年来&#xff0c;随着全球能源消耗快速增长以及环境的日趋恶化&#xff0c;清洁能源飞速发展&#xff0c;分…

超简单白话文机器学习 - 逐步回归Lasso,Ridge正则化(含算法讲解,公式全解,手写代码实现,调包实现)

1. 提高泛化能力 1.1 概念 正则化是一种在机器学习和统计建模中用于防止过拟合的技术。过拟合是指模型在训练数据上表现很好&#xff0c;但在未见过的测试数据或新数据上表现不佳。正则化通过在损失函数&#xff08;如最小二乘误差&#xff09;中添加一个惩罚项&#xff0c;限…

仓库管理WMS软件(Warehouse Management Software)百科解析

一、什么是仓库管理软件&#xff08;WMS&#xff09;&#xff1f; 仓库管理软件&#xff08;WMS&#xff09;全称Warehouse Management System&#xff0c;是一种专门用于仓库作业流程优化和库存控制的软件系统。它通过先进的自动识别与数据采集技术&#xff0c;实现对仓库货物…

Leecode热题100---114:二叉树展开为链表

题目&#xff1a; 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺序相同…

Spring Boot | Spring Boot 实现 “记住我“ 功能

目录: 一、SpringBoot 中 自定义 "用户授权管理" ( 总体内容介绍 )二、实现 "记住我" 功能 ( 通过 "HttpSecurity类" 的 rememberMe( )方法来实现 "记住我" 功能 ) :2.1 基于 "简单加密 Token" 的方式 ( 实现 "记住我&…

av_dump_format经验分析,FFmpeg获取媒体文件总时长(FLV获取总时长的误区)

播放器有个功能&#xff0c;当用户打开视频时&#xff0c;需要读取媒体文件的总时长等信息&#xff0c;不巧的时&#xff0c;获取FLV时总失败&#xff0c;下面来具体分析下FLV和MP4获取总时长的原因和区别&#xff1a; 播放器有个获取MediaInfo的接口&#xff0c;功能如下&am…

echarts全局设置饼图的颜色

&#x1f337;第一步 在js文件中写入你需要的颜色 这里的颜色也可以写渐变的 &#x1f337;下一步 在main.is中引用全局挂载 &#x1f337;最后一步 在初始化的时候加一个macarons即可 &#x1f337;第一步 在js文件中写入你需要的颜色 这里的颜色也可以写渐变的 (functi…

探索k8s集群中kubectl的陈述式资源管理

一、k8s集群资源管理方式分类 1.1陈述式资源管理方式&#xff1a;增删查比较方便&#xff0c;但是改非常不方便 使用一条kubectl命令和参数选项来实现资源对象管理操作 即通过命令的方式来实 1.2声明式资源管理方式&#xff1a;yaml文件管理 使用yaml配置文件或者json配置文…