# 前端项目发布

前端项目的环境配置就在 js/app.js 文件中

    serverUrl: "http://192.168.56.107:8080/foodie-dev-api",                      // 接口服务接口地址
    paymentServerUrl: "http://payment.t.mukewang.com/foodie-payment",       // 支付中心服务地址
    shopServerUrl: "http://192.168.56.105:8080/foodie-shop/",                            // 门户网站地址
    centerServerUrl: "http://192.168.56.105:8080/foodie-center/",                        // 用户中心地址
    cookieDomain: "",
1
2
3
4
5

foodie-center 和 foodie-shop 项目中都需要改成上面的配置,下面的配置没有修改说明:

  • cookieDomain:cookie 域,笔者这里是虚拟机部署,就和开发环境一样重置为空字符串
  • paymentServerUrl:没有自有的支付资格,也用视频中线上的地址

部署到服务器后,访问 http://192.168.56.105:8080/foodie-shop/ 打开控制台发现跨域了

Access to XMLHttpRequest at 'http://192.168.56.107:8080/foodie-dev-api/index/cats' from origin 'http://192.168.56.105:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
1

http://192.168.56.105:8080 发起的 js 请求 http://192.168.56.107:8080/foodie-dev-api/index/cats 跨域了

# 跨域解决

开发的时候配置过了,只不过限制了能访问的域名

package cn.mrcode.foodiedev.config;

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;

/**
 * 跨域配置
 *
 * @author mrcode
 * @date 2021/2/13 16:27
 */
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // 设置运行跨域的地址,也可以使用 * 代替允许所有地址
        config.addAllowedOrigin("http://localhost:8080");
        // 是否运行携带 cookie 相关信息
        // 在前端也有的框架可以配置这一样,前端是否允许跨域的时候携带:axios.defaults.withCredentials = true;
        config.setAllowCredentials(true);
        // 允许访问所有的 http 请求方式,如 get、post
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");

        // 为 url 添加映射路径
        // 允许所有路径使用该配置
        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
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

这里再增加一个线上的域名就可以了

        config.addAllowedOrigin("http://192.168.56.105:8080");
        config.addAllowedOrigin("http://192.168.56.105");  // 为以后做准备
1
2

由于不熟悉 http 协议,笔者找了很久才找到问题,具体做法是:

  1. 在登录页登录成功之后不进行跳转,停留在当前页面,就能看到发出的 js 请求

  2. js 请求中发现了问题

    image-20210301224551821

    可以看到,发出去了的请求是成功了的,也有 cookie 从服务端被设置,但是他的 Domain=192.168.56.107 这个 IP 是线上环境的后端 api 服务器地址,与前端项目 192.168.56.105 不匹配。

    另外 Chrome 浏览器新版本新增了一个 SameSite 属性,他的格式如下,那么我们需要改动代码

    Set-Cookie: CookieName=CookieValue; SameSite=None;
    
    1

    找到后端源码中,设置 cookie 的工具类中有如下的代码

    cn.mrcode.foodiedev.common.util.CookieUtils#doSetCookie(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String, java.lang.String, int, boolean)
    
     private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                                              String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
            try {
                if (cookieValue == null) {
                    cookieValue = "";
                } else if (isEncode) {
                    cookieValue = URLEncoder.encode(cookieValue, "utf-8");
                }
                Cookie cookie = new Cookie(cookieName, cookieValue);
                if (cookieMaxage > 0)
                    cookie.setMaxAge(cookieMaxage);
                if (null != request) {// 设置域名的cookie
                    String domainName = getDomainName(request);
                    logger.info("========== domainName: {} ==========", domainName);
                   // 这里不是 localhost 才设置为后端的域名
                    if (!"localhost".equals(domainName)) {
                        cookie.setDomain(domainName);
                    }
                }
                cookie.setPath("/");
                response.addCookie(cookie);
            } catch (Exception 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

    由于 Cookie 类不支持 SameSite,那么这里就需要我们自己拼接 cookie 然后放到 header 中,代码如下

       private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                                              String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
            try {
                if (cookieValue == null) {
                    cookieValue = "";
                } else if (isEncode) {
                    cookieValue = URLEncoder.encode(cookieValue, "utf-8");
                }
                ResponseCookie.ResponseCookieBuilder rr = ResponseCookie.from(cookieName, cookieValue)
                        .domain("")
                        .maxAge(cookieMaxage)
                        .path("/")
                        .httpOnly(false)
                        .secure(false) // 是否允许 js 读取,这里貌似要设置成 true
                        .sameSite("None");
                response.addHeader("Set-Cookie", rr.build().toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    // org.springframework.http.ResponseCookie 类
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    这个代码设置之后,还是被谷歌浏览器拒绝了,应该是要求是 https 链接才可以。

    笔者捣鼓了很长时间:

    1. domain 不设置,跨域环境下 cookie 不生效,不支持 ip,只支持域名
    2. sameSite 属性设置后,需要设置 secure 属性,并且需要 https 链接

    到最后都没有搞定,这里暂时就不解决这个了,后面还需要换成 nginx 来代理,就没有跨域问题了

    跨域的知识笔者懵逼了,后来又尝试过在本地 hosts 中使用域名来访问目标 IP,但是最后测试出来的结果没有报错,但是也看不到 cookie 的响应,算了,不搞跨域了