如何在shiro发生UnauthorizedException与UnauthenticatedException等异常时返回json而不是跳转到错误页面

        当客户端的用户要请求一个需要该用户所不具有的role的接口时,往往会抛出未授权异常UnauthorizedException。

默认处理改异常的方式是在springmvc.xml中配置

org.springframework.web.servlet.handler.SimpleMappingExceptionResolver

如下:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!-- 错误异常转发jsp页面 -->
                <prop key="org.apache.shiro.authz.UnauthorizedException">/unauthorized.jsp</prop>
                <prop key="org.apache.shiro.authz.UnauthenticatedException">/unauthenticated.jsp</prop>
            </props>
        </property>
    </bean>

如果遇到这种情况不需要跳转别的页面而只需要返回一个结果给客户端,则需要自定义此处的

SimpleMappingExceptionResolver
主要是覆盖
doResolveException
 
@Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
                                              Object handler, Exception ex) {
        try {
            // Expose ModelAndView for chosen error view.
            BaseResult result = new BaseResult();
            if (ex instanceof UnauthorizedException) {
                result.setMsg(RespMSG.MSG_UNAUTHORIZED );
                result.setStatus(RespMSG.STATUS_UNAUTHORIZED);
            } else if (ex instanceof UnauthenticatedException) {
                result.setMsg(RespMSG.MSG_UNAUTHENTICATED );
                result.setStatus(RespMSG.STATUS_UNAUTHENTICATED);
            } else {
                result.setMsg(RespMSG.MSG_FAILLED );
                result.setStatus(RespMSG.STATUS_FAILLED);
            }
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            PrintWriter writer = response.getWriter();
            writer.write(new Gson().toJson(result));
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


<think>好的,我现在需要解决用户提出的问题:Shiro在Token失效前端显示302重定向,而Postman测试接口却返回403,应该如何解决。首先,我需要理解问题的背景和可能的原因。 用户提到前端显示302,而Postman返回403。这两个状态码的区别很明显:302是重定向,通常Web应用中的页面跳转有关,比如未登录跳转到登录页面;而403表示禁止访问,服务器理解请求但拒绝授权。这说明同一个接口在同客户端(浏览器和Postman)有同的行为,可能涉及Shiro的配置、过滤链设置或前后端交互方式的同。 首先,我应该回忆Shiro的工作原理。Shiro通过Filter链来处理请求,同的Filter负责同的功能,比如认证、授权、注销等。当Token失效Shiro会根据配置决定如何处理未认证或未授权的请求。 接下来,分析可能的原因: 1. **前后端请求的差异**:浏览器发起的请求可能带有Cookie或Session信息,而Postman可能使用Token直接在Header中,导致Shiro处理方式同。例如,浏览器请求可能被重定向到登录页,而Postman因为含Session信息,直接返回403。 2. **Shiro的Filter配置**:Shiro的Filter链中,对于未认证请求的处理可能配置了重定向(如authc Filter),而API请求预期返回JSON而非重定向。如果Filter链针对同URL路径配置同,可能导致前后端表现一致。 3. **CORS或跨域问题**:前端跨域请求,若未正确处理,可能导致重定向响应被浏览器拦截,但Postman受影响。但这可能不是直接原因,因为403和302的差异更可能认证处理有关。 4. **响应内容类型处理**:Shiro可能根据请求的Accept头返回同响应。浏览器通常请求HTML,导致重定向到HTML登录页(302),而Postman可能设置Accept为application/json,触发同的处理逻辑。 接下来,思考解决方案: - **统一认证处理逻辑**:确保所有API请求都使用Token认证,并配置Shiro在未认证返回JSON响应而非重定向。这可能需要自定义Filter或修改Shiro配置。 - **检查Shiro Filter链配置**:确保API路径对应的Filter(如anon, authc, jwt等)正确设置。例如,对API路径使用Stateless的Filter,避免使用基于Session的authc Filter。 - **自定义未认证/未授权处理**:通过实现Shiro的AuthenticationFilter和AuthorizationFilter,覆盖默认的重定向行为,返回合适的HTTP状态码和JSON响应。 - **前后端分离架构调整**:确保前端在Token失效处理401/403状态码,而不是跟随302重定向。可能需要前端拦截响应,检查状态码并进行跳转。 可能的步骤: 1. **确认API路径的Filter配置**:检查shiroFilterFactoryBean的filterChainDefinitions,确保API路径使用正确的Filter,如jwtFilter或stateless authc。 2. **禁用Session创建**:对于API请求,配置Shiro创建Session,使用无状态认证。可以通过设置Shiro的SessionManager为DisabledSessionManager实现。 3. **自定义未认证处理**:创建自定义Filter继承AccessControlFilter,重写onAccessDenied方法,返回JSON响应和403状态码。 4. **处理CORS和响应头**:确保后端正确设置CORS头,避免浏览器因跨域问题导致的重定向处理异常。 具体实施可能包括代码示例,比如配置自定义Filter,调整Shiro的Filter链,或者修改响应生成方式。同需要测试验证,确保前后端行为一致,正确返回403而非302。</think>### 问题分析 出现前端302重定向而Postman返回403的根本原因在于**Shiro同客户端的请求处理策略同**: 1. **浏览器请求**:默认会被Shiro的`FormAuthenticationFilter`拦截,触发重定向到登录页(302状态码) 2. **Postman请求**:缺少Session/Cookie等浏览器特性,Shiro直接返回403禁止访问 ### 解决方案(分步骤) --- #### 一、统一认证响应格式(解决302问题) 需让Shiro对所有未认证请求返回JSON格式响应而非重定向,修改Shiro配置: ```java // 自定义未认证过滤器 public class CustomAuthenticationFilter extends FormAuthenticationFilter { @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { if (isLoginRequest(request, response)) { return true; } else { // 返回JSON响应 HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); // 401 httpResponse.setContentType("application/json;charset=utf-8"); httpResponse.getWriter().write("{\"code\":401, \"msg\":\"未认证\"}"); return false; } } } ``` --- #### 二、配置Shiro过滤器链 在`ShiroConfig`中指定API路径使用自定义过滤器: ```java @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); // 添加自定义过滤器 Map<String, Filter> filters = new LinkedHashMap<>(); filters.put("authc", new CustomAuthenticationFilter()); factoryBean.setFilters(filters); // 设置过滤规则 Map<String, String> filterChainMap = new LinkedHashMap<>(); filterChainMap.put("/api/**", "authc"); // API路径使用自定义认证 filterChainMap.put("/login", "anon"); factoryBean.setFilterChainDefinitionMap(filterChainMap); return factoryBean; } ``` --- #### 三、禁用Session创建(针对API接口) 在`ShiroConfig`中配置无状态Session管理: ```java @Bean protected SessionStorageEvaluator sessionStorageEvaluator() { DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator(); evaluator.setSessionStorageEnabled(false); // 禁用Session存储 return evaluator; } ``` --- #### 四、前端适配处理 前端需要拦截401/403状态码并跳转登录页: ```javascript axios.interceptors.response.use(response => { return response; }, error => { if (error.response.status === 401 || error.response.status === 403) { router.push('/login'); // 跳转登录页 } return Promise.reject(error); }); ``` --- ### 验证测试 1. **Postman测试**:Token失效返回401状态码和JSON消息 2. **浏览器测试**:Ajax请求再触发页面重定向,控制台应看到401错误 ### 补充建议 1. 如果使用JWT,建议改用`JWTFilter`替代默认的`authc` 2. 检查跨域配置(CORS),确保浏览器能正确接收响应头 3. 监控浏览器Network选项卡,确认实际响应的状态码和内容类型 通过以上配置,即可实现前后端统一的认证失败响应处理,避免302403状态码一致的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值