如何共享cookie跨源?更具体地说,如何将Set-Cookie
标题与标题Access-Control-Allow-Origin
结合使用?
以下是对我情况的解释:
我正在尝试为localhost:4000
上托管的网络应用中localhost:3000
上运行的API设置Cookie。
我似乎在浏览器中收到了正确的响应标头,但不幸的是它们没有效果。这些是响应标题:
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://localhost:3000 Vary: Origin, Accept-Encoding Set-Cookie: token=0d522ba17e130d6d19eb9c25b7ac58387b798639f81ffe75bd449afbc3cc715d6b038e426adeac3316f0511dc7fae3f7; Max-Age=86400; Domain=localhost:4000; Path=/; Expires=Tue, 19 Sep 2017 21:11:36 GMT; HttpOnly Content-Type: application/json; charset=utf-8 Content-Length: 180 ETag: W/"b4-VNrmF4xNeHGeLrGehNZTQNwAaUQ" Date: Mon, 18 Sep 2017 21:11:36 GMT Connection: keep-alive
此外,当我使用Chrome开发者工具的“网络”标签检查流量时,我可以在Response Cookies
下看到Cookie。但是,我无法在Storage/Cookies
下的应用程序选项卡中看到设置的cookie。我没有看到任何CORS错误,所以我认为我错过了其他的东西。
有什么建议吗?
我正在使用React-Redux应用程序中的request模块向服务器上的/signin
端点发出请求。对于服务器,我使用快递。
Express服务器:
res.cookie('token', 'xxx-xxx-xxx', { maxAge: 86400000, httpOnly: true, domain: 'localhost:3000' })
浏览器中的请求:
request.post({ uri: '/signin', json: { userName: 'userOne', password: '123456'}}, (err, response, body) => { // doing stuff })
我现在设置请求和响应标头现在像疯了一样,确保它们在请求和响应中都存在。下面是截图。请注意标题Access-Control-Allow-Credentials
,Access-Control-Allow-Headers
,Access-Control-Allow-Methods
和Access-Control-Allow-Origin
。看看我在Axios's github找到的问题,我的印象是现在已经设置了所有必需的标题。然而,仍然没有运气......
答案 0 :(得分:40)
允许接收&通过CORS请求成功发送cookie,请执行以下操作。
后端(服务器):
将HTTP标头Access-Control-Allow-Credentials
值设置为true
。
此外,请确保已设置HTTP标头Access-Control-Allow-Origin
。
前端(客户端):将XMLHttpRequest.withCredentials
标志设置为true
,这可以通过不同的方式实现,具体取决于所使用的请求 - 响应库:
jQuery 1.5.1 xhrFields: {withCredentials: true}
ES6 fetch() credentials: 'include'
axios:withCredentials: true
避免将CORS与Cookie结合使用。您可以使用代理实现此目的。
如果你出于某种原因不要避免它。解决方案在上面。
事实证明,如果域名包含端口,则Chrome不会设置Cookie。将其设置为localhost
(没有端口)不是问题。非常感谢Erwin提示!
答案 1 :(得分:14)
2020年发布的Chrome浏览器说明。
Chrome的未来版本将仅提供跨站点的Cookie 请求是否设置了
SameSite=None
和Secure
。
因此,如果您的后端服务器未设置SameSite = None,则Chrome默认情况下将使用SameSite = Lax,并且不会将此{cookie withCredentials:true}请求使用此cookie。
更多信息https://www.chromium.org/updates/same-site。
Firefox和Edge开发人员也希望将来发布此功能。
可在此处找到规格:https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#page-8
答案 2 :(得分:3)
为了使客户端能够读取跨源请求中的cookie,您需要具备以下条件:
服务器的所有响应的标头中均应包含以下内容:
Access-Control-Allow-Credentials: true
客户端需要使用withCredentials: true
选项发送所有请求
在Angular 7和Spring Boot的实现中,我通过以下方式实现了这一目标:
服务器端:
@CrossOrigin(origins = "http://my-cross-origin-url.com", allowCredentials = "true")
@Controller
@RequestMapping(path = "/something")
public class SomethingController {
...
}
origins = "http://my-cross-origin-url.com"
部分会将Access-Control-Allow-Origin: http://my-cross-origin-url.com
添加到每个服务器的响应标头中
allowCredentials = "true"
部分会将Access-Control-Allow-Credentials: true
添加到每个服务器的响应标头中,这是我们需要的,以便客户端读取cookie
客户端:
import { HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from 'rxjs';
@Injectable()
export class CustomHttpInterceptor implements HttpInterceptor {
constructor(private tokenExtractor: HttpXsrfTokenExtractor) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// send request with credential options in order to be able to read cross-origin cookies
req = req.clone({ withCredentials: true });
// return XSRF-TOKEN in each request's header (anti-CSRF security)
const headerName = 'X-XSRF-TOKEN';
let token = this.tokenExtractor.getToken() as string;
if (token !== null && !req.headers.has(headerName)) {
req = req.clone({ headers: req.headers.set(headerName, token) });
}
return next.handle(req);
}
}
使用此类,您实际上可以为所有请求注入其他内容。
第一部分req = req.clone({ withCredentials: true });
是使用withCredentials: true
选项发送每个请求所需要的。实际上,这意味着将先发送一个OPTION请求,以便您在发送实际的POST / PUT / DELETE请求之前,将其中的cookie和授权令牌发送给它们,并在其中发送需要附加此令牌的POST / PUT / DELETE请求。命令服务器验证并执行请求。
第二部分是专门处理所有请求的反CSRF令牌的部分。在需要时从cookie读取它,并将其写入每个请求的标头中。
所需的结果是这样的:
答案 3 :(得分:1)
对于Express,请将您的Express库升级到最新的稳定版本4.17.1
。然后;
在CorsOption中:将origin
设置为您的本地主机URL或前端生产URL,并将credentials
设置为true
例如
const corsOptions = {
origin: config.get("origin"),
credentials: true,
};
我使用config npm module动态设置了我的原点。
然后,在res.cookie中:
对于localhost:根本不需要设置sameSite和安全选项,可以将http cookie设置为httpOnly
到true
,以防止XSS攻击和其他有用的options,具体取决于您的用例。
对于生产环境,您需要将sameSite
设置为none
进行跨域请求,将secure
设置为true
。请记住,sameSite
仅适用于目前的Express最新版本,而最新的Chrome版本仅在https
上设置cookie,因此需要安全选项。
这就是我如何使我的动感
res
.cookie("access_token", token, {
httpOnly: true,
sameSite: app.get("env") === "development" ? true : "none",
secure: app.get("env") === "development" ? false : true,
})
答案 4 :(得分:0)
Pim的回答非常有帮助。就我而言,我必须使用
Expires / Max-Age: "Session"
如果它是一个dateTime,即使它没有过期,它仍然不会将cookie发送到后端:
Expires / Max-Age: "Thu, 21 May 2020 09:00:34 GMT"
希望这对将来可能遇到相同问题的人很有帮助。
答案 5 :(得分:0)
前端
`await axios.post(`your api`, data,{
withCredentials:true,
})
await axios.get(`your api`,{
withCredentials:true,
});`
后端
var corsOptions = {
origin: 'http://localhost:3000', //frontend url
credentials: true}
app.use(cors(corsOptions));
const token=jwt.sign({_id:user_id},process.env.JWT_SECRET,{expiresIn:"7d"});
res.cookie("token",token,{httpOnly:true});
hope it will work.