grpc-web架构下Set-Cookie无法在浏览器中生效的问题排查
架构配置
- 客户端:react-app(grpc-web),地址
192.168.3.2:3000 - 代理服务器:Envoy,部署在虚拟机,地址
192.168.233.129:8080 - 后端:java-auth-ms(gRPC),地址
192.168.3.2:8101
客户端调用代码
const grpcPromise = new Promise((resolve, reject) => { global.grpcAPI.AccountService.loginUser( request, { withCredentials: "true" }, (err, response) => { if (!err) { resolve({ status: response.getStatus(), msg: response.getMsg(), }); } else { reject(err); } } ); });
Envoy代理配置
admin: access_log_path: /tmp/admin_access.log address: socket_address: address: 0.0.0.0 port_value: 9901 static_resources: listeners: - name: listener_0 address: socket_address: address: 0.0.0.0 port_value: 8080 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/" route: cluster: grpc_backend timeout: 0s max_stream_duration: grpc_timeout_header_max: 0s cors: allow_origin_string_match: - prefix: "*" allow_methods: GET, PUT, DELETE, POST, OPTIONS allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout,withcredentials,set-cookie allow_credentials: true max_age: "1728000" expose_headers: custom-header-1,grpc-status,grpc-message,set-cookie http_filters: - name: envoy.filters.http.grpc_web typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb - name: envoy.filters.http.cors typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: grpc_backend connect_timeout: 2.25s type: logical_dns typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: {} lb_policy: round_robin load_assignment: cluster_name: grpc_backend endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 192.168.3.2 #host-machine (java-auth-ms) port_value: 8101
Java后端gRPC拦截器代码
@Override public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall( ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { ServerCall<ReqT, RespT> wrappedCall = new ForwardingServerCall.SimpleForwardingServerCall<>(call) { @Override public void sendHeaders(Metadata responseHeaders) { Metadata.Key<String> SET_COOKIE_HEADER = Metadata.Key.of("Set-Cookie", Metadata.ASCII_STRING_MARSHALLER); String jwtToken = getTokenFromContext(); // Получаем токен из контекста String cookie = "token=" + "30ha" + "; HttpOnly; Path=/; Max-Age=" + 24 * 3600; //24 responseHeaders.put(SET_COOKIE_HEADER, cookie); super.sendHeaders(responseHeaders); } }; return next.startCall(wrappedCall, headers); } private String getTokenFromContext() { return TokenConstants.TOKEN_KEY.get(); }
请求与响应详情
请求头
POST /project_d.grpcAPI.Account.Account/loginUser HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7 Cache-Control: no-cache Connection: keep-alive Content-Length: 22 Content-Type: application/grpc-web+proto Host: 192.168.233.129:8080 Origin: http://192.168.3.2:3000 Pragma: no-cache Referer: http://192.168.3.2:3000/ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 X-Grpc-Web: 1 X-User-Agent: grpc-web-javascript/0.1 withCredentials: true
响应头
HTTP/1.1 200 OK content-type: application/grpc-web+proto set-cookie: token=30ha; HttpOnly; Path=/; Max-Age=86400 grpc-encoding: identity grpc-accept-encoding: gzip x-envoy-upstream-service-time: 5 access-control-allow-origin: http://192.168.3.2:3000 access-control-allow-credentials: true access-control-expose-headers: custom-header-1,grpc-status,grpc-message,set-cookie date: Thu, 19 Sep 2024 13:33:32 GMT server: envoy transfer-encoding: chunked
问题描述
响应头中已包含带Token的Set-Cookie字段,但浏览器无法将其存入存储,添加SameSite标识后问题依旧,求原因。
问题原因及解决方案
Cookie Domain属性不匹配
当前Set-Cookie未指定Domain,浏览器默认将其绑定到请求的Host(192.168.233.129),但客户端运行在192.168.3.2,跨域场景下Domain不匹配,浏览器拒绝存储Cookie。需在Cookie中指定客户端所在IP作为Domain:String cookie = "token=30ha; HttpOnly; Path=/; Max-Age=86400; Domain=192.168.3.2";grpc-web请求参数类型错误
客户端代码中withCredentials传的是字符串"true",而grpc-web要求该参数为布尔值true。虽然部分环境兼容字符串,但标准写法应为布尔值,否则可能导致浏览器不识别该参数,进而忽略Cookie存储:{ withCredentials: true }SameSite属性需结合环境设置
跨域场景下,若为HTTP测试环境,建议设置SameSite=Lax(SameSite=Strict会限制跨域请求携带Cookie);若为HTTPS环境,设置SameSite=None; Secure。注意Secure仅在HTTPS下有效,HTTP环境下浏览器会拒绝存储带SameSite=None的Cookie:// HTTP环境 String cookie = "token=30ha; HttpOnly; Path=/; Max-Age=86400; Domain=192.168.3.2; SameSite=Lax"; // HTTPS环境 String cookie = "token=30ha; HttpOnly; Path=/; Max-Age=86400; Domain=192.168.3.2; SameSite=None; Secure";
内容的提问来源于stack exchange,提问作者L0mchansky
相关产品推荐
相关产品推荐

