• 关于跨域问题详解


    关于跨域问题详解

    1 为什么会有跨域问题

      因为浏览器的同源政策,就会产生跨域。比如说发送的异步请求是不同的两个源,就比如是不同的两个端口或者不同的两个协议或者不同的域名。由于浏览器为了安全考虑,就会产生一个同源政策,不是同一个地方出来的是不允许进行交互的。

    2 常见的跨域解决方案

      跨域的解决方案主要包括以下几种:

    1. 在控制层加入允许跨域的注解@CrossOrigin。
    2. 使用httpclient,不依赖浏览器。
    3. 使用网关Gateway。

    2.1 注解:@CrossOrigin

      在控制层加入允许跨域的注解,即可完成一个项目中前后端跨域的问题。

    2.2 网关整合

      Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix 、Zuul,其不仅提供统一的路由方式,并且还基于Filter链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等。

    2.2.1 路由

      路由是网关最基础的部分,路由信息有一个ID、一个目的URL、一组断言和一组Filter组成。如果断言路由为真,则说明请求的URL和配置匹配。

    2.2.1 断言

      java8中的断言函数。Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自于http request中的任何信息,比如请求头和参数等。

    2.2.3 过滤器

      一个标准的Spring WebFilter。Spring Cloud Gateway中的filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mtNOu9iU-1684916784133)(media/16849132170240/16849149415442.jpg)]
      Spring Cloud Gateway发出请求。然后再由Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway web handler。Handler再通过指定的过滤器链将请求发送到实际的服务执行业务逻辑,然后返回。

    2.2.4 项目中使用

      新建模块service_gateway

    
    
        
            com.lzq
            service_utils
            0.0.1-SNAPSHOT
        
        
            org.springframework.cloud
            spring-cloud-starter-gateway
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

      配置文件

    #服务端口
    server.port=9090
    
    # 服务名
    spring.application.name=service-gateway
    # nacos服务地址 默认8848
    spring.cloud.nacos.discovery.server-addr=127.0.0.1:8888
    #使用服务发现路由
    spring.cloud.gateway.discovery.locator.enabled=true
    #设置路由id
    spring.cloud.gateway.routes[0].id=service-hosp
    #设置路由的uri  lb负载均衡
    spring.cloud.gateway.routes[0].uri=lb://service-hosp
    #设置路由断言,代理servicerId为auth-service的/auth/路径
    spring.cloud.gateway.routes[0].predicates= Path=/*/hosp/**
     
    #设置路由id
    spring.cloud.gateway.routes[1].id=service-cmn
    #设置路由的uri
    spring.cloud.gateway.routes[1].uri=lb://service-cmn
    #设置路由断言,代理servicerId为auth-service的/auth/路径
    spring.cloud.gateway.routes[1].predicates= Path=/*/cmn/**
    #设置路由id
    spring.cloud.gateway.routes[2].id=service-hosp
    #设置路由的uri
    spring.cloud.gateway.routes[2].uri=lb://service-hosp
    #设置路由断言,代理servicerId为auth-service的/auth/路径
    spring.cloud.gateway.routes[2].predicates= Path=/*/userlogin/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

      创建启动类

    @SpringBootApplication
    public class ApiGatewayApplication {
        public static void main(String[] args) {
            SpringApplication.run(ApiGatewayApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      修改前端.evn文件,改成访问网关端口号
      做集群部署时,他会根据名称实现负载均衡
      跨域理解:发送请求后,网关过滤器会进行请求拦截,将跨域放行,转发到服务器中
      跨域配置类

    @Configuration
    public class CorsConfig {
        @Bean
        public CorsWebFilter corsFilter() {
            CorsConfiguration config = new CorsConfiguration();
            config.addAllowedMethod("*");
            config.addAllowedOrigin("*");
            config.addAllowedHeader("*");
     
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
            source.registerCorsConfiguration("/**", config);
     
            return new CorsWebFilter(source);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

      若之前采用注解跨域,需要将@CrossOrigin去掉

    2.3 HttpClient

      常见的使用场景:多系统之间接口的交互、爬虫
      http原生请求,获取百度首页代码

    public class HttpTest {
        @Test
        public void test1() throws Exception {
         String url = "https://www.badu.com";
            URL url1 = new URL(url);
            //url连接
            URLConnection urlConnection = url1.openConnection();
            HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection;
            //获取httpURLConnection输入流
            InputStream is = httpURLConnection.getInputStream();
            //转换为字符串
            InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8);
            BufferedReader br = new BufferedReader(reader);
            String line;
            //将字符串一行一行读取出来
            while ((line = br.readLine())!= null){
                System.out.println(line);
            }
        }
    }
    //设置请求类型
    httpURLConnection.setRequestMethod("GET");
    //请求包含 请求行、空格、请求头、请求体
    //设置请求头编码
    httpURLConnection.setRequestProperty("Accept-Charset","utf-8");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

      使用HttpClient发送请求、接收响应:

    1. 创建HttpClient对象。
    2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
    3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
    4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
    5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
    6. 释放连接。无论执行方法是否成功,都必须释放连接

      集成测试,添加依赖

    
        org.apache.httpcomponents
        httpclient
        4.5.13
    
    @Test
    public void test2(){
        //可关闭的httpclient客户端,相当于打开一个浏览器
        CloseableHttpClient client = HttpClients.createDefault();
        String url = "https://www.baidu.com";
        //构造httpGet请求对象
        HttpGet httpGet = new HttpGet(url);
        //响应
        CloseableHttpResponse response = null;
        try {
            response = client.execute(httpGet);
            // 获取内容
            String result = EntityUtils.toString(response.getEntity(), "utf-8");
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭流
            if (client != null){
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

      项目中使用,系统调用平台接口保存信息,根据传入josn数据保存信息
      系统中

    @RequestMapping(value="/hospital/save",method=RequestMethod.POST)
    public String saveHospital(String data, HttpServletRequest request) {
     try {
      apiService.saveHospital(data);
     } catch (YyghException e) {
      return this.failurePage(e.getMessage(),request);
     } catch (Exception e) {
      return this.failurePage("数据异常",request);
     }
     return this.successPage(null,request);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      saveHospital方法

    @Override
    public boolean saveHospital(String data) {
        JSONObject jsonObject = JSONObject.parseObject(data);
        Map paramMap = new HashMap<>();
        paramMap.put("hoscode","10000");
        paramMap.put("hosname",jsonObject.getString("hosname"))
        //图片
        paramMap.put("logoData", jsonObject.getString("logoData"));
        //  http://localhost:8201/api/hosp/saveHospital
        //httpclient
        JSONObject respone =
                HttpRequestHelper.sendRequest(paramMap,this.getApiUrl()+"/api/hosp/saveHospital");
        System.out.println(respone.toJSONString());
    
        if(null != respone && 200 == respone.getIntValue("code")) {
            return true;
        } else {
            throw new YyghException(respone.getString("message"), 201);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

      HttpRequestHelper工具类

    /**
     * 封装同步请求
     * @param paramMap
     * @param url
     * @return
     */
    public static JSONObject sendRequest(Map paramMap, String url){
        String result = "";
        try {
            //封装post参数
            StringBuilder postdata = new StringBuilder();
            for (Map.Entry param : paramMap.entrySet()) {
                postdata.append(param.getKey()).append("=")
                        .append(param.getValue()).append("&");
            }
            log.info(String.format("--> 发送请求:post data %1s", postdata));
            byte[] reqData = postdata.toString().getBytes("utf-8");
            byte[] respdata = HttpUtil.doPost(url,reqData);
            result = new String(respdata);
            log.info(String.format("--> 应答结果:result data %1s", result));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return JSONObject.parseObject(result);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

      HttpUtil工具类

    public static byte[] send(String strUrl, String reqmethod, byte[] reqData) {
      try {
       URL url = new URL(strUrl);
       HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
       httpcon.setDoOutput(true);
       httpcon.setDoInput(true);
       httpcon.setUseCaches(false);
       httpcon.setInstanceFollowRedirects(true);
       httpcon.setConnectTimeout(CONN_TIMEOUT);
       httpcon.setReadTimeout(READ_TIMEOUT);
       httpcon.setRequestMethod(reqmethod);
       httpcon.connect();
       if (reqmethod.equalsIgnoreCase(POST)) {
        OutputStream os = httpcon.getOutputStream();
        os.write(reqData);
        os.flush();
        os.close();
       }
       BufferedReader in = new BufferedReader(new InputStreamReader(httpcon.getInputStream(),"utf-8"));
       String inputLine;
       StringBuilder bankXmlBuffer = new StringBuilder();
       while ((inputLine = in.readLine()) != null) {  
           bankXmlBuffer.append(inputLine);  
       }  
       in.close();  
       httpcon.disconnect();
       return bankXmlBuffer.toString().getBytes();
      } catch (Exception ex) {
       log.error(ex.toString(), ex);
       return null;
      }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

      对应平台接口

    @RestController
    @RequestMapping("/api/hosp")
    public class ApiController {
        @Autowired
        private HospitalService hospitalService;
        @ApiOperation(value = "上传医院")
        @PostMapping("saveHospital")
        public R saveHospital(HttpServletRequest request) {
            //通过request取到前端接口传过来的值
            Map parameterMap = request.getParameterMap();
            //将数组值转换成一个值
            Map paramMap = HttpRequestHelper.switchMap(parameterMap);
            //将map集合转成josn字符串
            String mapStr = JSONObject.toJSONString(paramMap);
            //josn字符串转成对象
            Hospital hospital = JSONObject.parseObject(mapStr, Hospital.class);
            //加入MongoDB中
            hospitalService.saveHosp(hospital);
            return R.ok();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

      即可完成不同系统中的相互调用

  • 相关阅读:
    机器学习入门案例(2)之使用逻辑回归预测房子是否能被租出去
    漫画风格迁移神器 AnimeGANv2:快速生成你的漫画形象
    博客项目(前台功能实现)
    基于DPDK(x86平台)应用性能优化实践
    煤炭行业数据库-煤炭价格、消耗量、发电量&分省市民用电、工业用电数据
    SmartDialog迁移至4.0:一份真诚的迁移说明
    IDEA启动项目弹框提示:Lombok requires enabled annotation processing
    odoo关于计算字段store=True时导致的安装/更新时间较长问题的解决方案
    加密货币,可能是你唯一能实现“财富自由”的领域 2021-05-24
    System V IPC+消息队列
  • 原文地址:https://blog.csdn.net/a1920135141/article/details/130850008