Spring Boot2.x 您所在的位置:网站首页 nginx代理前后端两个端口 Spring Boot2.x

Spring Boot2.x

2024-05-26 21:26| 来源: 网络整理| 查看: 265

文章目录概述浏览器同源策略后台搭建pom.xmlinterceptor 配置Controller启动测试浏览器和session后端工程发布到服务器上问题复现通过Nginx反向代理解决跨域问题安装Nginx修改配置文件修改前台页面访问地址原因分析启动Nginx 测试小结概述

随着前后端分离这种开发模式的普及,前台和后台分开部署,可能部署在一台主机上不同的端口下,也有可能部署在多个主机上,前后台通过ajax或者axios等方式调用restful接口进行交互。由于浏览器的“同源策略”,协议、域名、端口号但凡有一个不同,势必会产生跨域问题。

如果发生跨域的话,浏览器中每次请求的session都是一个新的,即sessionId肯定不相同。

我们知道 ,服务器可以为每个用户浏览器创建一个session对象。默认情况下一个浏览器中独占一个session.

http请求是无状态的,那服务器是如何知道多次浏览器的请求是同一个会话呢?

事实上服务器创建session出来后,会将session的id,以cookie的形式回写给客户机,这样,只要浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户端浏览器携带session id过来了,就会使用内存中与之对应的session为之服务。 下文配合代码和浏览器一起来看下。

浏览器同源策略

参考阮一峰老师的文章:浏览器同源政策及其规避方法

后台搭建

为了简单,我们使用Spring Boot 快速搭建个后台服务,提供restful接口。 我这里加上了interceptor,其实验证这个问题,没必要加。 加上一方面是熟悉下拦截器的使用,二来也可以看下request中请求的URI

在这里插入图片描述在这里插入图片描述pom.xml代码语言:javascript复制 4.0.0modelVersion> org.springframework.bootgroupId> spring-boot-starter-parentartifactId> 2.1.3.RELEASEversion> parent> com.artisangroupId> CrossDomainartifactId> 0.0.1-SNAPSHOTversion> CrossDomainByNginxBackgroundname> Artisan description> 1.8java.version> properties> org.springframework.bootgroupId> spring-boot-starter-webartifactId> dependency> org.projectlombokgroupId> lombokartifactId> dependency> org.springframework.bootgroupId> spring-boot-devtoolsartifactId> trueoptional> truescope> dependency> org.springframework.bootgroupId> spring-boot-starter-testartifactId> testscope> dependency> dependencies> org.springframework.bootgroupId> spring-boot-maven-pluginartifactId> plugin> plugins> build> project>interceptor 配置

不多说了,MyInterceptor.java 参考 Spring Boot2.x-12 Spring Boot2.1.2中Filter和Interceptor 的使用

按照工程中restful的设计,注意下 WebConfig中的拦截路径即可。

在这里插入图片描述在这里插入图片描述Controller代码语言:javascript复制package com.artisan.controller; import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/artisan") public class ArtisanController { @GetMapping("/getValueFromSession") public String getSession(HttpServletRequest request) { // 获取当前request的session,将属性设置到session里 request.getSession().setAttribute("artisan", "artisanTest"); return "sessionId:" + request.getSession().getId() + ", artisans的属性值:" + request.getSession().getAttribute("artisan"); } @GetMapping("/checkCrossDomain") public String checkCrossDomain(HttpServletRequest request) { return "sessionId:" + request.getSession().getId() + ", artisans的属性值:" + request.getSession().getAttribute("artisan"); } }启动测试

没在application.yml中指定server.port ,使用了默认的8080端口,启动项目,确保可以访问 http://localhost:8080/artisan/getValueFromSession

在这里插入图片描述在这里插入图片描述

不要关闭浏览器,继续访问 http://localhost:8080/artisan/checkCrossDomain

在这里插入图片描述在这里插入图片描述

注意下这两个sessionId是一样的,说明是同一个session

浏览器和session

刚才概述中

在这里插入图片描述在这里插入图片描述

再细化点

用户向服务器发送请求,比如登录操作发送用户名和密码 服务器验证通过后,通过HttpServletRequest#getSession()#setAttribute等方法保存相关数据 服务器向用户返回一个 session_id,浏览器set-cookie Cookie 即Cookie = session_id 用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。 服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

当然了单节点的情况下还好,如果是集群环境,或者是跨域的服务请求,那么久需要实现session 数据共享,使集群中的每台服务器都能够读取 session。

总的来说【集群环境下】我目前所了解的有三种思路

session复制,比如Tomcat支持的Session复制. 优点:tomcat内置支持 缺点:如果集群过大,session 复制为all to all占用带宽,效率不高 session 数据持久化,写入redis或者数据库等。优点架构清晰,缺点是工程量大。而且也需要考虑session数据的持久层的高可用,否则单点登录就会失败。 服务端不保存 session ,所有数据都保存在客户端,比如 JWT (JSON WEB TOKEN)

我们清空浏览器的缓存(包括cookie)

在这里插入图片描述在这里插入图片描述

结合上面建好的工程来演示下上面的描述。

重新访问 http://localhost:8080/artisan/getValueFromSession

在这里插入图片描述在这里插入图片描述

上面的截图就是: 服务器创建session出来后,会将session的id,以cookie的形式回写给客户机

不要关闭浏览器,新开个窗口访问 http://localhost:8080/artisan/checkCrossDomain

在这里插入图片描述在这里插入图片描述

上面的截图就是: 只要浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户端浏览器携带session id过来了,就会使用内存中与之对应的session为之服务

后端工程发布到服务器上在这里插入图片描述在这里插入图片描述

把刚才的spring boot 服务端,达成了可执行的jar 【sts 工程右键-- Run As --Maven build , 输入clean package (清除、打包)】 ,放到192.168.31.34服务器上 , 为了创造一个不同的ip地址。 顺便我把端口号也通过启动脚本设置成了9000

启动脚本如下:

代码语言:javascript复制#!/bin/bash nohup java -jar CrossDomain-0.0.1-SNAPSHOT.jar --server.port=9000 > log.txt & tail -f log.txt问题复现

为了模拟【协议、域名、端口号但凡有一个不同,势必会产生跨域问题 】,那就让ip地址+端口号不同吧。

正好前几天折腾axis , 搭建axis环境的时候,正好需要用tomcat去验证下是否搭建成功(把axis拷贝到tomcat的webapps下),那顺便借用下这里的index.html ,修改后的index.html如下

代码语言:javascript复制 Cross Domain Test Artisan 跨域请求

$("#btn").click(function(event){ $.ajax({ url: 'http://192.168.31.34:9000/artisan/getValueFromSession', type: "GET", success: function (data) { $("#crossDomainRequest1").html("跨域访问成功->getValueFromSession方法返回:" + data); $.ajax({ url: 'http://192.168.31.34:9000/artisan/checkCrossDomain', type: "GET", success: function (data) { $("#crossDomainRequest2").html("跨域访问成功->checkCrossDomain方法返回:" + data); } }); }, error: function (data) { $("#crossDomainRequest1").html("发生跨域错误!!"); } }); });

启动tomcat ,访问 http://localhost:8080/axis/index.html ,点击按钮,观察开发者工具中的Network和Console

在这里插入图片描述在这里插入图片描述

点击 getValueFromSession 查看,

在这里插入图片描述在这里插入图片描述

服务端其实是返回了,也从侧面说明了跨域问题是浏览器的“同源策略”导致,和服务端不相干。

再继续看下报错

在这里插入图片描述在这里插入图片描述

Access to XMLHttpRequest at ‘http://192.168.31.34:9000/artisan/getValueFromSession’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

如上 发生了跨域问题。

通过Nginx反向代理解决跨域问题

原理: Nginx的反向代理“欺诈”浏览器,使得浏览器和服务器是同源访问。

安装Nginx

因为要测试跨域 ,为了方便,服务端放到了服务器上,使用Nginx部署的前台我们就放到本地吧,所以使用了windows版本的Nginx 。

Nginx 下载地址: http://nginx.org/en/download.html

在这里插入图片描述在这里插入图片描述修改配置文件代码语言:javascript复制worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #前端页面服务器信息 server { #启动的端口和域名 listen 8888; server_name localhost; #添加头部信息,proxy_set_header用来重定义发往后端服务器的请求头。 #语法 proxy_set_header Field Value proxy_set_header Cookie $http_cookie; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #代理地址 及映射的服务端的地址 # 最重要的配置 location /frontend/ { proxy_pass http://192.168.31.34:9000/; #使用代理地址时末尾加上斜杠"/" # 如下 proxy_set_header 和 add_header 不加经过验证也是OK的。 # 使用add_header指令来设置response header if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; '; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; } } #添加拦截路径和根目录 location / { root html/artisan; # 根目录 index index.html index.htm; #首页 } } }

最重要的是 proxy_pass配置

关于add_header ,比如 GET 增加了 add_header ,在浏览器中GET请求的方法可以在response header查看到相关信息

add_header ‘Access-Control-Expose-Headers’ 必须要加上你请求时所带的header,比如我们经常用的Token

参考: https://enable-cors.org/server_nginx.html

在这里插入图片描述在这里插入图片描述

下面的浏览器返回截图,是没有增加add_header的,故特意贴一张截图如上,增加上也是OK的,更细粒度的控制,请知悉。

修改前台页面访问地址代码语言:javascript复制 Nginx Cross Domain Test Artisan 跨域请求

$("#btn").click(function(event){ $.ajax({ url: 'http://localhost:8888/frontend/artisan/getValueFromSession', type: "GET", success: function (data) { $("#crossDomainRequest1").html("跨域访问成功->getValueFromSession方法返回:" + data); $.ajax({ url: 'http://localhost:8888/frontend/artisan/checkCrossDomain', type: "GET", success: function (data) { $("#crossDomainRequest2").html("跨域访问成功->checkCrossDomain方法返回:" + data); } }); }, error: function (data) { $("#crossDomainRequest1").html("发生跨域错误!!"); } }); }); 原因分析

先看index.html的存放位置

在这里插入图片描述在这里插入图片描述

与 nginx的配置文件中如下配置保持一致

在这里插入图片描述在这里插入图片描述

同时配置的启动端口和域名,对应配置文件中的

在这里插入图片描述在这里插入图片描述

所以通过访问 http://localhost:8888/index.html 就找到了 html/artisan目录下的index.html文件

再看下 index.html中修改的请求地址,由原先的直接请求后台,改为请求Nginx,让Nginx去转发请求

在这里插入图片描述在这里插入图片描述

localhost:8888上面说了,下面来看下这个frontend是个啥东西呢? 是自定义的,叫啥都行,只要能对应上就行。

在这里插入图片描述在这里插入图片描述

意思是让Nginx代理该请求

html中的两个地址经过Nginx后,发生如下变化

请求URL:http://localhost:8888/frontend/artisan/getValueFromSession 代理后的URL:http://192.168.31.34:9000/artisan/getValueFromSession

请求URL:http://localhost:8888/frontend/artisan/checkCrossDomain 代理后的URL:http://192.168.31.34:9000/artisan/checkCrossDomain

代理后的地址也是192.168.31.34:9000端口了,和服务端 192.168.31.34:9000一致,也就不存在跨域问题了。

跨域操作实际上是由Nginx的proxy_pass进行完成.

这个可以从控制台中得到确认

在这里插入图片描述在这里插入图片描述启动Nginx 测试

双击nginx.exe 启动Nginx , 访问 http://localhost:8888/index.html

在这里插入图片描述在这里插入图片描述

访问正常,且是通过一个session , 跨域问题使用Nginx得到解决。

小结 通过Nginx去解决跨域问题本质上是间接跨域,因为使用反向代理欺骗浏览器,所以浏览器任务客户端和服务端在相同的域名中,可以认为是同源访问,所以session不会丢失。上面的实验结论也证明了这一点 如果使用CORS实现了直接跨域,主要是在服务端通过给response设置header属性,帮助服务器资源进行跨域授权。 因为发生跨域访问,服务器会每次都创建新的Session,会导致session丢失,安全性和灵活性更高,但需要开发人员去解决跨域session丢失的问题。


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有