【问题标题】:Session id always changing for ASP.NET Core + Redis + nginxASP.NET Core + Redis + nginx 的会话 id 总是在变化
【发布时间】:2020-04-25 00:44:40
【问题描述】:

示例项目https://github.com/xingyu217/Redis-nginx-aspnetcore-session nginx.config:

upstream study_server{
        server localhost:8011 weight=1;
        server localhost:8013 weight=1;
    #server localhost:8014 weight=1;
    #server 172.17.16.147:8012 weight=2;
    #server 172.17.16.147:8011 weight=2;
    }
    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            #root   html;
            #index  index.html index.htm;
        proxy_pass http://study_server/;
            proxy_cookie_path ~*^/.* /;
        proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

获取会话ID:ViewBag.seId= HttpContext.Session.Id;

Startup.cs:

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            //services.AddDistributedMemoryCache();

            services.AddDistributedRedisCache(options =>
            {
                options.Configuration = Configuration.GetConnectionString("RedisConnection");
                options.InstanceName = "master";
            });
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromSeconds(100);
                //options.Cookie.HttpOnly = true;
            });
            //services.AddSession();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            //app.UseDeveloperExceptionPage();
            //app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseSession();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

访问URL:localhost,反复刷新,session id会一直在变化。

我检查了 redis 服务器中的密钥,它总是在刷新页面时生成新密钥。 (例如masterdef69307-fbd3-c6ed-91d2-009b2306f902)

如果我只使用服务器(localhost:8011):

upstream study_server{
        server localhost:8011 weight=1;
        #server localhost:8013 weight=1;
    #server localhost:8014 weight=1;
    #server 172.17.16.147:8012 weight=2;
    #server 172.17.16.147:8011 weight=2;
    }

会话 ID 不会更改。

任何人都知道它会不胜感激。

谢谢

【问题讨论】:

  • 试试this
  • @Rena 这也是我的主题。谢谢。

标签: nginx redis asp.net-core-mvc


【解决方案1】:

通过使用解决它

var redis = ConnectionMultiplexer.Connect("<REDIS URI>");

services.AddDataProtection().PersistKeysToRedis(redis, "DataProtection-Keys");

文章:

https://onecodex.ch/blogsdotnetcoredistributed-redis-cache/

必要的包:https://www.nuget.org/packages/Microsoft.AspNetCore.DataProtection.Redis/

【讨论】:

    【解决方案2】:

    根据这篇文章。 https://medium.com/@LuizAdolphs/asp-net-core-distributed-session-with-redis-8ce8bacf26cd

    正确的配置 Nginx 是:

    worker_processes 1;
    
    events { worker_connections 1024; }
    
    http {
    
        sendfile on;
    
        upstream app_server {
            server app_1:5000;
            server app_2:5050;
        }
    
        server {
            listen 80;
    
            location / {
                proxy_pass         http://app_server;
                proxy_redirect     off;
                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-Host $server_name;
            }
        }
    }
    

    请查看 X-Forwarded-Host。如果不能解决问题,尝试使用这个,可能是Redis配置的问题。

    【讨论】:

    • 如果它有效,那就奇怪了,因为请求来自同一个来源,它应该没有。除非会话“在配置中”附加到特定主机。
    【解决方案3】:

    您的会话似乎没有在您的后端(服务器)之间共享。

    您必须检查 cookie 的保存位置,以及该“位置”是否在您的多个服务器之间共享。

    如果您使用 redis :他们使用相同的 redis 吗?相同的命名空间? 如果是磁盘上的本地存储,它们是否共享相同的目录等。

    或者正如@Gomino 所说,您可以每次使用 nginx 粘性会话访问同一台服务器(当您有会话时)。但这与nginx配置无关。

    【讨论】:

    • 感谢您的回复。我正在使用相同的redis(在同一台机器上)。如何检查命名空间?我可以通过redis客户端(keys *)检查所有密钥,它会在刷新页面时生成一个新的密钥,比如master{guid}。 ip_hash是在nginx.config中配置的,什么意思和nginx配置无关?
    • Nginx 仅“代理”对网络服务器的请求,不是他管理会话(在您的情况下)。能不能禁用proxy_cookie_path配置,好像修改了cookie路径,所以cookie应该不会被webserver找到,可能是这个。
    • 关于redis命名空间,你可以在redis中定义一个“命名空间”来分隔键,你的两个网络服务器必须使用相同的,仅此而已。但是如果你告诉我们即使有两个服务器也只有一个会话密钥,那么配置一定是好的。
    • 尝试禁用proxy_cookie_path,结果还是一样。谢谢
    • 我认为您的问题来自您的会话管理到您的代码中,但很抱歉我是 .dot 开发人员。您应该尝试遵循@tematre 的文章目的。对不起;)
    【解决方案4】:

    我实际上无法在具有配置的mac上重现问题(2个本地服务器通过nginx负载平衡并连接到同一个本地redis分布式缓存)

    无论如何,如果适用,您是否尝试过在 nginx 上游模块中启用 ip_hash

    来自nginx doc 中的会话持久性部分:

    如果需要将客户端绑定到特定的应用程序服务器 - 换句话说,使客户端的会话“粘性”或“持久”,以始终尝试选择特定服务器 - ip-hash 负载平衡可以使用机制。

    upstream study_server{
        ip_hash;
        server localhost:8011 weight=1;
        server localhost:8013 weight=1;
        #server localhost:8014 weight=1;
        #server 172.17.16.147:8012 weight=2;
        #server 172.17.16.147:8011 weight=2;
    }
    

    如果这不能解决您的问题,请查看堆栈交换上的类似问题:

    【讨论】:

    • 感谢您的回复。 ip_hash 可以解决这个问题,但似乎总是在同一会话中使用服务器。无论如何,现在对我来说没问题。如果没有更好的解决方案,我会将您的回复标记为答案。非常感谢。
    • 很高兴为您提供帮助,您可以随时接受当前的答案,如果是这样的话,您可以改变主意换一个更好的答案;)
    【解决方案5】:

    如果CheckConsentNeeded 属性被分配了true,则应用程序不会在未经用户同意的情况下设置任何非必要的cookie。这包括会话 cookie,默认情况下它不是必需的。

    您的问题至少有三种解决方案:

    1. 您可以将CheckConsentNeeded 设置为false
        ...
    
        options.CheckConsentNeeded = context => false;
    
        ...
    

    这将禁用整个 cookie 同意功能。

    1. 您可以将会话 cookie 标记为必需。
        ...
    
        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromSeconds(100);
            options.Cookie.IsEssential = true;
        });
    
        ...
    

    这将允许应用程序设置会话 cookie,无论用户是否同意。

    1. 您可以将所有内容保持原样。在这种情况下,只有在用户单击 cookie 使用策略弹出窗口上的“接受”按钮后,会话才会开始。

    【讨论】:

    • 感谢您的回复。但是我尝试了这些选项,刷新时会话 id 仍然会更改(有时会话 id 是相同的)。这是提琴手跟踪github.com/xingyu217/fileshare。你能检查一下吗?根据日志,它会重置响应中的会话 cookie。
    • @starianchen-MSFT 我无法使用您提供的示例项目在本地复制问题。应用上述任何解决方案后,一切似乎都运行良好。你能提供你现在使用的程序的确切版本吗?当应用程序错误地启动新会话时,日志会说什么?是否可以临时切换到本地 Redis(这是一个很长的尝试,但仍然值得尝试)?
    • 你好lvan,我正在使用最新版本的项目github.com/xingyu217/Redis-nginx-aspnetcore-session。另外,我使用的是本地 Redis,但只需在 appsettings.json 中指定本地 IP。我也尝试使用 localhost:6379,结果相同。这些是我的步骤:1。在本地 IIS 中创建 website1 并指定已发布项目文件夹的路径。 2. 在本地 IIS 中创建另一个不同端口的 website2,并指定相同的路径。 3. 在本地 nginx 中指定这些地址并启动/重新加载 4. 在 Chrome/Edit/IE 浏览器中访问 localhost 5. 按 F5 刷新页面。 6.查看页面中的会话值。
    • 这些步骤和你的一样吗?您指的是哪个日志(开始一个新会话)。
    • 重要的是正如我所说,如果我只是在 nginx 中使用一个网站(任何一个),它就可以正常工作。服务器本地主机:8011 权重=1; #server localhost:8013 权重=1;如果我在 nginx 中使用多个网站就会有问题。
    猜你喜欢
    • 2017-05-05
    • 2016-07-28
    • 2018-09-21
    • 1970-01-01
    • 2016-02-11
    • 1970-01-01
    • 1970-01-01
    • 2021-03-02
    相关资源
    最近更新 更多