Vue前后端分离项目 【实战篇】

一、shiro中session的共享问题🍉

在这里插入图片描述

1.演示问题🥝

(1)启动shiro-springboot的集群项目🍓

在这里插入图片描述

(2)修改nginx的配置🍓

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

(3)测试🍓

使用swagger测试需要在过滤器中放行

//测试路径
http://localhost:8080/doc.html

在这里插入图片描述
在这里插入图片描述

登录成功后访问某些资源时,出现了未登录的json提示

在这里插入图片描述

2.如何解决session共享问题🥝

默认session存储再各自服务的内存中,可以让session统一存储再redis中。

疯狂的蛋糕的依赖。—提供了redis存储session的类。

修改shiro的配置类。

在这里插入图片描述

package com.lzq.config;import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.lzq.filter.LoginFilter;
import com.lzq.filter.MyWebSessionManagerextends;
import com.lzq.realm.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;@Configuration
public class ShiroConfig {@Beanpublic DefaultWebSecurityManager securityManager(){DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();securityManager.setRealm(myRealm());//设置session管理器securityManager.setSessionManager(sessionManager());securityManager.setCacheManager(redisCacheManager());return securityManager;}@Beanpublic SessionManager sessionManager(){MyWebSessionManagerextends sessionManager = new MyWebSessionManagerextends();//SessionDao用于操作session对象,在容器中对session对象进行CRUD操作sessionManager.setSessionDAO(sessionDAO());return sessionManager;}@Beanpublic SessionDAO sessionDAO(){//该类会对session对象进行crud操作RedisSessionDAO sessionDAO = new RedisSessionDAO();sessionDAO.setRedisManager(redisManager());return sessionDAO;}/*** RedisSessionDAO shiro sessionDao层的实现 通过redis* 使用的是shiro-redis开源插件*/@Beanpublic MyRealm myRealm(){MyRealm myRealm=new MyRealm();//设置密码加密器myRealm.setCredentialsMatcher(credentialsMatcher());return myRealm;}@Value("${shiro.hashAlgorithmName}")private String hashAlgorithmName;@Value("${shiro.hashIterations}")private int hashIterations;@Beanpublic HashedCredentialsMatcher credentialsMatcher(){HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();credentialsMatcher.setHashAlgorithmName(hashAlgorithmName);credentialsMatcher.setHashIterations(hashIterations);return credentialsMatcher;}@Bean(name = "shiroFilter")public ShiroFilterFactoryBean factoryBean(){ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager());shiroFilterFactoryBean.setLoginUrl("/login.html");//设置过滤规则Map<String,String> map=new HashMap<>();map.put("/login","anon");map.put("/doc.html","anon");map.put("/webjars/**","anon");map.put("/swagger-resources/**","anon");map.put("/v2/**","anon");map.put("/**","authc");shiroFilterFactoryBean.setFilterChainDefinitionMap(map);Map<String,Filter> filterMap=new HashMap<>();filterMap.put("authc",new LoginFilter());shiroFilterFactoryBean.setFilters(filterMap);return shiroFilterFactoryBean;}//springboot如何注册web三大组件。@Beanpublic FilterRegistrationBean<Filter> filterRegistrationBean(){FilterRegistrationBean<Filter> filterRegistrationBean=new FilterRegistrationBean<>();filterRegistrationBean.setFilter(new DelegatingFilterProxy());filterRegistrationBean.setName("shiroFilter");filterRegistrationBean.addUrlPatterns("/*");return filterRegistrationBean;}/*** 这里是为了能在html页面引用shiro标签,*/@Bean(name = "shiroDialect")public ShiroDialect shiroDialect() {return new ShiroDialect();}@Beanpublic RedisCacheManager redisCacheManager(){RedisCacheManager redisCacheManager = new RedisCacheManager();redisCacheManager.setRedisManager(redisManager());return redisCacheManager;}@Beanpublic RedisManager redisManager(){RedisManager redisManager = new RedisManager();redisManager.setHost("192.168.179.129");redisManager.setDatabase(1);return redisManager;}
}

在这里插入图片描述

二、解决前端不支持cookie的效果🍉

在这里插入图片描述

原因: 默认DefaultWebSessionManager它只接受Cookie中存储的JsessionId. 查询发现再redis中不存在对应的key.

客户发送请求时,再请求头中携带sessionId, 然后重写DefaultWebSessionManager中getSessionId()的方法。

思考:1. 如何把sessionId放入请求头。

​ 2. 重写getSessionId方法如何获取请求头的sessionID。

1.如何把sessionId放入请求头🥝

修改登录的接口

在这里插入图片描述

 @PostMapping("/login")@ResponseBodypublic Result login(@RequestBody LoginVo loginVo){Subject subject = SecurityUtils.getSubject();//判斷当前用户是否登陆过if (subject.isAuthenticated()){return new Result(200,"登陸成功",subject.getSession().getId());}UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(),loginVo.getPassword());try {subject.login(token);System.out.println("登录成功");return new Result(200,"登录成功",subject.getSession().getId());}catch (Exception e){e.printStackTrace();System.out.println("登录失败");return new Result(500,"登陆失败",null);}

修改前端登录方法

在这里插入图片描述

 lzq(){this.$http.post("http://localhost:8080/login",this.formLabelAlign).then(requs=>{console.log(requs)if (requs.data.code==200){this.$message.success("登陆成功")sessionStorage.setItem("token",requs.data.data)this.$router.push("/aaa")}else {this.$message.error("账户密码错误")}})},

修改main.js文件

设置axios请求拦截器 进行拦截请求 将后端传过来的session id 赋值给 sessionStorage中 赋给请求头中

在这里插入图片描述

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
import axios from "axios"
import it from "element-ui/src/locale/lang/it";router.beforeEach(((to, from, next) =>{//to:到哪去 from:从哪来  next:下一站var path = to.path;if (path=="/login"){return next();}//设置变量token  用来存储session idvar  token = sessionStorage.getItem("token");if (token){return  next();}return next("/login");
} ))
//设置axios的请求拦截器
axios.interceptors.request.use(config=>{//从sessionStorage中获取token  token存储的session idvar item=sessionStorage.getItem("token");if (item){config.headers.token=item;}return config;
})Vue.prototype.$http=axiosVue.config.productionTip = falsenew Vue({router,render: h => h(App)
}).$mount('#app')

2.重写DefaultWebSessionManager的方法🥝

在这里插入图片描述

package com.lzq.filter;import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;public class MyWebSessionManagerextends extends DefaultWebSessionManager {private static final String AUTHORIZATION = "token";private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";protected Serializable getSessionId(ServletRequest request, ServletResponse response) {//获取请求头中名称为token的内容String id = WebUtils.toHttp(request).getHeader("token");if (!StringUtils.isEmpty(id)) { //如果存在该tokenrequest.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "Stateless request");request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);return id;} else {//从cookie中获取sessionId.return super.getSessionId(request, response);}}}

重写DefaultWebSessionManager的方法是为了获取前端传过来的session id
重写后需要将配置文件交给ioc容器进行管理
在这里插入图片描述

 @Bean(name = "shiroFilter")public ShiroFilterFactoryBean factoryBean(){ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager());shiroFilterFactoryBean.setLoginUrl("/login.html");//设置过滤规则Map<String,String> map=new HashMap<>();map.put("/login","anon");map.put("/doc.html","anon");map.put("/webjars/**","anon");map.put("/swagger-resources/**","anon");map.put("/v2/**","anon");map.put("/**","authc");shiroFilterFactoryBean.setFilterChainDefinitionMap(map);Map<String,Filter> filterMap=new HashMap<>();filterMap.put("authc",new LoginFilter());shiroFilterFactoryBean.setFilters(filterMap);return shiroFilterFactoryBean;}

修改shiro配置类

在这里插入图片描述

  @Beanpublic SessionManager sessionManager(){MyWebSessionManagerextends sessionManager = new MyWebSessionManagerextends();//SessionDao用于操作session对象,在容器中对session对象进行CRUD操作sessionManager.setSessionDAO(sessionDAO());return sessionManager;}

修改shiroFilter过滤器

我们发现跨域请求,会发送两个请求:第一个OPTIONS请求,第二个请求是真实的请求。

OPTIONS请求:先头部队。

所以我们对OPTIONS请求都要放行。

在这里插入图片描述
在这里插入图片描述

package com.lzq.filter;import com.alibaba.fastjson.JSON;
import com.lzq.vo.Result;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;public class LoginFilter extends FormAuthenticationFilter {//当未登录账号访问接口时,会触发该方法。//默认内容是重定向到登录页面---重写改为返回json数据@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {//解决响应数据的中文乱码问题response.setContentType("application/json;charset=utf-8");//通过io流的方式将响应返还给前端PrintWriter writer = response.getWriter();Result result = new Result(401, "请先登录", null);//把java对象转换为json字符串  ----JSON java封装的工具类String jsonstring = JSON.toJSONString(result);writer.print(jsonstring);//刷新io流writer.flush();//关闭io流资源writer.close();return false;}@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {//将请求转换格式  获取其中内容HttpServletRequest request1 = (HttpServletRequest) request;//获取请求方式String method = request1.getMethod();//判断将options访问类型进行放行if ("OPTIONS".equals(method)){return true;}return super.isAccessAllowed(request, response, mappedValue);}
}

三、设置前端前置路由守卫🍉

在这里插入图片描述

router.beforeEach(((to, from, next) =>{//to:到哪去 from:从哪来  next:下一站var path = to.path;if (path=="/login"){return next();}//设置变量token  用来存储session idvar  token = sessionStorage.getItem("token");if (token){return  next();}return next("/login");
} ))

四、如何防止恶意重复登录🍉

在这里插入图片描述

package com.lzq.controller;import com.lzq.vo.LoginVo;
import com.lzq.vo.Result;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
//@CrossOrigin//origins:允许那些跨域访问该接口  allowedHeaders: 允许携带那些头信息的请求访问  methods:  允许那些请求跨域请求该接口
public class LoginController {@PostMapping("/login")@ResponseBodypublic Result login(@RequestBody LoginVo loginVo){Subject subject = SecurityUtils.getSubject();//判斷当前用户是否登陆过if (subject.isAuthenticated()){return new Result(200,"登陸成功",subject.getSession().getId());}UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(),loginVo.getPassword());try {subject.login(token);System.out.println("登录成功");return new Result(200,"登录成功",subject.getSession().getId());}catch (Exception e){e.printStackTrace();System.out.println("登录失败");return new Result(500,"登陆失败",null);}}@PostMapping("/logout")@ResponseBodypublic Result logout(){Subject subject = SecurityUtils.getSubject();//清空redissubject.logout();return new Result(200,"退出成功",null);}@GetMapping("/unlogin")@ResponseBodypublic Result unlogin(){return new Result(401,"请先登录",null);}}

五、退出功能🍉

编辑退出接口🥝

在这里插入图片描述

  @PostMapping("/logout")@ResponseBodypublic Result logout(){Subject subject = SecurityUtils.getSubject();//清空redissubject.logout();return new Result(200,"退出成功",null);}

编辑前端退出按钮🥝

在这里插入图片描述

<template><div><el-button type="primary" @click="logout">退出</el-button><el-button @click="info" type="primary">获取用户信息</el-button><el-button @click="query" type="primary">查询</el-button><el-button @click="daochu" type="primary">导出</el-button><span v-text="userinfo.username"></span></div>
</template><script>export default {name: "Aaa",data(){return{userinfo:{}}},created() {},methods: {logout() {this.$http.post("http://localhost:8080/logout").then(requs=>{if (requs.data.code==200){this.$message.success("退出成功")sessionStorage.clear()this.$router.push("/login")}})},info(){this.$http.get("http://localhost:8080/user/info").then(requs=>{this.userinfo=requs.data.data;})},query(){this.$http.get("http://localhost:8080/user/query").then(requs=>{if (requs.data.code==200){this.$message.success("查询成功")}})},daochu(){}}}</script><style scoped></style>

六、获取当前登录用户的信息🍉

在这里插入图片描述

package com.lzq.controller;import com.lzq.vo.Result;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@GetMapping("/info")public Result inf(){Object principal = SecurityUtils.getSubject().getPrincipal();return new Result(200,"查询成功",principal);}@GetMapping("/query")@ResponseBody
//    @RequiresPermissions(value = {"user:query","user:delete"},logical = Logical.OR)@RequiresPermissions(value = "user:query")public Result query(){return new Result(200,"查詢成功",null);}@GetMapping("/update")@RequiresPermissions(value = "user:update")public String update(){return "user:update------------------------";}@GetMapping("/delete")@RequiresPermissions(value = "user:delete")public String delete(){return "user:delete------------------------";}@GetMapping("/insert")@RequiresPermissions(value = "user:insert")public String insert(){return "user:insert------------------------";}@GetMapping("/export")@RequiresPermissions(value = "user:export") //该注解不能别识别public String export(){return "user:export------------------------";}
}

在这里插入图片描述

<template><div><el-button type="primary" @click="logout">退出</el-button><el-button @click="info" type="primary">获取用户信息</el-button><el-button @click="query" type="primary">查询</el-button><el-button @click="daochu" type="primary">导出</el-button><span v-text="userinfo.username"></span></div>
</template><script>export default {name: "Aaa",data(){return{userinfo:{}}},created() {},methods: {logout() {this.$http.post("http://localhost:8080/logout").then(requs=>{if (requs.data.code==200){this.$message.success("退出成功")sessionStorage.clear()this.$router.push("/login")}})},info(){this.$http.get("http://localhost:8080/user/info").then(requs=>{this.userinfo=requs.data.data;})},query(){this.$http.get("http://localhost:8080/user/query").then(requs=>{if (requs.data.code==200){this.$message.success("查询成功")}})},daochu(){}}}</script><style scoped></style>

七、设定登录设备的个数🍉

在这里插入图片描述

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

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

相关文章

ModaHub魔搭社区:向量数据库Zilliz Cloud集群、Collection 及 Entity教程

目录 集群 Collection 字段 Schema 索引 Entity Zilliz Cloud 集群由全托管 Milvus 实例及相关计算资源构成。您可以在 Zilliz Cloud 集群中创建 Collection,然后在 Collection 中插入 Entity。Zilliz Cloud 集群中的 Collection 类似于关系型数据库中的表。Collection …

请求响应-json参数的接收

JSON参数 JSON参数&#xff1a;JSON数据键名与形参对象属性名&#xff08;即实体类中的成员变量&#xff09;相同&#xff0c;定义POJO实体类即可接收参数&#xff0c;需要使用RequestBody标识前端发送JSON格式的数据&#xff0c;要使用POST方法发送请求&#xff0c;因为JSON格…

FASTADMIN联合查询 搜索

控制器中添加relationSearch开关 控制器里面添加联合查询 MODEL里面添加 js里面添加即可 可以查看数据json 搜索开启searchFileds就可以了

前端基础知识学习——圆角、透明圆角(十四)

文章目录 圆角方法一圆角方法二&#xff08;推荐&#xff09;透明圆角方法一透明圆角方法二&#xff08;推荐&#xff09; 圆角方法一 /* 添加圆角 方法1&#xff1a;border-radius cs3不兼容*/.bo{width: 100px; height: 200px;border: 1px solid #e5e5e5;margin:30px aut…

【MySQL】如何优化SQL查询的总体框架(详细版,关于如何优化数据库服务器从大到小详细说明了步骤)

文章目录 1 数据库服务器的优化步骤2 观察2.1 观察系统总体运行情况2.2 定位执行慢的 SQL&#xff1a;慢查询日志2.3 查看 SQL 执行成本&#xff1a;SHOW PROFILE2.4 分析查询语句&#xff1a;EXPLAIN&#xff08;重点掌握&#xff09;2.4.1 EXPLAIN各列作用2.4.2 EXPLAIN 的 t…

【嵌入式Qt开发入门】Qt如何使用多线程——继承QObject的线程

QObject 在上篇已经说过&#xff0c;继承 QThread 类是创建线程的一种方法&#xff0c;另一种就是继承 QObject 类。继承 QObject 类更加灵活。它通过 QObject::moveToThread()方法&#xff0c;将一个 QObeject 的类转移到一个线程里执行&#xff0c;可以通过下图理解。 通过…

6.2.1 网络基本服务---域名解析系统DNS

6.2.1 网络基本服务—域名解析系统DNS 因特网是需要提供一些最基本的服务的&#xff0c;今天我们就来讨论一下这些基本的服务。 域名系统&#xff08;DNS&#xff09;远程登录&#xff08;Telnet&#xff09;文件传输协议&#xff08;FTP&#xff09;动态主机配置协议&#x…

机器学习之随机森林(Random forest)

1 什么是随机森林 随机森林是一种监督式算法&#xff0c;使用由众多决策树组成的一种集成学习方法&#xff0c;输出是对问题最佳答案的共识。随机森林可用于分类或回归&#xff0c;是一种主流的集成学习算法。 1.1 随机森林算法原理 随机森林中有许多的分类树。我们要将一个输…

Android Studio无法打开问题解决记录

目录 1 问题起因2 发现问题3 解决问题 1 问题起因 问题的起因是我为了运行一个Kotlin项目&#xff0c;但是报了一个错误&#xff1a; Kotlin报错The binary version of its metadata is 1.5.1, expected version is 1.1.16 然后我就上百度去搜了以下&#xff0c;一篇博客让禁用…

echarts饼图设置颜色的两种方式

1. 直接写在color数组中 option {color:[#fac858,#e0504b,#e6e9ee],series: {type: pie,radius: [40%, 70%],data: [{ value: 1048, name: Search Engine, },{ value: 735, name: Direct},{ value: 580, name: Email },]} };2. 在series.data.itemStyle.color中 option {se…

ofd文件怎么打开?试试3个打开方法

什么是ofd文件&#xff1f; 很多朋友对ofd文件也许不太了解&#xff0c;它实际上就是开放版式文件的意思&#xff0c;即&#xff08;Open Fixed-layout documents&#xff09;的缩写。ofd文件与PDF文件很类似。都是有独立格式、版面固定的特点的。在我们日常生活中&#xff0c;…

langchain调用chatGLM2纪实

一、科学上网要注意&#xff1a; 域名全代和全局代理&#xff08;网卡&#xff09;&#xff0c;都要打开。这样conda install特别快。 二、安装langchain 1、 conda install langchain 2、 conda install openai 注意&#xff1a; 使用pip install和conda install 是不同…