springboot集成shiro+前端vue,前后端分离项目遇到跨域以及sessionid拿不到等问题

news2024/11/30 12:48:12

近期在写前后端分离的项目,由于前后端分离导致原来使用的shiro配置无法满足现有系统要求。同时在前后端分离项目中存在的一些问题。例如,一些用户信息需要存储在后端方便进行安全性判断,但这些存储在后端的session前端却获取不到(特别奇怪),以及浏览器访问后端接口出现的跨域问题。

1、跨域问题

由于前后端分离导致前端和后端分别占用不同的端口,所以浏览器在访问不同接口的时候就会存在跨域问题。

解决方法

我是在springboot后端项目中添加CorsConfig配置类,用于解决跨域问题,当然前端也可以解决这个跨域问题。

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class corsConfig {

    @Bean
    public WebMvcConfigurer CORSConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOrigins("*")
                        .allowedMethods("*")
                        .allowedHeaders("*")
                        //设置是否允许跨域传cookie
                        .allowCredentials(true)
                        //设置缓存时间,减少重复响应
                        .maxAge(3600);
            }
        };
    }


    @Bean
    public FilterRegistrationBean corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        // 允许cookies跨域
        config.setAllowCredentials(true);
        // #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedOrigin("*");
        // #允许访问的头信息,*表示全部
        config.addAllowedHeader("*");
        // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.setMaxAge(3600L);
        // 允许提交请求的方法,*表示全部允许
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        source.registerCorsConfiguration("/**", config);

        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        // 设置监听器的优先级
        bean.setOrder(0);

        return bean;
    }
}

这样就能解决前后端跨域问题。

2、后端存储的session,前端获取不到

起因是我在做登录的时候,用户信息被我用session存储在了后端,但是我在调用其他接口的时候,我却无法在后端获取到我保存在后端的用户信息,无法判断前端是否登录过。

这个问题一开始困扰了我很久,搜了半天发现了一个不错的解释:这是因为服务器判断前端的请求是同一个 session 的依据是通过网页默认的一个sessionid的cookie判断的,如果存在跨域,cookie值传不过来,也就当下一个请求过来时服务端无法识别为同一个会话,会被当做一个新的会话处理,故找不到session原保存的值。
在这里插入图片描述
所以我就去测试两个不同请求的sessionId是否是一样的,发现果然不一样,这也是为什么我在后端无法获取到我保存在后端的session。

解决方法

其实解决办法有很多种,例如说不用session存储数据而是利用Redis,这当然是最轻松的方式。但是本着我的session是利用shiro自带的Util存储的,所以我的解决方式是将sessionId回传给前端,然后前端保存后每次访问后端接口的时候携带保存的sessionId,这样就能我就能获取到原来的session了。

1、在登录接口中将sessionId回传给前端

public ResponseResult doLogin(String username, String password) {
    UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    Subject subject = SecurityUtils.getSubject();
    try{
        subject.login(token);
        UserInfo userInfo = baseMapper.selectById(username);
        //利用shiro的Util获取sessionId
        String sessionId = (String) subject.getSession().getId();
        //存储用户信息
        ShiroUtils.setSessionAttribute(SessionConst.USER_INFO_SESSION,userInfo);

        System.out.println((String)ShiroUtils.getSession().getId());
        
        userInfo.setSalt("");
        userInfo.setPassword("");
        Map<String,Object> data = new HashMap<>();
        //保存key-value类型数据AUTHORIZATION,值为sessionId
        data.put("AUTHORIZATION", sessionId);
        data.put("userInfo",userInfo);
        //回传给前端
        return ResponseResult.success(data);
    }catch(UnknownAccountException e){
        return ResponseResult.error(ResponseResultEnum.SESSION_ERROR);
    }catch (IncorrectCredentialsException e){
        return ResponseResult.error(ResponseResultEnum.PASSWORD_ERROR);
    }
}

前端拿到sesionId后进行保存,并每次调用接口的时候携带Id即可

//举例:登录method
login() {
    this.$axios.get("/login", {
        params: {
            username: this.form1Data.username,
            password: this.form1Data.password
        }
    }).then((res) => {
        if (res.data.code === 200) {
            console.log(res)
            var userInfo = res.data.data.userInfo
            var auth = res.data.data.AUTHORIZATION
            ElementUI.Message.success("登录成功");
            this.$store.commit("SET_USERINFO", userInfo);
            
            //将信息保存在auth中
            this.$store.commit("SET_AUTH", auth)
            
            let url_name = this.$route.params.redirect
            console.log(url_name)
            if (url_name !== undefined && url_name !== null && url_name != '') {
                this.$router.replace({
                    name: url_name
                })
            } else
                this.$router.push('/')
        } else { // 有问题
            ElementUI.Message.error(res.data.message);
        }
    }
           )
}


//在main.js文件中进行前置拦截
axios.interceptors.request.use(config => {
    let auth = store.getters.get_auth
    //从auth中获取后端传给前端的sessionId,以后调用任何接口就可以携带sessionId了
    if (auth != null) {
        config.headers['AUTHORIZATION'] = auth;
    }
    console.log(config.headers)
    return config;

}, error => {
    return Promise.reject(error)
});

2、在配置类中写一个自定义的sessionManager并重写getSessionId方法,便于对前端传递过来的sessionId进行判断,检测是否是原来的sessionId。

@Configuration
@Slf4j
public class MySessionManager extends DefaultWebSessionManager {

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {

        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader("AUTHORIZATION");
        //如果请求头中有 Authorization (前端请求头中设置的名字)则其值为sessionId
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            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);
        }
    }
}

3、在shiro配置类中注入自定义的session缓存管理器

@Bean("sessionManager")
public SessionManager sessionManager(){
    MySessionManager sessionManager = new MySessionManager();
    sessionManager.setSessionDAO(new EnterpriseCacheSessionDAO());
    sessionManager.setGlobalSessionTimeout(1000 * 60 * 30);
    sessionManager.setSessionValidationSchedulerEnabled(true);
    sessionManager.setSessionIdUrlRewritingEnabled(false);
    return sessionManager;
}

关键: 因为getSessionId()方法是通过DefaultWebSecurityManager类进行实现的,所以我们需要将SessionManager注入到安全管理中

/**
     * 安全管理
     *
     * @return
     */
@Bean
public DefaultWebSecurityManager securityManager(){
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    //注入
    securityManager.setSessionManager(sessionManager());
    securityManager.setRealm(getShiroRealm());
    return securityManager;
}

至此可解决前后端跨域后的sessionId的问题。

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

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

相关文章

专业140+总410+哈尔滨工业大学803信号与系统和数字逻辑电路考研经验哈工大电子信息(信息与通信工程-信通)

一年的努力付出终于有了收获&#xff0c;今年专业课140&#xff0c;总分410顺利上岸哈工大803电子信息&#xff08;信息与通信-信通&#xff09;&#xff0c;回顾总结了自己这一年的复习&#xff0c;有得有失&#xff0c;希望对大家复习有所帮助。 数学 时间安排&#xff1a;…

C++力扣题目40--组合总和II

力扣题目链接(opens new window) 给定一个数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用一次。 说明&#xff1a; 所有数字&#xff08;包括目标数&#xff09;都是…

linux pxe高效批量网络装机

系统装机的三种方式 U盘&#xff08;pe&#xff09; 光驱 网络启动&#xff08;pxe&#xff09; pxe PXE 的全称是 preboot execute environment它其实是Intel在很久以前提出来的一项用于通过网络来引导系统的标准。允许客户机通过网络从远程服务器上下载引导镜像&#…

252:vue+openlayers 绘制锥形渐变填充色的圆形

第252个 点击查看专栏目录 本示例的目的是介绍如何在vue+openlayer中绘制带有锥形渐变填充色的圆形。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共131行)相关API参考专栏目标示例效果 </

【论文阅读】Speech Driven Video Editing via an Audio-Conditioned Diffusion Model

DiffusionVideoEditing&#xff1a;基于音频条件扩散模型的语音驱动视频编辑 code&#xff1a;GitHub - DanBigioi/DiffusionVideoEditing: Official project repo for paper "Speech Driven Video Editing via an Audio-Conditioned Diffusion Model" paper&#…

IOS-高德地图SDK接入-Swift

申请key 这个要前往高德开发平台注册成为个人开发者然后在控制台创建一个应用&#xff1a; 高德开发平台 注册步骤就不写了&#xff0c;写一下创建应用的步骤&#xff1a; 1、点击应用管理——>我的应用 2、点击右上角的创建新应用 3、输入内容&#xff1a; 4、点击添加ke…

计算机视觉丨基于OpenCV的人脸识别打卡系统

文章目录 写在前面项目分析需求分析1.数据模型2.打卡功能3.数据维护4.考勤报表 系统设计项目设计1.文件系统2.数据模块3.工具模块4.服务模块5.人脸识别6.主函数 运行结果写在后面 写在前面 本期内容&#xff1a;基于OpenCV的WANT公司人脸识别打卡系统。 项目需求&#xff1a;…

Flink(十三)【Flink SQL(上)】

前言 最近在假期实训&#xff0c;但是实在水的不行&#xff0c;三天要学完SSM&#xff0c;实在一言难尽&#xff0c;浪费那时间干什么呢。SSM 之前学了一半&#xff0c;等后面忙完了&#xff0c;再去好好重学一遍&#xff0c;毕竟这玩意真是面试必会的东西。 今天开始学习 Flin…

阿里云 linux Centos7 安装 Miniconda3 + 创建Python环境

1.下载miniconda &#xff08;1&#xff09;法一&#xff1a;可以去下载清华源的miniconda镜像源&#xff0c;选择自己需要的版本&#xff0c;然后上传到Linux服务器上&#xff0c;linux上使用请选择linux版本&#xff0c;如下&#xff1a; &#xff08;2&#xff09;法二&…

在Excel中如何打开VBA,这里提供两种方法

想在Excel中创建或添加自己的自定义Visual Basic脚本吗&#xff1f;第一步是了解如何在Excel中打开VBA编辑器。 在易用性和整体功能方面&#xff0c;没有其他电子表格应用程序能与Excel相提并论。无论你想做什么&#xff0c;只要你能深入挖掘Excel的深层菜单&#xff0c;就有很…

Ubuntu 22.04 Cron使用

需要定时处理的场景还是比较多的&#xff0c;比如信息推送、日志清理等。 这篇文章我们来说说如何使用cron来实现定时处理&#xff0c;以及监控任务的执行。 使用 Ubuntu中使用cron&#xff0c;要用到的命令是crontab。不加sudo时&#xff0c;处理的是个人的定时任务。当加上…

HarmonyOS应用开发者高级认证试题库(鸿蒙)

目录 考试链接&#xff1a; 流程&#xff1a; 选择&#xff1a; 判断 单选 多选 考试链接&#xff1a; 华为开发者学堂华为开发者学堂https://developer.huawei.com/consumer/cn/training/dev-certification/a617e0d3bc144624864a04edb951f6c4 流程&#xff1a; 先进行…

支持华为GaussDB数据库的免费开源ERP:人力资源管理解决方案概述

开源智造所推出的Odoo SuperPeople数字化解决方案将HR和薪资数据与财务、项目规划、预算和采购流程连接起来&#xff0c;消除了多套系统给企业带来的信息孤岛问题。 ——复星集团 人力资源中心 高经理 一种更具吸引力、更有洞察力的人员管理方式 什么是开源智造Odoo的人力资源…

找不到mfc100.dll的解决方法,怎么修复mfc100.dll文件

当我们在使用电脑时&#xff0c;时常可能会遇到各类系统提示的错误信息。"找不到mfc100.dll" 就是这些错误之一&#xff0c;该错误提示会妨碍我们执行一些应用程序或特定代码。为了帮助读者克服这个技术障碍&#xff0c;本篇文章将详尽阐明导致该问题的根本原因&…

SQL进阶3

二、多表连结 1、什么叫联结 下面&#xff0c;我们举个例子来说明&#xff1a; 学校的安排的课程信息&#xff0c;我们平时都会为主要人员负责的对应课程信息创建表格&#xff0c;让其更好地检索得到对应数据信息。学生可以查到自己本身的课程信息&#xff0c;而老师也可以查…

三种引入CSS的方式

文章目录 CSS基础知识概述CSS的注释CSS的格式 三种引入CSS的方式内嵌式外链式行内式优先级 CSS基础知识 概述 Cascading Style Sheet 层叠样式表 前端三大基础之一(Html结构 CSS样式 JS动作) 最早由网景公司&#xff08;Netscape&#xff09;提出&#xff0c;在1996年受到w…

介绍 sCrypt:BTC 的 Layer-1 智能合约框架

在 TypeScript 中开发 BTC 智能合约 我们非常高兴地推出 sCrypt&#xff1a;一种现代 Typescript 框架&#xff0c;用于在 BTC 上开发第一层智能合约&#xff0c;无需分叉。 现在&#xff0c;人们可以使用现代开发工具在易于使用的统一框架中编写、测试、调试、部署和调用智能合…

leetcode—搜索二维矩阵II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,2…

Android studio第一次构建项目Gradle失败的解决方法

每次在AS上新创建一个项目&#xff0c;gradle要下载半天或者是直接下载半天后以失败告终&#xff0c;抓狂并崩溃。 原因&#xff1a; 这是因为AS默认去下载gradle的网站是在国外的&#xff0c;而且国内的网络经常是访问不到那个网站的&#xff0c;能访问到有时候就跟中大奖一…

Docker之网络配置的使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《Docker之网络配置的使用》。&#x1f3af;&…