SpringBoot整合Shiro 涉及跨域和@Cacheable缓存/@Transactional事务注解失效问题(五)
1. 跨域(多出现在前后端分离项目中)
(1) 跨域介绍可参考:跨域(CORS)
(2) SpringBoot中解决跨域方式有:
A. 使用@CrossOrigin注解;
B. 实现Filter类,重写doFilter方法
package com.ruhuanxingyun.config; import cn.hutool.core.util.StrUtil; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter(filterName = "CorsFilter") public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, OPTIONS, PUT, DELETE"); // 浏览器低版本不支持* response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Authorization, Content-Type"); // 跨域时会发送option请求 if (StrUtil.equals(request.getMethod(), HttpMethod.OPTIONS.name())) { response.setStatus(HttpStatus.OK.value()); } else { chain.doFilter(req, res); } } }
但是SpringBoot整合Shiro后,注解跨域就失效了,原因:shiro的过滤器会在注解跨域处理之前执行,这就导致未允许跨域的请求先到达shiro过滤器,这样就会出现跨域错误
(3) 在shiro中实现跨域,有以下两种方式
A. 继承BasicHttpAuthenticationFilter类,重写preHandle方法
/** * 提供跨域支持 * * @param request 请求对象 * @param response 响应对象 * @return 允许跨域 * @throws Exception 异常信息 */@Overrideprotected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, OPTIONS, PUT, DELETE"); // 浏览器低版本不支持* httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Authorization, Content-Type"); // 跨域时会发送option请求 if (StrUtil.equals(httpServletRequest.getMethod(), HttpMethod.OPTIONS.name())) { httpServletResponse.setStatus(HttpStatus.OK.value()); return false; } return super.preHandle(request, response);}
B. 实现Filter类,重写doFilter方法(如上)
2. @Cacheable缓存/@Transactional事务注解失效
(1) 问题体现:缓存和事务注解失效的类,都是在shiro框架中(loginRealm、jwtRealm)使用过@Autowired注入的类,而其他service事务都可以正常使用
(2) 产生原因:在shiro中为了引入权限注解,配置了defaultAdvisorAutoProxyCreator和authorizationAttributeSourceAdvisor类,他们是通过AOP方式对@RequiredPermission类进行增强,生成对应的代理类对象,由于shiroFilterFactoryBean实现了factoryBean接口,所以会被提前初始化,所以引发所有相关的bean提前初始化,导致他们没有被事务AOP包裹着,从而引发事务无效的问题
(3) 解决方式
A. @Autowired + @Lazy注解 延时加载注入
B. 在Realm中直接使用mapper,而不是service
C. ApplicationContextRegister.getBean()方法,手动注入bean