Shoir导致axios出现跨域导致报错的解决方法 ShiroAxios跨域
  • 创建时间:2024-08-30 / 最新修改时间:2024-08-30 11:47:09
  • 183
  • 0
转载请注明本文出处:http://limpire.cn/artifact/programming/java/235.html


Shoir导致Axios出现跨域导致报错的解决方法

Shoir 是需要cookies支持的,所以Axios一般都会:

//允许携带Cookies,AXIOS 会自动管理Cookies
const axiosInstance = axios.create({
    withCredentials: true, 
    baseURL: baseUrl
})

withCredentials: true 后,服务端需要返回指定详细的 http 头 access-control-allow-origin: http://xxxxxx .

换句话说,Controller中不能再这么写 @CrossOrigin("*") .

那么我们可以 @CrossOrigin("http://xxxxxx")

设置后我发现2个现象:

  • 没有被Shoir拦截的url可以正常访问
  • 被Shoir拦截的的url依旧提示 Cross 的错误。

原因和解决方法

假设这个地址 用 "authc" 过滤器过滤

filterMap.put("/home/**","authc");

而authc默认的过滤器没有对Cross进行处理,所以导致前端出现错误.

那么我可以重写这个过滤器,并重新注册.

继承FormAuthenticationFilter 重写两个方法

public class CorsAuthenticationFilter extends FormAuthenticationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) {
        boolean b = super.isAccessAllowed(servletRequest, servletResponse, mappedValue);
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        AccessLog.writeLog(request);//打印日志
        if (b || request.getMethod().equalsIgnoreCase("OPTIONS")){
            response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
            response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, If-Modified-Since, x-token");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
           // response.setHeader("Access-Control-Max-Age", "7600");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            return true;
        }

        return b;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String origin = request.getHeader("Origin");
        response.setHeader("Access-Control-Allow-Origin", origin);
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, If-Modified-Since, x-token");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        // response.setHeader("Access-Control-Max-Age", "7600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        //跳转到首页
        //response.sendRedirect(origin);
        response.setStatus(200);
        PrintWriter writer = response.getWriter();
        writer.print("no login");
        writer.close();
        return false;
       // return super.onAccessDenied(request, response);
    }
}

创建 ShiroFilterFactoryBean 时注册过滤器,覆盖默认的authc过滤器

 @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        //新建拦截过滤器的工厂类(生产不同的拦截器,拦截请求)
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器(处理请求的认证授权)
        filterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String,String> filterMap = new LinkedHashMap<String,String>();
        //自定义过滤器,用于处理跨域问题
        CorsAuthenticationFilter corsAuthenticationFilter = new CorsAuthenticationFilter();
        Map<String, Filter> filters = filterFactoryBean.getFilters();
        filters.put("authc",corsAuthenticationFilter); ///!!!!!!!!******这里是重点******* 覆盖原版 authc 过滤器
        filterMap.put("/user/login","anon");//anon 设置为公共资源
        filterMap.put("/user/register","anon");//anon 设置为公共资源
        filterMap.put("/home/**","authc");
        filterFactoryBean.setFilterChainDefinitionMap(filterMap);

        //设置其他配置信息
        //默认的认证登陆页面(认证失败时自动跳转)
        filterFactoryBean.setLoginUrl("/");
        //设置未授权提示页面
        filterFactoryBean.setUnauthorizedUrl("/");
        return filterFactoryBean;
    }

现在 Shoir 的跨域问题搞定了,但是没有被Shoir过滤的URL每一次Contoller都要添加详细 @CrossOrigin("http://xxxxxxxx") 注解,很麻烦。那么我们可以这样做,实现 servelt 的 Filter .


/**
 * 解决跨域问题,用于非拦截的url
 * @date 2024/8/28 17:07
 */
@Component
public class CorsFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
      
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, If-Modified-Since, x-token");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
       // response.setHeader("Access-Control-Max-Age", "7600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        AccessLog.writeLog(request);//打印日志

        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

总结:

使用后端验证框架Shoir和前端Axios 做项目时需要处理2个问题

  1. 需要解决当携带cookies时,服务端返回的头必须有详细的access-control-allow-origin ,不能是*号。
  2. Shoir 默认的过滤器需要重写,主要是添加返回头的access-control-allow-origin 信息逻辑。

请说:

昵称 Email

评论:

吃不起海鲜就买海鲜味的酱油

回车换行很爽,代表着一个段落或者一个小结的完成,打印出来之后就是一个可触摸的成品.围着这种意境,一起来技术性打酱油.

文章列表
分类目录
友情链接