前后端完全分离实现登录和退出

news2025/3/11 2:29:23

前后端分离的整合

使用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>
  1. 在views中创建视图——登录页面

  2. 在router的index.js中配置路由

{
    path: '/',
    name: 'home',
    //重定向,使之打开页面就跳转到登录页面
    redirect: '/login',
  },
    //配置登录页面的路由
  {
    path: '/login',
    name:'login',
    component: () => import('../views/Login.vue'),
  },
  1. 在App.vue默认组件中进行路由渲染
<template>
  <div id="app">
   <router-view/>
  </div>
</template>

3. 登录按钮的点击事件

1. 首先,需要在main.js全局配置中全局配置axios

  1. 导入axios
  2. 设置axios的基础路径:端口号为后端项目的端口号
  3. 将axios挂载到vue对象中
import axios form 'axios'//导入
axios.dafaults.baseURL="基础路径http://ip:端口号" //配置axios的基础路径,便于在进行axios请求时将其省略
Vue.prototype.$名称=axios//将axios挂载到vue对象中。axios要与导入时的import后的名称一致。

2. 然后,在login页面的登录页面添加点击事件

  1. 在loginForm表单中添加表单验证——>:rules关键字和ref
  2. 在data中添加表单验证的规则
  3. 实现点击事件
  • 表单验证

    在这里插入图片描述

  • 验证规则

    在这里插入图片描述

  • 登录点击事件

    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,类似于cookie
                    sessionStorage.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 {
        @Override
        public 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();
    
     @Override
        protected 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中

在设置前置路由守卫之前,在没有登录的情况下,可以直接访问登录后的页面——不符合现实,所以使用前置路由守卫,来判断有没有登录

  1. 先查看路径,若用户访问的是登录页面/login,直接放行
  2. 若不是,就获取sessionStorage中保存的token值
  3. 若token不存在,或者token为undefined,就强制跳转到/login登录页面
  4. 如果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完成。

  1. 在登录时,登录成功后,将token存入到redis中
  2. 在配置时,自定义实现退出接口,使其在退出登录后,删除redis中的缓存信息,并返回json数据,而不是页面
  3. 在登录过滤器中,验证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,"");

    @Autowired
        private StringRedisTemplate redisTemplate;
        //登录成功需要返回的json数据
        private   AuthenticationSuccessHandler successHandler(){
          return new AuthenticationSuccessHandler() {
              @Override
              public 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);

     @Override
        protected 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. 验证token
         if(!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中的token
            sessionStorage.removeItem("token")
            //跳转到登录页面
            this.$router.push("/login")
          })
        }
    
    

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

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

相关文章

深度学习中五种归一化原理

调用库API实现以及手写实现 import torch import torch.nn as nnbatch_szie2 #sample time_steps3 embedding_dim4 #channel num_group2inputxtorch.randn(batch_szie,time_steps,embedding_dim)# N*L*C 批归一化 BatchNorm1d ## 1. 批归一化 实现batch_norm并验证API --…

2024年钉钉杯大学生大数据挑战赛倒计时,最后冲刺

2024第三届钉钉杯大学生大数据挑战赛倒计时&#xff0c;小编给大家带来非常实用的最后冲刺助力【A题】&#xff0c;&#xff08;看图资料预览&#xff09;&#xff1a; 中国烟草行业作为国家税收和财政收入的重要支柱&#xff0c;近年来销售收入持续增长。国家对此实行严格的专…

C++跳跃表个人理解

一、概念 跳跃表是一种基于有序链表的扩展&#xff0c;简称跳表&#xff0c;其就是使用关键节点作为索引的一种数据结构。 可以通过以下方式更快查找到有序链表的某一节点&#xff1a; 利用类似索引的思想&#xff0c;提取出链表中的部分关键节点。比如&#xff0c;给定一个…

C语言 ——— 指针笔试题(中篇)

指针加减整数和解引用的笔试题 笔试题1&#xff1a; int a[5][5]; int(*p)[4]; p a;printf("%p %d", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);问&#xff1a;打印的结果为&#xff1f;&#xff08;分别以 %d 和 %p 的形式打印&#xff09; …

十六、maven git-快速上手(智慧云教育平台)

&#x1f33b;&#x1f33b; 目录 一、概述及项目管理工具介绍1.1 项目介绍1.2 maven 介绍及其配置1.2.1 maven 介绍1.2.2 maven 下载与配置 1.3 pom 中常见标签的使用1.4 后端项目环境的搭建1.5 Git 简介1.6 Git 的基本使用1.6.1 码云的注册与仓库创建1.6.2 上传代码到码云仓库…

【2024】InfluxDB v2 介绍和安装使用(1)

目录&#x1f4bb; 一、介绍1、时序数据库介绍特点&#xff1a;常见的时序数据库时序库受欢迎度排名 2、InfluxDB介绍**InfluxDB 1.x 和 2.0 的主要区别**InfluxDB行协议 二、docker安装 InfluxDB v2三. Web UI常用功能介绍以及使用1、页面介绍2、功能使用2.1、创建bucket2.2、…

如何设计一个测试用例

前言&#x1f440;~ 上一章我们介绍了什么是软件测试以及软件测试的一些基础概念&#xff0c;今天来聊聊如何设计一个测试用例&#xff0c;涉及到黑盒测试的测试方法 基于需求进行测试用例的设计 基于需求的具体设计方法 等价类 边界值 判定表法 正交表法 场景设计法 …

智能ai对话软件都有哪些?分享4款!

在科技日新月异的今天&#xff0c;智能AI对话软件正以前所未有的速度改变着我们的生活方式与工作模式。它们不仅仅是冷冰冰的代码堆砌&#xff0c;更是拥有理解、学习乃至创造能力的智能伙伴&#xff0c;为人类社会带来了前所未有的便捷与乐趣。那么&#xff0c;究竟有哪些智能…

git安装和使用(托管服务 分支 克隆)超细教程

先来了解一下知识 下载与安装 (默认安装,下一步即可) 下载地址&#xff1a; https://git-scm.com/download 下载完成后可以得到如下安装文件&#xff1a; 双击下载的安装文件来安装Git。安装完成后在电脑桌面&#xff08;也可以是其他目录&#xff09;点击右键&#xff0c;如…

动态规划-斐波那契数列

一. 什么是动态规划 dp一般是需要前面状态的值的问题。比如&#xff0c;解决一个问题需要很多步骤&#xff0c;且步骤之间相关联&#xff0c;后一个步骤的推导需要前一个步骤的结论。而我们所做的就是&#xff0c;将这个带求解的问题分成若干步骤&#xff0c;将每个步骤答案保…

成为git砖家(10): 根据文件内容生成SHA-1

文章目录 1. .git/objects 目录2. git cat-file 命令3. 根据文件内容生成 sha-14. 结语5. References 1. .git/objects 目录 git 是一个根据文件内容进行检索的系统。 当创建 hello.py, 填入 print("hello, world")的内容&#xff0c; 并执行 git add hello.py gi…

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——1到手测试

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——1到手测试 ​ 大家好&#xff0c;今天给大家带来的是购买到小车或者说RDK X3之后直接快速体验&#xff0c;今天主要围绕官方的快速入门手册进行逐步测试 1.知识补充1 ​ 在这里首先要给新手小白补充几…

JVM结构、架构与生命周期总结

【1】JVM结构 不同厂商的JVM产品 &#xff1a; 厂商JVMOracle-SUNHotspotOracleJRocketIBMJ9 JVM阿里Taobao JVM HotSpot VM是目前市面上高性能虚拟机的代表作之一。它采用解释器与即时编译器并存的架构。 在今天&#xff0c;Java程序的运行性能早已脱胎换骨&#xff0c;已…

数据上新 | 景联文科技推出高质量方言音文对数据集,驱动方言语音大模型技术革新

中国电信人工智能研究院&#xff08;TeleAI&#xff09;正式对外发布星辰超多方言语音识别大模型。这是业内首个支持30种方言自由混说的语音识别大模型&#xff0c;也是目前国内支持最多方言的语音识别大模型。 方言语音大模型具有广泛的应用场景&#xff0c;可以应用于语音助手…

1.1.9创建应用

1.在全局配置文件下找到urls.py进行路由配置 配置路由要指明哪个应用&#xff0c;和写好模块导入的函数 2.找到路由要启动的应用&#xff0c;在应用里找到视图函数进行设计 path&#xff08;‘url模式/“&#xff0c;视图函数&#xff09; 注意第五部没有/ 1.1.110路由匹配模…

小阿轩yx-KVM+GFS 分布式存储系统构建 KVM 高可用

小阿轩yx-KVMGFS 分布式存储系统构建 KVM 高可用 案例分析 案例概述 使用 KVM 及 GlusterFS 技术&#xff0c;结合起来实现 KVM 高可用利用 GlusterFS 分布式复制卷对 KVM 虚拟机文件进行分布存储和冗余 分布式复制卷 主要用于需要冗余的情况下把一个文件存放在两个或两个…

刚起步的海外仓怎么选WMS系统,要注意什么

对于刚起步的海外仓企业来说&#xff0c;最紧要的事情就是把核心业务打磨平稳&#xff0c;形成核心竞争力&#xff0c;才能在激烈的竞争中赢得一席之地。 而要实现这个目的&#xff0c;WMS海外仓系统的引入当然是必要的一环&#xff0c;不过因为刚起步&#xff0c;业务和资源都…

AI产品经理的职责与能力:将AI技术转化为实际价值

一、AI产品经理的职责 发现和解决问题&#xff1a;AI产品经理需要具备敏锐的洞察力&#xff0c;能够发现用户需求和痛点&#xff0c;并提出相应的解决方案。传递价值给用户&#xff1a;AI产品经理需要确保产品能够满足用户的需求&#xff0c;提供价值&#xff0c;并提升用户体…

大模型之语言大模型技术

本文作为大模型综述第二篇,介绍语言大模型基本技术。 近年来,在 Transformer 架构基础上构建的预训练语言模型为自然语言处理领域带来了一系列突破式进展,成为人工智能主流技术范式。预训练语言模型采用“预训练+微调”方法,主要分为两步: 1)将模型在大规模无标注数据上…

No static resource favicon.ico.问题解决

一&#xff0c;问题 Spring Boot项目调用接口时报错 org.springframework.web.servlet.resource.NoResourceFoundException: No static resource favicon.ico. at org.springframework.web.servlet.resource.ResourceHttpRequestHandler.handleRequest(ResourceHttpReques…