前后端分离项目的跨域问题解决

168 阅读2分钟

方案一:代理服务器

使用代理服务器可以在开发环境解决跨域,但一旦打包放到springboot里,开发环境配置的代理服务器就不管用了。

所以axiso请求的base_url不能写死,需要动态获取。根据浏览器中输入的地址来获取base_url。

let SQIMS_request =axios.create({

    baseURL: window.location.protocol+'//'+window.location.host,

})

若分离部署,如何设置本地的代理服务器?每次打包时根据项目修改项目上的ip地址?(暂未解决)

如何设置nginx的代理服务器?(暂未解决)

方案二:后端配置全局跨域

前端设置

let SQIMS_request =axios.create({

    baseURL: window.location.protocol+'//'+window.location.host,

    withCredentials:true  //开启凭证,axios在请求的时候会携带 JSESSIONID,用以shiro验证。如无需验证,则不设置此项。配合后端全局跨域设置,也可解决跨域问题

})

后端设置

springboot+shiro 采用如下设置并将过滤器执行顺序调到最高,一定要高于shiroFilter

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsFilterConfig {
    /**
     * @Description :跨域访问过滤器,设置执行顺序
     * @Date 19:55 2021/6/15 0015
     * @return org.springframework.boot.web.servlet.FilterRegistrationBean<org.springframework.web.filter.CorsFilter>
     **/
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilterRegistrationBean(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        //设置执行顺序,数字越小越先执行
        bean.setOrder(0);
        return bean;
    }

}

若采用

@Configuration
public class GlobalCorsConfig {

    /**
     * 允许跨域调用的过滤器
     */
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        //允许所有域名进行跨域调用
        config.addAllowedOrigin("*");
        //允许跨越发送cookie
        config.setAllowCredentials(true);
        //放行全部原始头信息
        config.addAllowedHeader("*");
        //允许所有请求方法跨域调用
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

这种方式,则会出现shiroFilter早于该过滤器执行,导致需要经过shiroFilter的跨域失败

若选用这种情况,需要在shiroFilter中,也设置跨域配置


import com.sqims.sqiot.commons.entity.FangResponse;
import com.sqims.sqiot.commons.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
public class ShiroFilter extends FormAuthenticationFilter {

    /**
     * 在访问controller前判断是否登录,返回json,不进行重定向。
     *
     * @param request
     * @param response
     * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied (ServletRequest request, ServletResponse response) throws IOException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        //这里是个坑,如果不设置的接受的访问源,那么前端都会报跨域错误,因为这里还没到corsConfig里面
        setHeader(httpServletRequest, httpServletResponse);
        httpServletResponse.setCharacterEncoding("UTF-8");
//        这里不能抛给全局异常处理,因为抛给全局异常的话,shiro会返回一个html
        log.error("未登录或登录已失效,请重新登录!");
        FangResponse fangResponse = FangResponse.unAuthentication("未登录或登录已失效,请重新登录!");
        httpServletResponse.getWriter().write(JsonUtil.toJson(fangResponse));
        return false;
    }

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        setHeader(httpRequest, httpResponse);
        //无条件放行OPTIONS
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }
        return super.preHandle(request, response);
    }

    /**
     * 为response设置header,实现跨域
     */
    private void setHeader(HttpServletRequest request, HttpServletResponse response) {
        log.info("ShiroFilter");
        response.setHeader("Access-Control-Allow-Origin",request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods","*");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Max-Age", "3600");
    }

方案三:采用JWT方式代替session方式