还在问跨域?本文记录js跨域的多种实现实例
发布日期:2021-05-20 17:28:24 浏览次数:20 分类:博客文章

本文共 8113 字,大约阅读时间需要 27 分钟。

  前言

  众所周知,受浏览器的影响,产生了跨域问题,那么我们应该如何实现跨域呢?本文记录几种跨域的简单实现

  前期准备

  为了方便测试,我们启动两个服务,10086(就是在这篇博客自动生成的项目,请戳:)服务提供者,10087服务消费者,消费者有一个页面test.html跟一个后端controller

跨域测试

直接ajax请求10086服务的接口

@RequestMapping("page/test")    public ModelAndView pageLogin() {        return new ModelAndView("test.html");    }

  我们先启动10086服务

  浏览器访问接口,正常获取数据

  启动10087服务

  访问test.html页面,直接ajax请求10086服务的接口,直接报错

 

   几种跨域方式

  1、后端安全跨域

  前端发起请求后端,后端使用httpclient(使用方法参考:)或者feign(使用方法参考:)安全跨域

  我们给10087服务消费者新增两个controller接口,用于后台调用10086跟响应数据,并修改test.html

  httpclient

跨域测试

后端安全跨域(httpclient)

@RequestMapping("/test/httpclient")    public Object httpclient(String id) {        String result = null;        try {            //创建httpclient对象 (这里设置成全局变量,相对于同一个请求session、cookie会跟着携带过去)            CloseableHttpClient httpClient = HttpClients.createDefault();            //创建get方式请求对象            HttpGet httpGet = new HttpGet("http://localhost:10086/tbUser/get/"+id);            httpGet.addHeader("Content-type", "application/json");            //包装一下            httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");            httpGet.addHeader("Connection", "keep-alive");            //通过请求对象获取响应对象            CloseableHttpResponse response = httpClient.execute(httpGet);            //获取结果实体            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {                result = EntityUtils.toString(response.getEntity(), "GBK");            }            //释放链接            response.close();        }        //这里还可以捕获超时异常,重新连接抓取        catch (Exception e) {            result = null;            e.printStackTrace();        }        return result;    }

 

  feign

  我们先在10087服务消费者修改test.html,新增controller接口,maven引入feign,创建TestFeign,值得注意的是消费者的返回值必须与提供者的返回值一致,参数对象也要一致

跨域测试

后端安全跨域(feign)

@FeignClient(name = "http://localhost:10086", path = "/tbUser/")public interface TestFeign {    @RequestMapping(value = "get/{id}")    Result
get(@PathVariable("id") Integer id);}
@Autowired    private TestFeign testFeign;    @RequestMapping("/test/feign")    public Object feign(Integer id){        return testFeign.get(id);    }

  直接使用url会报这个错,因为我们没有注册这两个服务,eureka也没起...,所以想使用feign调用,两个服务都需要在eureka上注册,负载均衡器才能找到它

  我们将两个服务注册到eureka上(参考springcloud系列博客之),将@FeignClient(name = "http://localhost:10086", path = "/tbUser/")的name的值改成10086服务提供者的 spring.application.name的值即可

 

  2、jsonp

   由于同源策略,浏览器禁止向不同源的服务器发起请求,但是 HTML 的<script> 元素是一个例外,我们可以利用标签的src发起请求,服务提供者的后端响应消费者期望的数据格式(必须是符合js对象的格式,否则会在回调函数解析的时候报错)

  我们先重写10086服务提供者的get接口,使其返回值符合消费者期待的格式

@RestController@RequestMapping("/tbUser/")public class TbUserController extends CommonController
{ @Autowired private TbUserService tbUserService; @RequestMapping("get") public Object get(Integer id, String callback) { Result
result = super.get(id); TbUserVo tbUserVo = result.getData(); StringBuffer re = new StringBuffer(); re.append("{"); re.append("'flag':'" + result.isFlag() + "',"); re.append("'msg':'" + result.getMsg() + "',"); re.append("'data':{"); re.append("'id':'" + tbUserVo.getId() + "',"); re.append("'username':'" + tbUserVo.getUsername() + "',"); re.append("'password':'" + tbUserVo.getPassword() + "',"); re.append("'created':'" + tbUserVo.getCreated() + "',"); re.append("'descriptionId':'" + tbUserVo.getDescriptionId()+"'"); re.append("}}"); return callback + "(" + re.toString() + ")"; }}

  修改10087服务消费者的test.html

跨域测试

jsonp

  

  3、cors

  详情介绍可以看这里: ,代码实现过程参考: 

  参考大佬博客,我们在10086服务提供者新建一个CorsFilter过滤器,

@Component@ServletComponentScan@WebFilter(filterName = "corsFilter", //过滤器名称        urlPatterns = "/tbUser/*",//拦截路径        initParams = {@WebInitParam(name = "allowOrigin", value = "http://localhost:10087"),//允许来源                @WebInitParam(name = "allowMethods", value = "GET,POST,PUT,DELETE,OPTIONS"),//允许请求方法                @WebInitParam(name = "allowCredentials", value = "true"),                @WebInitParam(name = "allowHeaders", value = "Content-Type,X-Token")})public class CorsFilter implements Filter {    private String allowOrigin;    private String allowMethods;    private String allowCredentials;    private String allowHeaders;    private String exposeHeaders;    @Override    public void init(FilterConfig filterConfig) throws ServletException {        allowOrigin = filterConfig.getInitParameter("allowOrigin");        allowMethods = filterConfig.getInitParameter("allowMethods");        allowCredentials = filterConfig.getInitParameter("allowCredentials");        allowHeaders = filterConfig.getInitParameter("allowHeaders");        exposeHeaders = filterConfig.getInitParameter("exposeHeaders");    }    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) servletRequest;        HttpServletResponse response = (HttpServletResponse) servletResponse;        if (!StringUtils.isEmpty(allowOrigin)) {            if(allowOrigin.equals("*")){                response.setHeader("Access-Control-Allow-Origin", allowOrigin);            }else{                List
allowOriginList = Arrays.asList(allowOrigin.split(",")); if (allowOriginList != null && allowOriginList.size() > 0) { String currentOrigin = request.getHeader("Origin"); if (allowOriginList.contains(currentOrigin)) { response.setHeader("Access-Control-Allow-Origin", currentOrigin); } } } } if (!StringUtils.isEmpty(allowMethods)) { response.setHeader("Access-Control-Allow-Methods", allowMethods); } if (!StringUtils.isEmpty(allowCredentials)) { response.setHeader("Access-Control-Allow-Credentials", allowCredentials); } if (!StringUtils.isEmpty(allowHeaders)) { response.setHeader("Access-Control-Allow-Headers", allowHeaders); } if (!StringUtils.isEmpty(exposeHeaders)) { response.setHeader("Access-Control-Expose-Headers", exposeHeaders); } filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { }}

 

  然后修改10087服务消费者调用方式,改成普通的ajax请求即可

跨域测试

cors

 

  在启动10086服务提供者时出现一个启动报错,说corsFilter已经存在,叫我们改名或者启用覆盖,我们启用一下覆盖  PS:最好是重命名我们的bean

 

  启动成功后我们进行测试

  测试下list请求,修改这一部分

//发送ajax请求        $.post("http://localhost:10086/tbUser/list",{username:"张三"},function (data) {            $("#text").text("");            $("#text").text(JSON.stringify(data));        })

  没有问题!

   以上是CORS是基于Filter过滤器的实现,事实上,springboot通过@CrossOrigin注解优雅的实现CORS跨域,我们在10086服务提供者的controller层那里加入@CrossOrigin注解:

@RestController@RequestMapping("/tbUser/")@CrossOrigin(origins = "http://localhost:10087", methods = "GET,POST,PUT,DELETE,OPTIONS", allowedHeaders = "Content-Type,X-Token",allowCredentials = "true")public class TbUserController extends CommonController
{ @Autowired private TbUserService tbUserService;}

  @CrossOrigin注解,官方文档:

  1、Spring 4.2之后提供了跨域注解 @CrossOrigin;

  2、可以用在方法或Controller上;

  3、Controller和方法上都有时,Spring会合并两个注解的属性一起使用;

  注解属性有以下7个:

String[] value() default {}String[] origins() default {} //允许来源String[] allowedHeaders() default {}String[] exposedHeaders() default {}RequestMethod[] methods() default {} //允许调用方法String  allowCredentials() default {}long maxAge() default -1L

 

  如果是SpringBoot项目,还有更简洁的配置,我们看一下官网介绍:

management.endpoints.web.cors.allowed-origins=http://localhost:10087 management.endpoints.web.cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS

 

  后记

   跨域暂时先记录到这里,如果大家发现有什么错误,还望指正

 

上一篇:Web Worker——js的多线程,实现统计博客园总阅读量、总评论量、总推荐量
下一篇:SpringBoot系列——利用系统环境变量与配置文件的分支选择实现“智能部署”

发表评论

最新留言

不错!
[***.144.177.141]2025年04月19日 01时13分28秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章