shiro的前后端分离模式

news2024/11/30 2:24:53

shiro的前后端分离模式

前言:在上一篇《shiro的简单认证和授权》中介绍了shiro的搭建,默认情况下,shiro是通过设置cookie,使前端请求带有“JSESSION”cookie,后端通过获取该cookie判断用户是否登录以及授权。但是在前后端分离模式中,前后端不在同一个域内,后端无法自动给请求添加cookie,因而默认的模式不能实现正常的认证授权逻辑。

我们可以将subject的sessionId通过登录成功的请求返回给前端,前端获取sessionId后在每个请求的header中带上sessionId(token),后端通过继承DefaultWebSessionManager类,实现自定义的session管理器,改写getSession的方法,改在header中获取sessionId进行后续认证与授权。

以下例子只是demo,不注重正式开发细节,例如返回值封装,请求Restful风格等等,正式项目中注意甄别

1.登录成功,将sessionId返回给前端

    /**
     * 登录接口
     * @param username
     * @param password
     * @return
     */
    @GetMapping("/login")
    public String login(String username,String password){
            // 获取当前主体
            Subject subject = SecurityUtils.getSubject();
            // 构建登录token,login方法进入中进入我们自定义的realm的doGetAuthenticationInfo方法
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
            subject.login(usernamePasswordToken);
            String token = (String) SecurityUtils.getSubject().getSession().getId();
            return "登陆成功,token为:"+token;
    }

2.前端携带token请求

前端将获取到的token写到sessionStorage或者localStorage,然后请求时添加到请求头中,例如axios请求例子

    axios({
      method:"GET",
      url:"http://127.0.0.1:8080/testIndex",
      headers:{
        'Content-Type':' application/json',
        'token':'bd9ae5c3-570f-4ed2-ac57-8f935655ef55',   // token从localStorage中获取
      },
      withCredentials: true,
    }).then((r)=>{
      console.log(r)
    })

3.继承DefaultWebSessionManager

Shiro中DefaultWebSessionManager管理shiro的session会话,包括设置cookie,获取前端携带的shiro的sessionId等等。我们可以重写其方法,实现自己的逻辑,最后添加到SecurityManager中,例如重写getSessionId方法,改为从header中获取sessionId

public class CustomSessionManager extends DefaultWebSessionManager {

    private static final String HEADER_TOKEN = "token";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public CustomSessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader(HEADER_TOKEN);
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled());
            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 {

            return super.getSessionId(request, response);
        }
    }
}

4. 在ShiroConfig中加入SecurityManager

在ShiroConfig中,创建自定义的SessionManager实例,然后添加到SecurityManager

    /**
     * sessionManager
     */
    public DefaultWebSessionManager sessionManager() {
//        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        CustomSessionManager sessionManager = new CustomSessionManager();
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setSessionIdCookieEnabled(false);
        return sessionManager;
    }

    /**
     * SecurityManager
     */
    @Bean
    public SecurityManager securityManager(Realm myRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置一个Realm,这个Realm是最终用于完成我们的认证号和授权操作的具体对象
        securityManager.setRealm(myRealm);
        securityManager.setSessionManager(sessionManager());
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

5.跨域设置

前后端分离肯定离不开跨域设置,跨域有多重设置方法,这里不赘述

/**
 * 解决跨域问题
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders(CorsConfiguration.ALL);
//                .allowedHeaders("*");	
    }
}

6.添加过滤器

这里是一个坑,我以为完成以上步骤就可以实现前后端分离,但是测试后发现,有一些请求符合预期,但是有一些请求却报跨域,甚至乎不是寻常的报跨域
在这里插入图片描述

浏览器控制台输出

在这里插入图片描述

如果是跨域的话,按道理讲应该所有请求都会报跨域,但是login请求没有报

那么就应该跟shiro的FilterChainDefinitionMap有关,只有拦截做验证的请求才会报错。以为是菜狗,查了好久,都没有找到正确答案。然后在调试时发现报错的请求不是我写的get请求,而是options请求。

事实上在这之前我只听说过options,并没去了解options请求的作用,看到报差后才去百度一下

Options 请求是 HTTP 协议中的一种请求方法,它用于获取服务器支持的请求方法和其他选项。Options 请求通常不会包含实际的请求数据,而是用于获取服务器的元数据信息。

Options 请求的主要作用包括:

  1. 获取服务器支持的请求方法:Options 请求可以获取服务器支持的请求方法,例如 GET、POST、PUT、DELETE 等。这对于客户端来说非常有用,因为它可以确定服务器是否支持特定的请求方法。

  2. 检查服务器的能力:Options 请求可以检查服务器的能力,例如是否支持跨域请求、是否支持特定的 HTTP 头、是否支持特定的内容类型等。

  3. 探测服务器:Options 请求可以用于探测服务器,例如确定服务器的版本、操作系统、服务器软件等信息。这对于安全测试和漏洞扫描非常有用。

Options 请求通常被视为一种安全的请求方法,因为它不会对服务器产生实际的影响。但是,服务器可能会限制 Options 请求的使用,例如限制请求频率或限制请求的来源。因此,在使用 Options 请求时,应该遵守服务器的规定,并确保请求是合法和必要的。

原来options这么重要,那报的跨域肯定就是这个options搞的鬼了。当然就算知道问题所在,我这菜狗也是想不到解决方案的,但至少会百度。

然后就查到以下文章

[SpringBoot+Shiro放行OPTIONS请求,解决跨域问题] https://blog.csdn.net/qq_41289165/article/details/121529166

第一步,新建一个Filter继承Shiro的UserFilter,里面的preHandle方法对options请求直接放行。还有其他逻辑看注释,这里不赘述

@Slf4j
public class ShiroUserFilter extends UserFilter {
    /**
     * 在访问过来的时候检测是否为OPTIONS请求,如果是就直接返回true
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            log.info("OPTIONS放行");
            setHeader(httpRequest,httpResponse);
            return true;
        }
        return super.preHandle(request,response);
    }

    /**
     * 该方法会在验证失败后调用,这里由于是前后端分离,后台不控制页面跳转
     * 因此重写改成传输JSON数据
     */
    @Override
    protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        saveRequest(request);
        setHeader((HttpServletRequest) request,(HttpServletResponse) response);
        PrintWriter out = response.getWriter();
        //自己控制返回的json数据
        Map map = new HashMap();
        map.put("code",500);
        map.put("message","Shiro验证失败");
        out.println(map);
        out.flush();
        out.close();
    }

    /**
     * 为response设置header,实现跨域
     */
    private void setHeader(HttpServletRequest request, HttpServletResponse response){
        //跨域的header设置
        response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", request.getMethod());
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
        //防止乱码,适用于传输JSON数据
        response.setHeader("Content-Type","application/json;charset=UTF-8");
        response.setStatus(HttpStatus.OK.value());
    }
}

第二步,将该Filter添加到FilterFactoryBean中

在ShiroConfig中的shiroFilterFactoryBean方法中,添加shiroFilter.getFilters().put("authc", new ShiroUserFilter());

@Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        // 添加自定义filter
        shiroFilter.getFilters().put("authc", new ShiroUserFilter());

        // 认证失败(用户没登录时)会重定向到这个loginUrl
        shiroFilter.setLoginUrl("/");
        // 授权失败重定向路径
        shiroFilter.setUnauthorizedUrl("/noPermission");
        // 定义一个LinkHashMap,保存认证的路径和规则
        Map<String,String> map = new LinkedHashMap();
        // key为uri,anon为标识符,标识不需要验证
        map.put("/login","anon");
        // authc表示Authentication,即是需要认证
        map.put("/admin/**","authc");
        // key “/**"表示所有uri,”/**“必须写在最后,如果不写该项,默认放行所有没配置的uri
        map.put("/**","authc");
        // 将该map设置进shiroFilter
        shiroFilter.setFilterChainDefinitionMap(map);
        return shiroFilter;
    }

7.测试

至此,前后端分离实现完毕

options请求正常

在这里插入图片描述

验证通过的请求能正常访问接口

在这里插入图片描述

补充

以上已经实现shiro的前后端分离功能,这里再补充下,将shiro默认的set-cookie行为去掉,只需要设置SessionManager的SessionIdCookieEnabled为false即可,如下在ShiroConfig中的SessionManager

    public DefaultWebSessionManager sessionManager() {
        CustomSessionManager sessionManager = new CustomSessionManager();
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionDAO(redisSessionDAO());
        // 将shiro默认的set-cookie行为去掉
        sessionManager.setSessionIdCookieEnabled(false);
        return sessionManager;
    }

至此,全篇结束

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

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

相关文章

30系列显卡在ubuntu下不能满血运行的问题

之前发现在ubuntu下&#xff0c;我的3080只能跑115w最高&#xff0c;而这在win下是可以跑165w的。于是乎google了所有结果&#xff0c;无解… 现已经过去一年&#xff0c;显卡价格飞涨&#xff0c;无奈只能使用笔记本跑自己的代码了。结果发现nvidia推了Linux下的动态加速&…

用友NC word.docx接口存在任意文件读取漏洞 附POC

@[toc] 用友NC word.docx接口存在任意文件读取漏洞 附POC 免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使…

有了倾斜摄影,如何搭建一座智慧城市?

随着无人机航测、倾斜摄影等全新一代测绘信息技术方法的发展&#xff0c;可以迅速搜集制作精细化的城市三维模型&#xff0c;搭建城市地理信息基础服务架构。 近期都在重点关注的“智慧城市”究竟是什么&#xff0c;有什么重大作用&#xff0c;同时又面临着什么难关&#xff0c…

Deepin使用记录-deepin系统下安装RabbitMq

目录 0、引言 1、由于RabbitMq是erlang语言开发的&#xff0c;所有需要先安装erlang 2、更新源并安装RabbitMq 3、安装完成之后&#xff0c;服务是启动的&#xff0c;可以通过以下语句查看状态 4、这样安装完成之后&#xff0c;是看不到web页面的&#xff0c;需要再安装一…

调试器gdb

目录 一、调试 1、前言 2、 debug和release 二、基本操作 1、退出 quit 2、开始调试 r 3、打断点 b 4、查看断点 info b 5、查看代码 l 6、删除断点 d 7、逐过程 n 8、打印变量内容 p 9、逐语句&#xff08;进入函数&#xff09; s 10、查看函数调用堆栈 bt 11、…

扫描条形码到电脑:Barcode to pc 4.6.3 Crack

像专业人士一样使用条形码将条形码发送到 PC 排名第一的智能手机扫描应用程序 将条形码即时发送到计算机程序并自动执行任务的最简单方法 受到全球 500,000 多名用户的信赖 条形码到 PC&#xff1a;Wi-Fi 扫描仪应用程序&#xff0c;条码到 PC&#xff1a;适用于 Android 和 i…

visual stdio动态库的使用

导出类和使用方式 #ifndef PCH_H #define PCH_H// 添加要在此处预编译的标头 #include "framework.h"#ifdef _WIN32 #ifdef MYCLASS_EXPORTS #define MYCLASS_API __declspec(dllexport) #else #define MYCLASS_API __declspec(dllimport) #endif #else #define MYC…

Nginx反向代理实现负载均衡webshell

目录 本实验所用的环境&#xff1a; 问题一&#xff1a;由于nginx采用的反向代理是轮询的方式&#xff0c;所以上传文件必须在两台后端服务器的相同位置上传相同的文件 问题二&#xff1a;我们在执行命令时&#xff0c;无法知道下次的请求交给哪台机器去执行我们在执行hostn…

C#,数值计算——有理函数插值和外推(Rational_interp)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 有理函数插值和外推 /// Rational Function Interpolation and Extrapolation /// Given a value x, and using pointers to data xx and yy, this routine returns …

AtomicReference原子引用类-线程安全

简介与作用&#xff1a; AtomicReference是Java中的一个原子类&#xff0c;它的主要作用是提供了一种原子操作的方式来更新对象的引用。它通常用于多线程环境下&#xff0c;用来解决并发访问共享对象时可能出现的竞态条件问题。 &#xff08;实际开发中用于某个数据模型更新&a…

Elasticsearch:ES|QL 查询中的元数据字段及多值字段

在今天的文章里&#xff0c;我来介绍一下 ES|QL 里的元数据字段以及多值字段。我们可以利用这些元数据字段以及多值字段来针对我们的查询进行定制。 ES|QL 源数据字段 ES|QL 可以访问元数据字段。 目前支持的有&#xff1a; _index&#xff1a;文档所属的索引名称。 该字段的…

从0开始学习JavaScript--JavaScript事件:响应与交互

JavaScript的事件处理是Web开发中至关重要的一部分&#xff0c;通过事件&#xff0c;能够实现用户与页面的互动&#xff0c;使得网页更加生动和交互性。本文将深入探讨JavaScript事件的各个方面&#xff0c;包括事件的基本概念、事件类型、事件对象、事件冒泡与捕获、事件委托、…

MATLAB在信号系统中的应用

1.产生一个幅度为1, 基频为2Hz&#xff0c;占空比为50%的周期方波.要求画出图形。 在MATLAB中&#xff0c;函数square(w0*t, DUTY)产生基本频率为w0 (周期T2*pi/w0)、占空比DUTY (τ/T)*100的周期矩形波&#xff08;方波&#xff09;&#xff0c;默认情况下占空比DUTY50。占空…

数据结构总复习

文章目录 线性表动态分配的顺序存储结构链式存储 线性表 动态分配的顺序存储结构 通过分析代码&#xff0c;我们发现&#xff0c;要注意什么&#xff1a; 要分清你的下标Insert 函数是可以用来没有元素的时候&#xff0c;增加元素的Init(或者Create )函数一般只用来分配空间…

关于泛型方法重写的问题--继承/重写/泛型

第二张图中的抽象类实现了图1 中的泛型接口;其中图2中的handler方法重写图1中的接口方法是没有问题的,但是图2中的next泛型方法指定类型重写图1中的泛型方法next却是有问题的,为什么? 其中

2023-11-25 LeetCode每日一题(二叉树中的伪回文路径)

2023-11-25每日一题 一、题目编号 1457.二叉树中的伪回文路径二、题目链接 点击跳转到题目位置 三、题目描述 给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中…

R语言期末复习一

创建一个长度为7的字符向量&#xff0c;元素为"A", "B", "C", "D", "E", "F", "G"&#xff0c;并命名为vec1。 创建一个因子&#xff0c;包含6个水果&#xff1a;"apple", "banana"…

leetcode做题笔记1457. 二叉树中的伪回文路径

给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中&#xff0c;存在一个回文序列。 请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。 示例 1&#xff1a; 输…

YOLO目标检测——二维码检测数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;二维码识别、追踪与管理系统数据集说明&#xff1a;二维码检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标注框质量高&#xff0c;含voc(xml)、coco(json)和yolo…

前端review

关于实时预览vs code中的颜色代码需要安装的插件&#xff0c;包括html文件格式中的颜色代码安装Flutter Color插件 VSCode 前端常用插件集合 1.Auto Close Tag自动闭合HTML/XML标签 2.Auto Rename Tag自动完成另一侧标签的同步修改 3.Beautify格式化代码&#xff0c;值得注…