shiro的前后端分离模式

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();}@Overrideprotected 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*/@Beanpublic 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 {@Overridepublic 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*/@Overrideprotected 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数据*/@Overrideprotected 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());

@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);// 添加自定义filtershiroFilter.getFilters().put("authc", new ShiroUserFilter());// 认证失败(用户没登录时)会重定向到这个loginUrlshiroFilter.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,”/**“必须写在最后,如果不写该项,默认放行所有没配置的urimap.put("/**","authc");// 将该map设置进shiroFiltershiroFilter.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.hqwc.cn/news/219122.html

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

相关文章

什么是零拷贝 、零拷贝优化方案 - 真正的零拷贝,哪些地方会用到零拷贝技术

文章目录 什么是零拷贝3、零拷贝优化方案 - 真正的零拷贝哪些地方会用到零拷贝技术 现在来谈谈零拷贝&#xff0c;以及在开发中哪些地方使用到零拷贝。 开干… 什么是零拷贝 零拷贝指的是&#xff0c;从一个存储区域到另一个存储区域的copy任务无需CPU参与就可完成。零拷贝的底…

11-25碎片小知识

一.strlen补充 strlen函数返回值是size_t&#xff0c;即无符号整型&#xff0c; size_t有头文件&#xff0c;是stdio.h 由于strlen函数返回值是无符号整型&#xff0c;所以下面代码要注意 -3会被转换成无符号的 实现my_strlen 法一&#xff1a;指针减指针 #define _CRT_S…

CleanMyMac X好不好用?有哪些优势

CleanMyMac X2024正是这一愿景和使命的体现。 作为一个团队&#xff0c;我们致力于采用令人过目不忘的设计来打造我们引以为豪的产品。 这是 UX/UI 设计已经成为我们核心价值的原因之一。 这也是我们不断完善它&#xff0c;从而为我们的用户创造最神奇体验的动力。 CleanMyMac …

论文导读 | 10月专题内容精选:人的预测

编者按 本次论文导读&#xff0c;编者选择了10月份OR和MS上与"人的预测"有关的三篇文章&#xff0c;分别涉及群体智慧的提取&#xff0c;个体序列预测的评估&#xff0c;以及决策者对风险的扭曲感知在分布式鲁棒优化中的应用。其中&#xff0c;从基于"生成式可能…

Java、PHP、C语言经典项目源码合集推荐(一)

&#xff08;一&#xff09;.Java智慧校园系统源码、 智慧学校源码、 智慧校园平台源码、智慧校园电子班牌系统源码、中小学智慧校园系统源码、 原生微信小程序端源码、电子班牌系统源码 项目技术栈 1、使用springboot框架Javavue2 2、数据库MySQL5.7 3、移动端小程序使用小程…

基于springboot实现智慧党建系统项目【项目源码】

基于springboot实现智慧党建系统演示 Java技术 Java是由Sun公司推出的一门跨平台的面向对象的程序设计语言。因为Java 技术具有卓越的通用性、高效性、健壮的安全性和平台移植性的特点&#xff0c;而且Java是开源的&#xff0c;拥有全世界最大的开发者专业社群&#xff0c;所以…

GIT版本控制和常用命令使用介绍

GIT版本控制和常用命令使用介绍 1. 版本控制1.1 历史背景1.2 什么是版本控制1.3 常见版本控制工具1.4 版本控制的分类 2 Git介绍2.1 Git 工作流程2.2 基本概念2.3 文件的四种状态2.4 忽略文件2.5 Git命令2.5.1 查看本地git配置命令2.5.2 远程库信息查看命令2.5.3 分支交互命令2…

适用于 Mac 和 Windows 的顶级U 盘数据恢复软件

由于意外删除或设备故障而丢失 USB 驱动器中的数据始终是一件令人压力很大的事情&#xff0c;检索该信息的最佳选择是使用优质数据恢复软件。为了让事情变得更容易&#xff0c;我们已经为您完成了所有研究并测试了工具&#xff0c;并且我们列出了最好的 USB 记忆棒恢复软件&…

零基础学Linux内核:1、Linux源码组织架构

文章目录 前言一、Linux内核的特征二、Linux操作系统结构1.Linux在系统中的位置2.Linux内核的主要子系统3、Linux系统主要数据结构 三、linux内核源码组织1、下载Linux源码2、Linux版本号3、linux源码架构目录讲解 前言 这里将是我们从零开始学习Linux的第一节&#xff0c;这节…

大量索引场景下 Easysearch 和 Elasticsearch 的吞吐量差异

最近有客户在使用 Elasticsearch 搜索服务时发现集群有掉节点&#xff0c;并且有 master 收集节点信息超时的日志&#xff0c;节点的负载也很高&#xff0c;不只是 data 节点&#xff0c;master 和协调节点的 cpu 使用率都很高&#xff0c;看现象集群似乎遇到了性能瓶颈。 查看…

3、MSF使用

文章目录 一、利用ms17-010漏洞对靶机执行溢出攻击二、后渗透模块meterpreter的使用 一、利用ms17-010漏洞对靶机执行溢出攻击 分别输入以下命令&#xff0c;使用ms17_010_eternalblue模块对目标机的ms17-010漏洞进行利用&#xff1a; use exploit/windows/smb/ms17_010_eter…

iar如何全擦芯片内存

Project ->Download -> Erase memory