IT七剑客 IT七剑客
首页
wresource
郭霖
孤寒者
IT邦德
沉默王二
老麦
stackoverflow
GitHub (opens new window)
首页
wresource
郭霖
孤寒者
IT邦德
沉默王二
老麦
stackoverflow
GitHub (opens new window)
  • Java基础语法

  • 程序人生

  • 实用工具

  • Java重要知识点

  • Java工具

    • currentTimeMillis 统计耗时了,太 Low,试试 Spring Boot 源码在用的 StopWatch吧,够优雅!
    • WindTerm:新一代开源免费的终端工具,GitHub星标6.6k+,太酷了!
    • 在IDEA里下个五子棋不过分吧?
    • 好用到爆!GitHub 星标 32.5k+的命令行软件管理神器,功能真心强大!
    • 我们公司使用了6年的Spring Boot项目部署方案,打包 + 一键部署,稳的一批
    • 再见 Spring Task,这款老而弥坚的开源任务调度框架,用起来够优雅!
    • 解放双手!推荐一款 GitHub 星标 8.2k+的命令行软件管理器,非常酷炫!
    • 再见收费的TeamViewer,推荐一款不限速的国产远程控制软件
    • 几行代码,网站图片访问速度 100ms 飙升到 20ms!
    • 某意大利小哥,竟靠一个缓存中间件直接封神?
    • WindTerm:新一代开源免费的终端工具,GitHub星标6.6k+,太酷了!
    • 厉害!我带的实习生仅用四步就整合好SpringSecurity+JWT实现登录认证!
    • 保姆级SpringBoot+Vue图片上传到阿里云OSS教程
    • 两天两夜,1M图片优化到100kb!
    • 干掉Session?这个跨域认证解决方案真的优雅!
    • 前后端分离项目,如何解决跨域问题?
      • Spring Boot AOP 扫盲,实现接口访问的统一日志记录
      • 再见收费的Navicat!操作所有数据库就靠它了!
      • 取代 Mybatis Generator,这款代码生成神器配置更简单,开发效率更高!
      • 再见丑陋的 SwaggerUI,这款开源的API文档生成神器界面更炫酷,逼格更高!
    • 数组与字符串

    • 沉默王二 Java
    • Java工具
    沉默王二
    2022-09-01
    目录

    前后端分离项目,如何解决跨域问题?

    跨域问题是前后端分离项目中非常常见的一个问题,举例来说,编程猫(codingmore (opens new window))学习网站的前端服务跑在 8080 端口下,后端服务跑在 9002 端口下,那么前端在请求后端接口的时候就会出现跨域问题。

    img

    403 Forbidden 是HTTP协议中的一个状态码(Status Code),意味着后端服务虽然成功解析了请求,但前端却没有访问该资源的权限。

    那怎么解决这个问题呢?通常有两个思路:

    • 前端使用 Nodejs 代理(开发环境下,生产环境下可以用 Nginx 替代)
    • 或者后端开启跨域资源共享

    # 一、关于跨域

    跨域对于前后端开发者来说,就像一块狗皮膏药,无论是面试还是开发中,都会经常遇到。

    之所以出现跨域问题,是因为浏览器的同源策略 (opens new window),为了隔离潜在的恶意文件,为了防御来自歪门邪道的攻击,浏览器限制了从同一个源加载的文档或脚本与来自另一个源的资源进行交互。

    前面我们提到了,前端跑在 8080 端口下,后端跑在 9002 端口下,这种情况就属于不同的源(域名不同,协议不同,端口不同),所以 8080 端口下的前端请求直接访问 9002 端口下的后端接口时就访问失败了。

    那正确的打开方式是什么呢?我们前面也提到了,前端使用 Nodejs 代理或者后端开启跨域资源共享,我们一一来实践下。

    img

    # 二、Nodejs 代理

    在 Nodejs 出现之前,JavaScript 编写的程序通常需要在用户的浏览器上执行,Node.js 出现后,JavaScript 也能用于服务端编程了。Nodejs 一系列的内置模块使得程序可以脱离 IIS、Apache 这种 Web 服务作为独立的服务器执行。

    我们使用 Nodejs 来解决跨域问题的思路就是,在本地创建一个虚拟服务器,对 8080 端口下的前端请求进行代理,同时接收 9002 端口下的服务器端响应,这样服务端和服务端进行数据的交互就不会出现跨域问题了。

    第一步,配置 Nodejs 代理服务

    module.exports = {
      dev: {
        // Paths
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        proxyTable: {
          '/api': {
            target: 'http://localhost:9002', // 你请求的第三方接口
            changeOrigin: false, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
            pathRewrite: { // 路径重写,
              '^/api': '' // 替换target中的请求地址,也就是说以后你在请求http://api.codingmore.top/v2/XXXXX这个地址的时候直接写成/api即可。
            }
          },
        },
    }
    复制代码
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    第二步,配置前端访问请求路径

    module.exports = merge(prodEnv, {
      NODE_ENV: '"development"',
      VUE_APP_BASE_API: '"/api"'
      // VUE_APP_BASE_API: '"http://localhost:9002"'
    })
    复制代码
    
    1
    2
    3
    4
    5
    6

    第三步,重启前端服务

    img

    再次点击「登录」按钮,可以看到请求的 URL 发生了改变,原来是 http://localhost:9002/users/login,现在是 http://localhost:8080/api/users/login。与此同时,可以看到多了一个 Remote Address,端口也是 8080,也就是说经过 Nodejs 的代理,前后端的交互在同一个源下面了,这样就不会发生跨域问题了。

    同时,可以看得到,服务器端返回的状态码变成了 200,表示请求成功。

    img

    # 三、开启跨域资源共享

    跨域资源共享,也就是 Cross-Origin Resource Sharing,简拼为 CORS,是一种基于 HTTP 头信息的机制,通过允许服务器标识除了它自己以外的资源,从而实现跨域访问。

    第一步,开启 CORS 支持

    在 Spring Boot 应用中,加入 CORS 的支持简单到不忍直视,添加一个配置类就可以了。

    @Configuration
    public class GlobalCorsConfig {
        @Bean
        public CorsFilter corsFilter() {
            CorsConfiguration config = new CorsConfiguration();
            // 设置你要允许的网站域名
            config.addAllowedOrigin("http://localhost:8080");
            //允许跨域发送cookie
            config.setAllowCredentials(true);
            //放行全部原始头信息
            config.addAllowedHeader("*");
            //允许所有请求方法跨域调用
            config.addAllowedMethod("*");
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", config);
            return new CorsFilter(source);
        }
    }
    复制代码
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    第二步,重启后端服务,再次点击登录按钮,发现请求已经可以正常访问了。

    img

    本例中,后端返回 Access-Control-Allow-Origin: http://localhost:8080 就表示,跑在 9002 端口下的后端接口可以被 8080 端口的前端请求访问。

    如果允许所有域名进行跨域调用的话,只需改变一行代码即可。

    //允许所有域名进行跨域调用
    config.addAllowedOriginPattern("*");
    // 设置你要允许的网站域名
    //        config.addAllowedOrigin("http://localhost:8080");
    复制代码
    
    1
    2
    3
    4
    5

    对于 login 这种简单的请求来说,它们是不会触发 CORS 预检的,因此不需要在服务器端增加其他配置就可以了。那什么是简单请求呢?

    1)请求方法是以下三种方法之一:

    • HEAD
    • GET
    • POST

    2)HTTP 的头信息不超出以下几种字段:

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

    那对于会触发 CORS 预检的非简单请求(比如说请求方法是 PUT 或 DELETE,或者 Content-Type 字段的类型是 application/json,或者请求消息头包含了一些自定义的字段),该怎么办呢?

    非简单请求在正式通信之前,会增加一次 HTTP 查询请求,称为“预检”请求。预检请求通过后,才会返回正常的响应内容。

    img

    拿编程猫的文章管理页来举例,该页面会向后端发起一个 posts/queryPageable 的分页查询,该请求包含了一个自定义的消息头 Authorization,于是浏览器认为该请求是一个非简单请求,然后就会自动发起一次 OPTIONS 请求,但由于我们的 Spring Boot 项目整合了 SpringsScurity 安全管理框架,没有对OPTIONS请求放开登录认证,导致验证失败,文章分页请求的响应数据就没有返回回来。

    img

    第三步,通过以下代码给 OPTIONS 请求放行。

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                    .authorizeRequests();
            //允许跨域请求的OPTIONS请求
            registry.antMatchers(HttpMethod.OPTIONS)
                    .permitAll();
        }
    }
    复制代码
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    再次重启后端服务,重新访问文章列表接口,发现有响应数据了。

    img

    非简单请求必须首先使用 OPTIONS 请求方法发起一个预检请求到服务器端,以获知服务器是否允许该实际请求。"预检请求“的使用,避免了跨域请求对服务器的用户数据造成未预期的影响。

    我们来通过两张图片简单总结一下预检请求的整个过程,第一张,发起 OPTIONS 预检请求:

    img

    第二章,发起正式请求:

    img

    # 四、源码路径

    编程猫后端源码:

    github.com/itwanger/co… (opens new window)

    编程猫后台管理的前端源码:

    github.com/itwanger/co… (opens new window)

    参考链接:

    跨域:segmentfault.com/a/119000001… (opens new window) CORS:developer.mozilla.org/zh-CN/docs/… (opens new window) 阮一峰:www.ruanyifeng.com/blog/2016/0… (opens new window) 简单请求+预检请求:github.com/amandakelak… (opens new window)


    本篇已收录至 GitHub 上星标 1.6k+ star 的开源专栏《Java 程序员进阶之路》,据说每一个优秀的 Java 程序员都喜欢她,风趣幽默、通俗易懂。内容包括 Java 基础、Java 并发编程、Java 虚拟机、Java 企业级开发、Java 面试等核心知识点。学 Java,就认准 Java 程序员进阶之路😄。

    github.com/itwanger/to… (opens new window)

    star 了这个仓库就等于你拥有了成为了一名优秀 Java 工程师的潜力。也可以戳下面的链接跳转到《Java 程序员进阶之路》的官网网址,开始愉快的学习之旅吧。

    tobebetterjavaer.com/ (opens new window)

    img

    没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。

    作者:沉默王二 链接:https://juejin.cn/post/7068076816665149471 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    编辑 (opens new window)
    #Java#沉默王二
    上次更新: 2023/01/30, 02:04:53
    干掉Session?这个跨域认证解决方案真的优雅!
    Spring Boot AOP 扫盲,实现接口访问的统一日志记录

    ← 干掉Session?这个跨域认证解决方案真的优雅! Spring Boot AOP 扫盲,实现接口访问的统一日志记录→

    最近更新
    01
    Coding 102 Writing code other people can read
    02-26
    02
    Kotlin Flow响应式编程,StateFlow和SharedFlow
    02-05
    03
    Kotlin Flow响应式编程,操作符函数进阶
    02-05
    更多文章>
    Theme by Vdoing | Copyright © 2022-2023 IT七剑客 | MIT License
  • 闽ICP备2021006579号-4
  • 闽公网安备 35012102500470号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式