前后端完全分离实现登录和退出
前后端分离的整合
使用springsecurity+前端项目+redis完成认证授权的代码
1. 搭建一个前端工程
使用
vue ui
搭建,使用webstrom
操作
2. 创建一个登录页面
<template><div class="login_container"><!-- 登录盒子 --><div class="login_box"><!-- 头像 --><div class="avatar_box"><img src="../assets/3.jpg" alt=""></div><!-- 登录表单 --><el-form :model="loginForm" ref="LoginFormRef" label-width="0px" class="login_form"><!-- 用户名 --><el-form-item prop="username"><el-input v-model="loginForm.username" prefix-icon="el-icon-user-solid" ></el-input></el-form-item><!-- 密码 --><el-form-item prop="password"><el-input type="password" v-model="loginForm.password" prefix-icon="el-icon-s-grid"></el-input></el-form-item><!-- 按钮 --><el-form-item class="btns"><el-button type="primary" >登录</el-button><el-button type="info">重置</el-button></el-form-item></el-form></div></div>
</template>
<script>
export default {name: "Login",data() {return {//数据绑定loginForm: {username: '张三',password: '123456'},}},methods:{}
}
</script><style scoped>
.login_container {background-color: #2b5b6b;height: 100%;
}
.login_box {width: 450px;height: 300px;background: #fff;border: 1px solid #42b983;border-radius: 3px;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);}
.login_box>.avatar_box {height: 130px;width: 130px;border: 1px solid #eee;border-radius: 50%;padding: 10px;box-shadow: 0 0 10px #ddd;position: absolute;left: 50%;transform: translate(-50%, -50%);background-color: #fff;}
.login_box>.avatar_box>img {width: 100%;height: 100%;border-radius: 50%;background-color: #eee;
}
.login_form {position: absolute;bottom: 0;width: 100%;padding: 0 20px;box-sizing: border-box;
}
.btns {display: flex;justify-content: flex-end;
}
</style>
在views中创建视图——登录页面
在router的index.js中配置路由
{path: '/',name: 'home',//重定向,使之打开页面就跳转到登录页面redirect: '/login',},//配置登录页面的路由{path: '/login',name:'login',component: () => import('../views/Login.vue'),},
- 在App.vue默认组件中进行路由渲染
<template><div id="app"><router-view/></div> </template>
3. 登录按钮的点击事件
1. 首先,需要在main.js全局配置中全局配置axios
- 导入axios
- 设置axios的基础路径:端口号为后端项目的端口号
- 将axios挂载到vue对象中
import axios form 'axios'//导入
axios.dafaults.baseURL="基础路径http://ip:端口号" //配置axios的基础路径,便于在进行axios请求时将其省略
Vue.prototype.$名称=axios//将axios挂载到vue对象中。axios要与导入时的import后的名称一致。
2. 然后,在login页面的登录页面添加点击事件
- 在loginForm表单中添加表单验证——>:rules关键字和ref
- 在data中添加表单验证的规则
- 实现点击事件
-
表单验证
-
验证规则
-
登录点击事件
myLogin(){this.$refs.LoginFormRef.validate(valid => {if(valid){this.$axios.post('/login?username='+this.loginForm.username+'&password='+this.loginForm.password).then(res=>{if(res.data.code == 200){this.$message.success("登录成功")//将token保存到sessionStorage,类似于cookiesessionStorage.setItem("token",res.data.data)//跳转到后台this.$router.push("/home")}else{this.$message.error("登录失败")}})}})}
在该请求中,将获取到的token存放到
sessionStorage
中,通过setItem
方法
解决跨域问题
此时允许点击“登录”按钮,会报错,出现跨域问题
跨域问题:通过ajax从一个服务访问另一个服务时,出现跨域问题
服务:只要ip或端口或协议不同,都称为不同的域
如何解决:
由两种方式:前端解决和后端解决,这里仅讲解后端的解决方式
-
后端解决
在config中创建一个用于解决跨域问题的配置类
@Configuration public class AllowOriginConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 所有接口.allowCredentials(true) // 是否发送 Cookie.allowedOrigins("*")//支持域// .allowedOriginPatterns("*") // 支持域.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"}) // 支持方法.allowedHeaders("*").exposedHeaders("*");} }
在security配置类中添加登录允许跨域的设置
因为其他需求可以,而login不行,因为login是security写的,不止自己写的
http.cors();
@Overrideprotected void configure(HttpSecurity http) throws Exception {//把自定义的过滤器放在之前http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);http.formLogin()//登录页面;//登录的处理路径 默认 /login.loginProcessingUrl("/login").successHandler(successHandler())//登录成功.failureHandler(failureHandler()) //登录失败转发的路径 必须为post请求.permitAll(); //上面的请求路径无需认证//指定权限不足跳转的页面http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());http.csrf().disable();//禁用跨域伪造请求的过滤器//security登录允许跨域http.cors();//除了上的请求,其他请求都需要认证http.authorizeRequests().anyRequest().authenticated();}
4. 前置路由守卫
前置路由守卫用于判断有没有登录
放在main.js或/router/index.js中
在设置前置路由守卫之前,在没有登录的情况下,可以直接访问登录后的页面——不符合现实,所以使用前置路由守卫,来判断有没有登录
- 先查看路径,若用户访问的是登录页面
/login
,直接放行- 若不是,就获取sessionStorage中保存的token值
- 若token不存在,或者token为undefined,就强制跳转到
/login
登录页面- 如果token存在,直接放行
//前置路由守卫
//to:即将要访问的路径
//from:从哪里来
//next:放行函数
router.beforeEach((to,from,next)=>{//如果用户访问的是登录页面,直接放行if(to.path === '/login'){//放行return next();}//获取sessionStorage中保存的token值const token = window.sessionStorage.getItem('token');//如果token不存在,强制跳转到登录页面if(!token){return next("/login");}//如果token存在,直接放行next();
})
5. 设置携带token令牌:请求拦截器
在未设置请求拦截器之前,成功登录后,点击按钮均显示“未登录”,是因为请求没有携带token令牌,后端判断为未登录
放在main.js中
//设置请求拦截器——携带token令牌
axios.interceptors.request.use(config=>{var token = sessionStorage.getItem("token");if(token){config.headers.token = token;}return config;
})
6. 设置响应拦截器
确保后端,返回类型为R类型
在
main.js
中设置响应拦截器
- 后端示例
@PreAuthorize("hasAuthority('user:query')")@GetMapping("/select")public R select(){System.out.println("查询用户");return new R(200,"查询用户","查询用户");}
- 前端响应拦截器
//设置响应拦截器
axios.interceptors.response.use(response=>{//如果返回的code为200,为成功,显示返回的信息if(response.data.code===200){Vue.prototype.$message.success(response.data.msg);return response;}else {//否则,为失败,显示返回的信息Vue.prototype.$message.error(response.data.msg);return response;}
})
设置响应拦截器之后,就可以通过响应拦截器将后端的信息显示到前端页面,无需在axios请求中再次处理相关
msg
-
示例
methods:{query(){this.$axios.get('/user/select').then(res=>{})},insert(){this.$axios.get('/user/add').then(res=>{})},del(){this.$axios.get('/user/delete').then(res=>{})},update(){this.$axios.get('/user/update').then(res=>{})},myExport(){this.$axios.get('/user/export').then(res=>{})}},
7. 退出
后端:借助redis完成。
- 在登录时,登录成功后,将token存入到redis中
- 在配置时,自定义实现退出接口,使其在退出登录后,删除redis中的缓存信息,并返回json数据,而不是页面
- 在登录过滤器中,验证token时加判断,判断redis中是否存在登录的token
因为要借助redis,所以需要导入redis,并在配置文件中进行配置,并且开启装有redis的虚拟机,开启redis服务
-
引入依赖
<!--引入redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
-
在配置文件中添加redis相关配置
#Redis服务器连接端口 spring.redis.port=6379 #Redis服务器地址 spring.redis.host=172.16.7.112
-
在登录成功后将token添加到redis中
//将token存入redis缓存中
redisTemplate.opsForValue().set("login:"+token,"");
@Autowiredprivate StringRedisTemplate redisTemplate;//登录成功需要返回的json数据private AuthenticationSuccessHandler successHandler(){return new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {//设置响应编码httpServletResponse.setContentType("application/json;charset=utf-8");//获取输出对象PrintWriter writer = httpServletResponse.getWriter();//返回json数据Map<String,Object> map=new HashMap<>();map.put("username",authentication.getName());//获取权限信息列表Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();//获取权限标识码List<String> collect = authorities.stream().map(item -> item.getAuthority()).collect(Collectors.toList());map.put("permission",collect);String token = JWTUtil.createToken(map);//将token存入redis缓存中redisTemplate.opsForValue().set("login:"+token,"");//返回一个统一的json对象R r=new R(200,"登录成功!",token);//转换未json字符串String s = JSON.toJSONString(r);//servlet//发送响应到客户端writer.println(s);//将JSON字符串写入到HTTP响应中writer.flush();//确保所有缓冲的输出都被发送到客户端writer.close();//关闭PrintWriter}};}
-
在security配置文件中重写退出方法
//退出登录后,删除redis中的缓存信息
redisTemplate.delete("login:"+token);
@Overrideprotected void configure(HttpSecurity http) throws Exception {//把自定义的过滤器放在之前http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);http.formLogin()//登录页面;//登录的处理路径 默认 /login.loginProcessingUrl("/login").successHandler(successHandler())//登录成功.failureHandler(failureHandler()) //登录失败转发的路径 必须为post请求.permitAll(); //上面的请求路径无需认证//指定权限不足跳转的页面http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());http.csrf().disable();//禁用跨域伪造请求的过滤器//退出,重写使其返回json数据,而不是网页http.logout(item->{item.logoutSuccessHandler((httpServletRequest, httpServletResponse, e) -> {httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriter writer = httpServletResponse.getWriter();String token = httpServletRequest.getHeader("token");//退出登录后,删除redis中的缓存信息redisTemplate.delete("login:"+token);R r=new R(200,"退出成功",null);String jsonString =JSON.toJSONString(r);writer.println(jsonString);writer.flush();writer.close();});});//security登录允许跨域http.cors();//除了上的请求,其他请求都需要认证http.authorizeRequests().anyRequest().authenticated();}
-
在登录过滤器中添加判断条件
!redisTemplate.hasKey("login:"+token
//3. 验证tokenif(!JWTUtil.verify(token)||!redisTemplate.hasKey("login:"+token)){PrintWriter writer = httpServletResponse.getWriter();//返回一个token失效的json数据R r=new R(500,"token失效!",null);String s = JSON.toJSONString(r);writer.write(s);writer.flush();writer.close();return;}
-
前端,添加退出点击事件,退出后删除sessionStorage中的token信息,并跳转返回登录页面
logout(){this.$axios.post("/logout").then(res=>{//移除sessionStorage中的tokensessionStorage.removeItem("token")//跳转到登录页面this.$router.push("/login")})}
相关文章:

前后端完全分离实现登录和退出
前后端分离的整合 使用springsecurity前端项目redis完成认证授权的代码 1. 搭建一个前端工程 使用 vue ui搭建,使用webstrom操作 2. 创建一个登录页面 <template><div class"login_container"><!-- 登录盒子 --><div class"l…...

生信技能55 - WisecondorX分析结果过滤和质控
WisecondorX分析CNV,对每条染色的CNV loss和gain进行分组,对每个组求ratio平均值和zscore平均值,基于该数值对CNV进行质控和过滤,并对连续的CNV进行合并,获得可信的CNV。 WisecondorX基本使用方法以及npz文件转换和reference构建参考文章: 生信技能53 - wiseconrdoX自动…...

待办管理软件电脑版哪个好?待办事项清单app
在快节奏的现代社会中,有效地管理时间和任务变得越来越重要。很多人喜欢使用待办管理软件来协助整理琐碎事务、规划工作任务,以此提升工作效率。特别是对于上班族来说,一款能在电脑上便捷使用的待办软件,更是提升工作效率的得力助…...

【Mind+】掌控板入门教程01 “秀”出我创意
我们的好朋友麦乐佳即将举办一场派对,她要求每个参加派对的人都要佩戴一个可以彰显自己独特创意的装置。可以是会发光的帽子,可以是复古的电子表,还可以是其他有创意的作品。而现在,我们的手边刚好有一块掌控板,它自带…...

操作系统篇--八股文学习第十一天|进程调度算法你了解多少,进程间有哪些通信方式,解释一下进程同步和互斥,以及如何实现进程同步和互斥
进程调度算法你了解多少? 答: 先来先服务:按照请求的顺序进行调度。 这种调度方式简单,但是能导致较长作业阻塞较短作业。最短作业优先:非抢占式的调度算法,按估计运行时间最短的顺序进行调度。 但是如果…...
慢慢欣赏arm64内核启动6 primary_entry之el2_setup代码第三部分
分析代码 解析完虚拟化部分,我们继续分析启动过程中,对中断控制器的处理 #ifdef CONFIG_ARM_GIC_V3/* GICv3 system register access */mrs x0, id_aa64pfr0_el1ubfx x0, x0, #ID_AA64PFR0_GIC_SHIFT, #4cbz x0, 3fmrs_s x0, SYS_ICC_SRE_EL2orr x0, x…...

初谈Linux多线程--线程控制
文章目录 线程的概述理解线程Linux中的线程重新理解的进程Windows的线程线程的优点线程的缺点理解线程调度成本低 进程VS线程 线程控制创建线程等待线程线程函数传参线程的返回值新线程的返回值新线程返回值错误返回值为类对象 创建多线程线程的终止线程的分离pthread_detach 线…...
文件工具类 - FileUtils
Slf4j Component public class FileUtils {/*** 文件夹复制到指定的文件夹*/SneakyThrowspublic static void copyDir(File source, File target) {if (!target.exists()) {boolean mkdirs target.mkdirs();}if (source.isDirectory()) {File[] files source.listFiles();if …...
Kafka源码剖析-Producer基于内存缓存池分配ByteBuffer
文章目录 在将消息发送到内存缓中区之前做的准备工作发送消息前的准备工作代码示例源码分析1. **消息序列化**2. **元数据准备**3. **分区选择**4. **批处理准备**总结大致浏览一下源码中将消息写入内存缓冲的运行流程源码分析1. **消息序列化和创建记录批次**2. **确定分区**3…...
实习十九:学习笔记
上午 1、构建vue发行版本 [rootserver ~]# cd eleme_web/ [rootserver eleme_web]# npm run buid //项目未执行时运行该命令,创建发行版本 [rootserver eleme_web]# cd dist/ //dist中包含发行版本的所有文件 [rootserver dist]# ls css favicon.ico i…...

OrionX:革新GPU资源管理,助力AI开发团队高效运作
您的AI开发团队是否经常陷入这样的窘境: 人多卡少,GPU资源难以满足每个成员的需求? 当开发环境中需要变更GPU卡配置时,流程繁琐不堪,不得不关闭容器、重新配置再重启? 是否曾因GPU卡分配后未被充分利用而…...

RabbitMQ发送者重连、发送者确认
RabbitMQ发送者重连、发送者确认 一、发送者重连 spring:rabbitmq:connection-timeout: 1s #设置MQ的连接超时时间template:retry:enabled: true #开启超时重试机制initial-interval: 1000ms #失败后的初始等待时间multiplier: 1 #失败后下次的等待时长倍数,下次等…...

请转告HPC计算AI计算单位,选对存储事半功倍
U.2 NVMe全闪混合统一存储GS 5000U是Infortrend产品中一款高性能机型。得益于搭载强劲的第五代IntelXeon处理器,以及支持PCIe 5.0、NVMe-oF、100GbE等多种特点,GS 5000U单台块级性能可达50 GB/s的读、20 GB/s的写,以及1300K的IOPS;…...

[GYCTF2020]Blacklist1
打开题目 判断注入类型,输入1试试 输入2 输入1 判断为字符型注入 堆叠查询2;show databases;# 然后来输入2; show tables;#来查看数据库的表 然后我们通过FlagHere表来查看列输入2;show columns from FlagHere;# 来查看列 、 重新构造payload:0;HAND…...

Blackcat V2.2付费会员制WordPress资源站主题
Blackcat-付费会员制WordPress资源站主题,该主题是基于简约实用的主题选项框架 Codestar Framework 进行开发的功能强大的付费会员制主题,该主题尤其适合用于搭建付费下载资源网站,比如素材站、软件站、视频教程站等付费资源下载网站。 集成…...
动手学强化学习 第 18 章 离线强化学习 训练代码
基于 https://github.com/boyu-ai/Hands-on-RL/blob/main/%E7%AC%AC18%E7%AB%A0-%E7%A6%BB%E7%BA%BF%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0.ipynb 理论 离线强化学习 修改了警告和报错 运行环境 Debian GNU/Linux 12 Python 3.9.19 torch 2.0.1 gym 0.26.2 运行代码 CQL.…...
Python笔试面试题AI答之面向对象常考知识点
Python面向对象面试题面试题覆盖了Python面向对象编程(OOP)的多个重要概念和技巧,包括元类(Metaclass)、自省(Introspection)、面向切面编程(AOP)和装饰器、重载…...
面试经典算法150题系列-数组/字符串操作之买卖股票最佳时机
买卖股票最佳时机 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易…...

安装jdk和tomcat
安装nodejs 1.安装nodejs,这是一个jdk一样的软件运行环境 yum -y list installed|grep epel yum -y install nodejs node -v 2.下载对应的nodejs软件npm yum -y install npm npm -v npm set config .....淘宝镜像 3.安装vue/cli command line interface 命令行接…...
mongodb 备份还原
### 加入 MongoDB 官方 repositoryecho [mongodb-org-4.4] nameMongoDB Repository baseurlhttps://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.4/x86_64/ gpgcheck1 enabled1 gpgkeyhttps://www.mongodb.org/static/pgp/server-4.4.asc| tee /etc/yum.repos.d/mo…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...