shiro安全控制目录
跨域问题系列文章
1. 同源策略与CORS(跨域请求的起源)
2. SpringBoot2.x整合CORS解决跨域问题(两种方案)
3. 跨域预检请求进行权限认证(复杂跨域请求的处理)
4. Filter返回失败如何使用CORS配置(SendError和setStatus的区别)
shiro分为有状态认证和无状态认证,但是均会在Shiro Filter完成权限控制(认证和授权)。[详情查看附录1]
而shiroFilter
并不能使用@Response注解返回错误信息(因为是在拦截器中直接返回)。故需要使用Response
对象方法,去返回用户指定的错误信息。
一般会有两种方式:
- Response.sendError() ---简单理解设置:错误页面
- 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串。
在SpringBoot2.x整合CORS解决跨域问题中有两种方法可以去配置CORS,实现跨域资源访问。
addCorsMappings
跨域配置,在Shiro Filter
中认证失败。SpringMVC将请求doDispatch
到错误页面,会使用addCorsMappings配置来完成CORS资源访问。CorsFilter
跨域配置,若在shiro Filter
之后,那么在shiro Filter
执行失败后,是不会执行后续的Filter配置的。
故:若配置addCorsMappings进行跨域资源访问,需要搭配使用sendError()方法。
2.2 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映射。故响应头并未增加跨域设置。)
若是使用setStaus返回认知失败,那么需要使用CorsFilter的进行CORS配置。
附录
有状态认证:
虽然HTTP是无状态协议,但是服务器可以通过cookie-session
机制来保存用户的状态。
- 用户在
SecurityManager
的Realm
组件完成认证和授权。 - 用户的
principal
(身份信息)和authenticated
(授权状态)会通过subjectDAO
组件保存到session中。 - 用户再次登录时,在
shiroFilter
中,可通过cookie查找session信息。从而组装出subject对象,保存到LocalContext
中,而后在线程中便可随时操纵subject
对象。 - 请求会执行
shiro Filter
,会进行认证和权限过滤。
(user认证器和authc认证器的区别便是在此。authc
会在isAccessAllowed
方法中校验authenticated
是否认证,而user
只会校验是否存在principal
对象。)
无状态认证:
无状态认证,是依靠JWT生成的Token来进行权限控制的。Token中不会保存用户权限和机密信息,这就用户每次请求均要在Shiro Filter
中调用Realm
组件来完成授权和认证(更重要的是生成subject对象,并存入LocalContext中,以便后续可以在线程中获取对象)。