微服务设计指导-纠正目前网上近90%以上错误的对于“API跨域”问题的所谓指导

背景

  • 因为我们是前后台分离的写法;
  • 因为我们用的是VUE/Angelar;
  • 因为我们在开发这套东西时,存在上述两个问题,而Spring Boot2默认是不允许跨域。因此我们开发时经常会发生这样的情况:

我们有一个spring boot controller如下

/**
 * 系统项目名称 org.sky.demo.redisdemo.controller RedisDemoController.java
 * 
 * Jan 15, 2021-2:55:10 PM 2021XX公司-版权所有
 * 
 */
package org.mk.demo.mvc1.api;
import org.mk.demo.mvc1.bean.Student;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

/**
 * 
 * RedisDemoController
 * 
 * 
 * Jan 15, 2021 2:55:10 PM
 * 
 * @version 1.0.0
 * 
 */
@RestController
@RequestMapping("/demo")
@Api(tags = "demo mvc1")
public class ApiController {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @ApiOperation(value = "修改学生信息接口", notes = "返回success或者是fail")
    @PostMapping(value = "/updStudent", produces = "application/json")
    @ResponseBody
    public String updStudent(@RequestBody  Student std) {
        logger.info(">>>>>>intp mvc1->updStudent");
        logger.info(">>>>>>name->" + std.getName() + "  age->" + std.getAge());
        return "success";
    }

}

我们有一个vue端的axios提交如下:

/src/utils/request.js

import axios from 'axios'

axios.interceptors.response.use(
  response => {
    const data = response.data
    console.log(data)
  },
  error => {
    console.log('error', error)
  }
)
export const request = axios

HelloWorld.vue

<template>
  <div class="hello">
  <button v-on:click="updateStudent()">跨域访问</button>
  </div>
</template>

<script>
import {request} from '@/utils/request.js'
export default {
  name: 'HelloWorld',
  methods: {
    updateStudent () {
      request.post('http://localhost:9081/demo/updStudent', {
        'name': 'abc',
        'age': 10
      })
    }
  }
}
</script>

 运行后,我们点了一下这个“按钮”,然后我们在浏览器的web console得到了如下这样的“跨域不允许”的报错

 已拦截跨源请求:同源策略禁止读取位于 http://localhost:9081/demo/updStudent 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。状态码:403。

错误的做法来了

/**
 * 系统项目名称 org.mk.demo.skypayment.config.webmvc WebMvcConfig.java
 * 
 * Feb 21, 2022-10:30:19 AM 2022XX公司-版权所有
 * 
 */
package org.mk.demo.config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
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;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 
 * WebMvcConfig
 * 
 * 
 * Feb 21, 2022 10:30:19 AM
 * 
 * @version 1.0.0
 * 
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 跨域配置
     */
    @Value("${project.basePath}")
    private String basePath;

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // 允许访问的客户端域名
        List<String> allowedOriginPatterns = new ArrayList<>();

        allowedOriginPatterns.add("*");

        config.setAllowedOrigins(allowedOriginPatterns);
        // 允许服务端访问的客户端请求头
        config.addAllowedHeader("*");
        // 允许访问的方法名,GET POST等
        config.addAllowedMethod("*");
        // 对接口配置跨域设置
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

改完后我们再去点一下vue端的按钮,哎,请求正常进来了,好了,完事了,上生产了。

然后生产生产,定期有安全扫描,每年还有等保资质审核,等保不过,你网站也别开了就,直接到时可以找个电子厂上班了哈。

一看,哎呀。。。到处API都有这个,然后此时再一看我们的商城相关的系统已经成下面这样了

 任何一根API的改动,就得来一次回归测试。。。一般来说比如说中台系统,几十个模块。。。几千个API,这个改大了。

不改吗。。。如梗在咽喉,唉。。。

正确的做法

因此,我们在一开始碰到这种问题时就需要引起注意,这边只教各位对的做法。而不是简单的什么后台来一句:allow(*),网上90%以上的教程都这种,全是错的

/**
 * 系统项目名称 org.mk.demo.skypayment.config.webmvc WebMvcConfig.java
 * 
 * Feb 21, 2022-10:30:19 AM 2022XX公司-版权所有
 * 
 */
package org.mk.demo.config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
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;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 
 * WebMvcConfig
 * 
 * 
 * Feb 21, 2022 10:30:19 AM
 * 
 * @version 1.0.0
 * 
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 跨域配置
     */

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // 允许访问的客户端域名
        List<String> allowedOriginPatterns = new ArrayList<>();
        allowedOriginPatterns.add(basePath);
        //config.setAllowedOriginPatterns(allowedOriginPatterns);
        config.setAllowedOrigins(allowedOriginPatterns);
      
        // 允许服务端访问的客户端请求头
        config.addAllowedHeader("*");
        // 允许访问的方法名,GET POST等
        config.addAllowedMethod("*");
        // 对接口配置跨域设置
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

 此处的这一句:       

List<String> allowedOriginPatterns = new ArrayList<>();

这一句话就是把你允许访问后台的域名一个个加进去,而我们在设计上应该如下操作:

  1. 把允许的访问的域名,一个个加到以下这个List<String>中,可以用一个mongoDb的collection存所有的“被允许的域名”;
  2. 然后把这个List给到config.setAllowedOrigins(allowedOriginPatterns);

最后,以上代码适合spring boot 2.1~2.3,对于2.4及以后版本上述的config.setAllowedOrigins要写成“config.setAllowedOriginPatterns(allowedOriginPatterns);”

我们来看,正确设置了跨域后的访问吧。

首先,我们在.yml里配置一个允许的跨域访问(这块我为了演示因此配在.yml文件里了,各位生产上一定要按照我的核心设计来做这件事以便于应用上扩展性更好)。

server:
  port: 9081
  tomcat:
    max-http-post-size: -1
  max-http-header-size: 10240000

spring:
  application:
    name: mvc1
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB
swagger:
  enable: false
project:
  basePath: http://localhost:8080

/**
 * 系统项目名称 org.mk.demo.skypayment.config.webmvc WebMvcConfig.java
 * 
 * Feb 21, 2022-10:30:19 AM 2022XX公司-版权所有
 * 
 */
package org.mk.demo.config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
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;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 
 * WebMvcConfig
 * 
 * 
 * Feb 21, 2022 10:30:19 AM
 * 
 * @version 1.0.0
 * 
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 跨域配置
     */
    @Value("${project.basePath}")
    private String basePath;

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // 允许访问的客户端域名
        List<String> allowedOriginPatterns = new ArrayList<>();
        allowedOriginPatterns.add(basePath);

        // config.setAllowedOriginPatterns(allowedOriginPatterns);
        config.setAllowedOrigins(allowedOriginPatterns);
        // config.addAllowedOrigin(serverPort);
        // 允许服务端访问的客户端请求头
        config.addAllowedHeader("*");
        // 允许访问的方法名,GET POST等
        config.addAllowedMethod("*");
        // 对接口配置跨域设置
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

我们在vue上点一下原来这个按钮,前端什么都不需要动。

看,后台请求进来了。

真实生产环境如果位于“同域”下的各应用不需要使用上述的一个个setAllow

 如果企业用的生产环境是“同域”下的各应用,就不需要上述这一陀“麻烦”了。全部保持默认即可。只有在“跨域”时才会使用。

下面是一个典型的商城+LEGACY+前端+后台运营管理平台(vue/angelar)的接入,通用做法就是把它纳入同域的实际架构例子 。

这也是为什么企业在做商城时,要围绕着商城能申请“泛域名证书”就用泛域名证书的道理。

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TGITCIC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值