在配置CORS(跨源资源共享)时,当allowCredentials设置为true时,allowedOrigins不能包含通配符*。这是因为当允许凭据(如cookies、HTTP认证信息等)时,响应头中的Access-Control-Allow-Origin不能是一个通配符,而必须是具体的源。
要解决这个问题,有几种方法:
明确列出允许的源:如果你知道哪些特定的源需要访问你的API,可以将这些源明确地添加到allowedOrigins列表中。
使用allowedOriginPatterns:Spring 5.3引入了allowedOriginPatterns属性,它允许你使用模式来匹配多个源,同时支持凭据。
为了确保CORS和凭据支持的正确性,建议进行一些调整。特别是当你在后端配置了CORS并且允许凭据时,Nginx的配置也需要确保正确传递所有必要的请求头。
前端服务器的作用:
CORS配置:
listen 80;
的作用listen 80;
指令告诉 Nginx 监听 80 端口上的所有传入 HTTP 请求。当你直接在浏览器中输入 http://localhost
并按下回车键时,会发生以下过程:
DNS 解析
localhost
到一个 IP 地址。通常,localhost
会被解析为 127.0.0.1
或 ::1
(IPv6)。建立连接
127.0.0.1
或 ::1
)的 80 端口建立 TCP 连接。发送 HTTP 请求
GET / HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win; x) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Nginx 处理请求
listen
和 server_name
指令来匹配合适的 server
块。listen 80;
和 server_name localhost;
的配置,Nginx 会选择这个 server
块来处理请求。HTTP 默认端口 (80):
http://example.com
时,实际上是在请求 http://example.com:80
。前端开发默认端口 (3000 或其他):
http://localhost:3000
上运行。开发 vs 生产:
代理和反向代理:
http://localhost:3000
上运行。http://localhost:5000
上运行。/api
路径的请求代理到 http://localhost:5000
,从而避免跨域问题。生产阶段:
http://example.com
时,Nginx 会根据配置将请求路由到正确的后端服务。Nginx 可以通过配置来解决跨域问题,主要是通过设置适当的 HTTP 头来允许特定的跨域请求。以下是一些常见的配置方法:
在 Nginx 中,你可以通过 add_header
指令来添加必要的 CORS 头信息。以下是一个基本的示例:
server {
listen 80;
server_name localhost;
location /api/ {
# 允许所有来源的请求
add_header 'Access-Control-Allow-Origin' '*';
# 允许的方法
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
# 允许的头部字段
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization';
# 处理预检请求 (OPTIONS)
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# 转发请求到后端服务器
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
add_header 'Access-Control-Allow-Origin' '*';
*
允许所有来源可能会带来安全风险。建议根据实际情况指定具体的域名,例如 add_header 'Access-Control-Allow-Origin' 'http://frontend.example.com';
。add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization';
Origin
、Content-Type
、Accept
和 Authorization
头部字段。处理预检请求 (OPTIONS)
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
转发请求到后端服务器
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
定义:满足以下条件之一的请求被认为是简单请求:
Content-Type
只能是 application/x-www-form-urlencoded
、multipart/form-data
或 text/plain
。行为:
定义:不满足简单请求条件的请求会被视为预检请求。
Content-Type
包含其他值(如 application/json
)。行为:
Access-Control-Allow-Origin
:指定允许访问的源。例如,Access-Control-Allow-Origin: http://frontend.example.com
。Access-Control-Allow-Methods
:指定允许的 HTTP 方法。例如,Access-Control-Allow-Methods: GET, POST, PUT, DELETE
。Access-Control-Allow-Headers
:指定允许的请求头部字段。例如,Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization
。Access-Control-Allow-Credentials
:指示是否允许发送凭证(如 cookies)。例如,Access-Control-Allow-Credentials: true
。Access-Control-Max-Age
:指定预检请求的结果可以被缓存的时间。例如,Access-Control-Max-Age: 1728000
(20 天)。过程:
当一个网页向不同源发出请求时,CORS 会通过以下几个步骤来处理:
预检请求(Preflight Request):对于某些类型的请求(如使用 HTTP 方法 PUT、DELETE,或者请求带有非简单头部),浏览器会首先发送一个 OPTIONS 请求,这个请求称为“预检请求”。服务器收到这个请求后,会返回一个响应头部,指明实际请求是否被允许。
实际请求(Actual Request):如果预检请求通过,浏览器会继续发送实际的请求。
响应头部(Response Headers):服务器在响应中会包含一些特定的 CORS 头部,如 Access-Control-Allow-Origin,以指示哪些域名可以访问资源。
Origin,翻译为 源(域),在 CORS 上下文中 Origin 由三个元素组成:
Origin = 协议 + 域名 + 端口
对于复杂请求,浏览器会首先发送一个 OPTIONS 请求,包含以下头部字段:
Origin:指示请求的源。
Access-Control-Request-Method:指示实际请求将使用的方法。
Access-Control-Request-Headers:指示实际请求将包含的自定义头部。
服务器收到预检请求后,会返回一个响应,包含以下头部字段以指示是否允许请求:
Access-Control-Allow-Origin:表明允许访问资源的源,可以是具体的源或通配符 *;
Access-Control-Allow-Methods:表明允许的方法,如 GET, POST, PUT, DELETE;
Access-Control-Allow-Headers:表明允许的自定义头部;
Access-Control-Allow-Credentials:表明是否允许发送凭据(如 Cookies);
Access-Control-Expose-Headers:表明哪些头部可以作为响应的一部分被访问;
Access-Control-Max-Age:表明预检请求的结果可以被缓存的时间,单位是秒;
如果预检请求通过,浏览器会继续发送实际请求。
可以设置一个同源的代理服务器,所有的跨域请求都先发送到这个代理服务器,然后由它转发给目标服务器。这样可以避免浏览器的同源策略,因为从浏览器的角度看,所有请求都是同源的。
修改浏览器设置(仅限开发/测试):
--disable-web-security
来临时禁用同源策略。但这只适合本地开发和测试,绝不应该在生产环境中这样做。使用浏览器扩展:
服务器端配置CORS:
JSONP (仅适用于GET请求):
<script>
标签来发起跨域请求。由于<script>
标签不受同源策略,所以可以用来加载来自不同源的JavaScript代码。但是,JSONP只支持GET请求,而且存在一定的安全隐患,因此现代应用中较少使用。WebSocket:
防止数据泄露:
保护用户隐私:
防止CSRF攻击 (Cross-Site Request Forgery):
场景: 用户在一个银行网站 (https://bank.com
) 上登录并查看账户余额。同时,用户打开了一个恶意网站 (https://malicious-site.com
)。
用户在 https://bank.com
登录:
https://bank.com/api/login
发送POST请求。用户访问 https://malicious-site.com
:
恶意网站发送跨域请求:
https://bank.com/api/balance
。Origin
头,并发送预检请求(OPTIONS)到 https://bank.com/api/balance
。https://malicious-site.com
,浏览器阻止实际的GET请求发送到 https://bank.com/api/balance
。场景: 用户在一个社交媒体平台 (https://socialmedia.com
) 上浏览个人资料,并在同一时间打开了一个论坛网站 (https://forum.com
)。
用户在 https://socialmedia.com
浏览个人资料:
用户访问 https://forum.com
:
恶意论坛发送跨域请求:
https://socialmedia.com/api/user-info
https://forum.com
,浏览器阻止实际的GET请求发送到 https://socialmedia.com/api/user-info
。缓存有效期:
Access-Control-Max-Age
字段,该字段指定了预检请求结果的有效期(以秒为单位)。首次跨域请求:
Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等。后续跨域请求:
为了更好地理解预检请求和缓存机制,以下是一些关键的CORS响应头:
Access-Control-Allow-Origin
:
Access-Control-Allow-Origin: http://frontend.com
Access-Control-Allow-Methods
:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers
:
Access-Control-Allow-Headers: Content-Type, X-Custom-Header
Access-Control-Allow-Credentials
:
Access-Control-Allow-Credentials: true
Access-Control-Max-Age
:
Access-Control-Max-Age: 3600
缓存位置:
Access-Control-Max-Age
头指定,通常以秒为单位。缓存内容:
首次跨域请求:
后续跨域请求:
多个目标服务器:
location /
:
/
开头的请求。proxy_pass http://localhost:3000;
:
http://localhost:3000
,即运行在本地 3000 端口的前端服务器。proxy_http_version 1.1;
:
proxy_set_header Upgrade $http_upgrade;
:
Upgrade
头传递给后端服务器。这通常用于 WebSocket 协议的升级。proxy_set_header Connection 'upgrade';
:
Connection
头设置为 upgrade
,以便支持协议升级(如 WebSocket)。proxy_set_header Host $host;
:
Host
头传递给后端服务器。这有助于后端服务器正确处理请求,特别是在有多个虚拟主机的情况下。proxy_cache_bypass $http_upgrade;
:
Upgrade
头,则不使用缓存,直接将请求转发给后端服务器。这通常用于避免缓存 WebSocket 请求。proxy_set_header Host $host;
:
Host
头传递给后端服务器。这样后端服务器可以看到原始请求的主机名。proxy_set_header X-Real-IP $remote_addr;
(虽然你没有配置这个,但可以添加):
X-Real-IP
头获取客户端的真实 IP 地址。proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
(虽然你没有配置这个,但可以添加):
X-Forwarded-For
头中。如果请求经过多个代理,X-Forwarded-For
头会包含所有中间代理的 IP 地址。Content-Type: application/json
application/json
表示请求体是以JSON格式编码的数据。服务器会根据这个头部来解析接收到的数据Referer:
http://localhost:3000/
表明请求来自本地运行的一个Web应用,可能是开发环境中的一个前端应用。浏览器发送请求后:
添加注册弹窗:使用 useState 来控制弹窗的显示与隐藏。
添加注册表单:在弹窗中添加用户名和密码输入框,以及提交按钮。
处理注册逻辑:编写处理注册逻辑的函数,包括发送注册请求和处理响应。
错误提示:在注册过程中,如果出现错误,需要给出相关提示。
添加了 isRegisterModalOpen 状态来控制注册弹窗的显示与隐藏。
添加了 registerUsername 和 registerPassword 状态来存储注册表单中的用户名和密码。
添加了 registerError 和 registerLoading 状态来存储注册过程中的错误信息和加载状态。
添加了 handleRegisterSubmit 函数来处理注册逻辑。
在登录表单下方添加了一个“注册”按钮,点击后打开注册弹窗。
在注册弹窗中添加了注册表单和关闭按钮。
有点蠢,就是每个函数块最后要加分号;
最后return好像不用
遮罩层:
添加了一个全屏的 div 作为遮罩层,覆盖整个页面。
遮罩层的样式设置为 position: fixed, top: 0, left: 0, width: 100%, height: 100%, backgroundColor: 'rgba(0, 0, 0, 0.5)',使其覆盖整个视口并带有半透明的黑色背景。
设置 zIndex: 1000 确保遮罩层在其他内容之上。
点击遮罩层时,会调用 setIsRegisterModalOpen(false) 关闭弹窗。
弹窗:
弹窗的 zIndex 设置为 1001,确保它在遮罩层之上。
点击弹窗内部的元素不会触发遮罩层的点击事件,从而保持弹窗的焦点。
通过这种方式,当弹窗打开时,用户将无法与背景内容进行交互,只能操作弹窗内的内容。
单个根节点:
JSX 要求每个 JSX 表达式只能有一个根节点。如果你直接返回多个相邻的元素,JSX 会报错,因为这违反了单个根节点的规则。
Fragment 的作用:
使用 <> 或 <React.Fragment> 可以将多个元素包裹在一起,而不会在 DOM 中引入额外的节点。这使得代码更简洁,并且不会影响布局或样式。
通常情况下,你需要在全局异常处理器中捕获并处理这个异常,以便返回适当的错误响应。以下是一个简单的全局异常处理器示例:
无法捕获来自后端的错误信息,直接进入catch块
打印err信息::
、这不是已经接收到了吗???
懂了,并不是按照json格式组织的,正确的响应是这样的
是按Json格式组织的
正确的ResponseEntity的泛型是DTO的JAVA类
之前一直没留意,而在错误类里,就是String,不是对象类,没组织成JSON格式,所以错了
fetch 函数的 options 参数类型与你在 fetchWithAuth 方法中传递的对象类型不完全匹配。具体来说,RequestInit 类型中的一些属性(如 next)在标准的 fetch 请求中并不适用,导致类型不匹配。
为了确保类型兼容性,你可以采取以下几种方法之一:
移除不兼容的属性:确保传递给 fetch 的 options 对象只包含 RequestInit 类型中定义的属性。
自定义类型:定义一个自定义类型,继承 RequestInit 并添加额外的属性。
类型断言:使用类型断言来告诉 TypeScript 你传递的对象符合 RequestInit 类型。
在 TypeScript 中,fetch 函数的 options 参数类型被定义为 RequestInit。RequestInit 是一个接口,定义了可以传递给 fetch 的各种选项,例如 method、headers、body 等。然而,RequestInit 接口中并没有 priority 属性。
当你在 fetchWithAuth 方法中传递一个包含 priority 属性的对象时,TypeScript 会报错,因为这个属性不在 RequestInit 接口中定义。
在上面的例子中,options 对象包含了一个 priority 属性,而 RequestInit 接口中并没有 priority 属性。因此,TypeScript 会报错,提示类型不匹配。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- bangwoyixia.com 版权所有 湘ICP备2023022004号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务