Shiro:未登录时请求跳转问题
问题:前后端分离项目,在用Shiro做权限控制时,未登录状态发送的请求都会重定向,导致前端无法捕捉重定向后的消息。如何不重定向在原来的请求返回信息提示未登录,前端根据信息调到登录页?
首先,看一下Shiro是在哪里做的重定向。下面是Shiro的部分源码
package org.apache.shiro.web.filter.authc;
public class FormAuthenticationFilter extends AuthenticatingFilter {
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response)) {
if (isLoginSubmission(request, response)) {
if (log.isTraceEnabled()) {
log.trace("Login submission detected. Attempting to execute login.");
}
return executeLogin(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Login page view.");
}
//allow them to see the login page ;)
return true;
}
} else {
if (log.isTraceEnabled()) {
log.trace("Attempting to access a path which requires authentication. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
// 这里做的重定向
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
}
发现是FormAuthenticationFilter.onAccessDenied()中做的重定向。接下来就就可以着手解决问题了。
解决:
1. 继承FormAuthenticationFilter,重写onAccessDenied方法
/**
* 继承FormAuthenticationFilter,重写onAccessDenied方法
*/
public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {
private static final Logger log = LoggerFactory.getLogger(ShiroFormAuthenticationFilter.class); @Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (this.isLoginRequest(request, response)) {
if (this.isLoginSubmission(request, response)) {
if (log.isTraceEnabled()) {
log.trace("Login submission detected. Attempting to execute login.");
} return this.executeLogin(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Login page view.");
} return true;
}
} else {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse)response;
if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
resp.setStatus(HttpStatus.OK.value());
return true;
} else {
if (log.isTraceEnabled()) {
log.trace("Attempting to access a path which requires authentication. Forwarding to the Authentication url [{}]" ,this.getLoginUrl());
}
/**
* 在这里实现自己想返回的信息,其他地方和源码一样就可以了
*/
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setContentType("application/json; charset=utf-8");
resp.setCharacterEncoding("UTF-8");
DataResponse<?> result = DataResponse.failed(ExceptionCode.NO_AUTH);
PrintWriter out = resp.getWriter();
out.println(JsonUtils.objectToJson(result));
out.flush();
out.close();
return false;
}
}
} }
2. 在config中配置filter
@Configuration
public class ShiroConfig { private static final Logger log = LoggerFactory.getLogger(ShiroConfig.class);
private static Map<String, String> filterChainDefinitionMap = new LinkedHashMap(); @Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
shiroFilterFactory.setSecurityManager(securityManager); filterChainDefinitionMap.put("/sys/login","anon");
filterChainDefinitionMap.put("/captcha.jpg", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
filterChainDefinitionMap.put("/logout","anon");
filterChainDefinitionMap.put("/index","anon");
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/img/**","anon");
filterChainDefinitionMap.put("/fonts/**","anon");
filterChainDefinitionMap.put("/chosen/**","anon");
filterChainDefinitionMap.put("/static/**","anon");
filterChainDefinitionMap.put("/swagger-ui.html","anon");
filterChainDefinitionMap.put("/swagger-ui.html/**","anon");
filterChainDefinitionMap.put("/webjars/**","anon");
filterChainDefinitionMap.put("/layout/**","anon");
filterChainDefinitionMap.put("/swagger-resources/**","anon");
filterChainDefinitionMap.put("/v2/**","anon");
filterChainDefinitionMap.put("/**","authc");
LinkedHashMap<String, Filter> filtsMap = new LinkedHashMap<>();
// 这里使用自定义的filter
filtsMap.put("authc", new ShiroFormAuthenticationFilter());
shiroFilterFactory.setFilters(filtsMap);
shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactory;
}
}
OK,问题解决。
附注:
servlet的两种跳转方式:forward转发、redirect重定向
两者的区别:
1.地址栏
1)forward是服务器内部的跳转,服务器直接访问目标地址,客户端不知情,因此浏览器的网址不发生变化
2)redirect是服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求另一个地址,所以地址栏显示新的地址
2.数据共享
forward在整个跳转过程中用的是同一个request,forward会将request的信息带到被跳转的jsp或者是servlet中使用,所以数据是共享的。而redirect是新的request,所以数据不共享。
3.运用
1) forward一般用于用户登录的时候,根据角色转发到相应的模块
2) redirect一般用于用户注销登录时返回主页或者跳转到其他网站
4.效率
forward效率高,redirect效率低
5.本质
forward转发时服务器上的行为,而redirect重定向是客户端的行为
6.请求次数
forward 一次,redirect两次
Shiro:未登录时请求跳转问题的更多相关文章
- dede用户登录时,跳转到提示页时报404错误
做了一个项目,本地运行,用的是Apache服务器,一切正常. 可是当我把项目放到VPS中运行时,每当输入用户名登录时,调转到"成功登录,3秒钟后转向网站主页"的提示页面时,页面的顶 ...
- dotnetnuke 7.x登录时不跳到站点设置中的指定页
查源码发现登录按钮有参数,点击跳到登录页或者弹窗登录,真正登录后会根据传参的url反回.因为皮肤对像没有相应参数,所以只能去掉参数.我是用js去的,偷个懒吧.如下所示: <script type ...
- php实现:当未登录时转到登陆页面
判断session是否存在,不存在则跳转到登录页面session_start(); if ( !$_SESSION['xxx'] ) { header("Location: login ...
- 实用jstl实现未登录时不能绕过登录界面的效果
package com.filter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; ...
- 单点登录CAS使用记(五):cas-client不拦截静态资源以及无需登录的请求。
一.问题在哪? 在配置cas-client中,有这么一段配置: <filter> <filter-name>CAS Filter</filter-name> < ...
- 基于redis实现未登录购物车
springboot 工程 主要说明购物车流程(故将登录用户信息保存至session) 未登录时 将用户临时key 保存至cookie 有不足之处 请大佬指点 项目源码: https://github ...
- 自定义HttpModule,用于未登录用户,不弹出Windows认证窗口,而是跳转回SSO站点
2012年的一篇随笔记录,可以学习到如何自定义HttpModule,而具体里面针对需求开发的代码,可能未必能让大伙了解到什么,可快速扫描而过. using System; using System.W ...
- 前后端分离项目shiro的未登录和权限不足
在前后端分离的项目中.前端代码和后端代码几乎不在同一个目录下,甚至不是在一台服务器上:我这个项目部署在linux.同一台服务器,不同目录下:所有的页面跳转由前台路由,后台只是提供返回的数据: 干货↓ ...
- C#-WebForm-Session、Cookie-登录验证(未登录跳至登录界面)、隐藏地址栏传值
Post 传值(看不见的传值) Get 传值(看得见的传值) Session - 全局变量组 存放位置:服务端 作用:只要里面有内容,那么这个网站中所有的C#端都能访问到这个变量 -- object类 ...
随机推荐
- 从原理到场景 系统讲解 PHP 缓存技术
第1章课程介绍 此为PHP相关缓存技术的课堂,有哪些主流的缓存技术可以被使用? 第1章 课程介绍 1-1课程介绍1-2布置缓存的目的1-3合理使用缓存1-4哪些环节适合用缓存 第2章 文件类缓存 2- ...
- C++ 变量判定的螺旋法则
C++ 中一个标识符配合着各种修饰界定符,使得标识符的本意不那么直观一眼就能看出,甚至需要仔细分析,才能知道该标识符的具体你含义. 比如: void (*signal(int, void (*fp)( ...
- Ubuntu18.04 显卡驱动+Cuda安装踩坑记录 以及Ubuntu虚拟内存的添加
前几天买了张亮机卡,终于把主显卡成功直连到Unraid OS的虚拟机上了.然后就开始安装ubuntu系统开始配置环境,遇到了不少坑,特此记录. gcc版本问题 在安装显卡驱动的时候,不要修改gcc版本 ...
- Android Studio [水平布局LinearLayout]
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...
- wait()与notify()
一,前言 简单画了一下线程的流程图,只是一个大概.如图所示,线程有多种状态,那么不同状态之间是如何切换的,下面主要总结关于wait()和notify()的使用. 二,wait() wait ...
- 【ADO.NET基础-数据加密】第一篇(加密解密篇)
可以采用下面的函数实现密码的加密 public static string EncryptString(string str) { //密文 string key = "www"; ...
- 一条SQL查询语句是如何执行的?
本篇文章将通过一条 SQL 的执行过程来介绍 MySQL 的基础架构. 首先有一个 user_info 表,表里有一个 id 字段,执行下面这条查询语句: select * from user_inf ...
- AVR单片机教程——数字IO寄存器
前两篇教程中我们学习了LED.按键.开关的基本原理,数字输入输出的使用以及两者之间的关系.我们用到了 pin_mode . pin_read 和 pin_write 这三个函数,实际上它们离最底层(至 ...
- 你不知道的 IDEA Debug调试小技巧
一.多线程调试断点 Intellij IDEA 的debug断点调试是有一个模式的选择的,就像下面这张图,平时我们都使用的是默认的 ALL(在Eclipse中默认是线程模式) ,这种模式我们只能将一个 ...
- Patorjk
http://www.patorjk.com/software/taag/#p=display&f=Henry%203D&t=CFR%20SpringBoot%20