shiro(16)-Filter返回失败如何增加CORS配置(SendError和setStatus的区别)

shiro安全控制目录

跨域问题系列文章

1. 同源策略与CORS(跨域请求的起源)
2. SpringBoot2.x整合CORS解决跨域问题(两种方案)
3. 跨域预检请求进行权限认证(复杂跨域请求的处理)
4. Filter返回失败如何使用CORS配置(SendError和setStatus的区别)

shiro分为有状态认证和无状态认证,但是均会在Shiro Filter完成权限控制(认证和授权)。[详情查看附录1]

shiroFilter并不能使用@Response注解返回错误信息(因为是在拦截器中直接返回)。故需要使用Response对象方法,去返回用户指定的错误信息。
一般会有两种方式:

  1. Response.sendError() ---简单理解设置:错误页面
  2. Response.setStatus() ---简单理解设置:错误码

2.1. Response.sendError()

HttpServletResponse.sendError()将指定的状态和错误码响应发送到客户端。服务器默认创建响应,将内容设置为text/html,而cookie和其他header保持不变。

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest req = (HttpServletRequest) request;
        //从request的header中得到token和host
        try {
            //转换为token
            JwtToken jwtToken = new JwtToken(token, host);
            //委托给Realm进行登录
            getSubject(request, response).login(jwtToken);
            return true;
        } catch (AccountException e) {
            logger.error("该账号已在其他设备登陆,账号异常退出:{}", e.getMessage());
            WebUtils.toHttp(response).sendError(Integer.parseInt(ResponseCodeEnum.STATUS_998.getRespCode()), ResponseCodeEnum.STATUS_998.getRespDesc());
        } catch (AuthenticationException e) {//401 认证失败
            logger.error("身份认证失败:{}", e.getMessage());
            WebUtils.toHttp(response).sendError(Integer.parseInt(ResponseCodeEnum.STATUS_401.getRespCode()), ResponseCodeEnum.STATUS_401.getRespDesc());
        } catch (Exception e) {
            logger.error("身份认证失败:", e);
            WebUtils.toHttp(response).sendError(Integer.parseInt(ResponseCodeEnum.STATUS_401.getRespCode()), ResponseCodeEnum.STATUS_401.getRespDesc());
        }
        return false;
    }
}

若是ajax访问,服务器响应类型为application/json类型,可以通过responseText字段获取到JSON串。

模拟跨域请求.png
  1. sendError()执行流程图

在SpringBoot2.x整合CORS解决跨域问题中有两种方法可以去配置CORS,实现跨域资源访问。

  1. addCorsMappings跨域配置,在Shiro Filter中认证失败。SpringMVC将请求doDispatch到错误页面,会使用addCorsMappings配置来完成CORS资源访问。

  2. CorsFilter跨域配置,若在shiro Filter之后,那么在shiro Filter执行失败后,是不会执行后续的Filter配置的。

sendError()流程.png

故:若配置addCorsMappings进行跨域资源访问,需要搭配使用sendError()方法。

2.2 Response.setStatus()

在Servlet2.3规范中,2.1和2.2这两种处理方式相同。如果使用任一方法设置HTTP状态响应,并且为该状态配置内容,则会转发到错误页提供响应。但是在Servlet2.4中,sendError仍是转发到已配置的错误页面,但是setStatus会自己提供响应。
节选自——response.sendError()与response.setStatus()区别

            WebUtils.toHttp(response).setStatus(401);
            WebUtils.toHttp(response).
//                    getWriter().
//                    write(JSON.toJSONString(responseVo));
                    getOutputStream().
                    write(JSON.toJSONString(responseVo).getBytes(StandardCharsets.UTF_8));

若使用setStatus()进行响应时,是不会执行addCorsMappings的配置,那么还是会遇到跨域问题。
(原因是当Shiro Filter使用setStatus返回,不会执行Mapping映射。故响应头并未增加跨域设置。)

response.setStatus()流程.png

若是使用setStaus返回认知失败,那么需要使用CorsFilter的进行CORS配置。

附录

  1. Shiro有状态认证和无状态认证的流程

有状态认证:

虽然HTTP是无状态协议,但是服务器可以通过cookie-session机制来保存用户的状态。

  1. 用户在SecurityManagerRealm组件完成认证和授权。
  2. 用户的principal(身份信息)和authenticated(授权状态)会通过subjectDAO组件保存到session中。
  3. 用户再次登录时,在shiroFilter中,可通过cookie查找session信息。从而组装出subject对象,保存到LocalContext中,而后在线程中便可随时操纵subject对象。
  4. 请求会执行shiro Filter,会进行认证和权限过滤
    (user认证器和authc认证器的区别便是在此。authc会在isAccessAllowed方法中校验authenticated是否认证,而user只会校验是否存在principal对象。)

无状态认证:

无状态认证,是依靠JWT生成的Token来进行权限控制的。Token中不会保存用户权限和机密信息,这就用户每次请求均要在Shiro Filter中调用Realm组件来完成授权和认证(更重要的是生成subject对象,并存入LocalContext中,以便后续可以在线程中获取对象)。

推荐阅读

Interface HttpServletResponse官方API文档

你可能感兴趣的:(shiro(16)-Filter返回失败如何增加CORS配置(SendError和setStatus的区别))