零、背景介绍

在学习ASP.NET CORE开发的过程中,身份认证是必须考虑的一项必要的组件。ASP.NET CORE Identity是由微软官方开发的一整套身份认证组件,兼具完整性和自由度。Docker作为目前虚拟化的主流解决方案,可以很快捷地实现应用的打包和部署。Nginx作为反向代理,结合Docker多环境部署,可以实现负载均衡功能。而在分布式环境下,Session的共享,特别是登录状态的共享是难以逾越的一个“小”问题。

然而,这个“小”问题,却让我花费了大量的时间搞清楚了相互之间的协作关系,并成功实现了Docker+Nginx+Redis多种组件相结合的解决方案。

环境:ASP.NET Core 2.0

一、ASP.NET CORE Identity

为了实现Session共享,需要在Cookie中存储Session的ID信息以及用户信息,从而实现在多个应用之间的信息共享。有关ASP.NET CORE Identity的介绍,这里不在赘述。

ASP.NET CORE Identity主要包括UserManager和SignInManager两个主要的管理类,从名称可以看出来SignInManager实现的是登陆的管理,因为涉及到登录状态以及登录用户信息的共享,所以我们需要实现自定义的SignInManager类,重写其中最为重要的登录和登出方法。

 public override Task<SignInResult> PasswordSignInAsync(ApplicationUser user, string password, bool isPersistent, bool lockoutOnFailure)
{
return base.PasswordSignInAsync(user, password, isPersistent, lockoutOnFailure)
.ContinueWith<SignInResult>(task =>
{
if (task.Result == SignInResult.Success)
{
LoginSucceeded(user);
} return task.Result;
});
} public override Task<SignInResult> TwoFactorAuthenticatorSignInAsync(string code, bool isPersistent, bool rememberClient)
{
ApplicationUser au = this.GetTwoFactorAuthenticationUserAsync().Result;
return base.TwoFactorAuthenticatorSignInAsync(code, isPersistent, rememberClient)
.ContinueWith<SignInResult>(task =>
{
if (task.Result == SignInResult.Success && au != null)
{
LoginSucceeded(au);
} return task.Result;
});
} public override Task SignOutAsync()
{
return base.SignOutAsync()
.ContinueWith(task =>
{
LogoutSucceeded(Context.Request.Cookies["sessionId"]);
}); ;
} public override bool IsSignedIn(ClaimsPrincipal principal)
{
if (!Context.User.Identity.IsAuthenticated)
{
if (Context.Request.Cookies.ContainsKey("sessionId"))
{
string userInfor = Context.Session.GetString(Context.Request.Cookies["sessionId"]);
if (!string.IsNullOrEmpty(userInfor))
{
ApplicationUser user = JsonConvert.DeserializeObject<ApplicationUser>(userInfor);
if (user != null)
{
principal = Context.User = this.ClaimsFactory.CreateAsync(user).Result;
}
}
}
} var flag = base.IsSignedIn(principal); return flag;
} private void LoginSucceeded(ApplicationUser user)
{
try
{
string sessionId = Guid.NewGuid().ToString();
string userInfor = JsonConvert.SerializeObject(user);
Context.Session.SetString(sessionId, userInfor);
Context.Response.Cookies.Delete("sessionId");
Context.Response.Cookies.Append("sessionId", sessionId);
}
catch (Exception xcp)
{
MessageQueue.Enqueue(MessageFactory.CreateMessage(xcp));
}
} private void LogoutSucceeded(string sessionId)
{
try
{
if (!string.IsNullOrEmpty(sessionId))
{
Context.Session.Remove(sessionId);
}
}
catch (Exception xcp)
{
MessageQueue.Enqueue(MessageFactory.CreateMessage(xcp));
}
}

MySignInManager

   重写之后,需要在Startup.cs代码ConfigureServices方法中注册使用。

            services.AddIdentity<ApplicationUser, IdentityRole>(o =>
{
o.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<MyDbContext>()
.AddSignInManager<MySignInManager>()
.AddDefaultTokenProviders();

二、Docker

    在实现自定义Identiy中的SignInManager类以后,将网站打包为Docker镜像(Image),然后根据需要运行多个容器(Container),这些容器的功能是相同的,其实是多个网站实例,跑在不同的端口上面,相当于实现了分布式部署。比如,运行三个容器的命令如下,分别跑在5000,5001和5002端口。

docker run --name webappdstr_0 -d -p : -v /etc/localtime:/etc/localtime webapp:1.0
docker run --name webappdstr_1 -d -p : -v /etc/localtime:/etc/localtime webapp:1.0
docker run --name webappdstr_2 -d -p : -v /etc/localtime:/etc/localtime webapp:1.0

三、Nginx

在完成应用部署后,通过修改Nginx配置,实现负载均衡功能。主要配置如下:

    # WebAppDistributed
server{
listen ssl;
server_name www.webapp.com; ssl_certificate ../cert/ssl.crt;
ssl_certificate_key ../cert/ssl.key; location / {
proxy_pass http://webappserverd/;
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;
}
} upstream webappserverd{
server localhost:;
server localhost:;
server localhost:;
}

Nginx config

     以上配置5000,5001和5002三个应用,即对应于Docker部署的三个网站应用。

四、Redis

    Redis主要是实现Session的共享,通过Microsoft.Extensions.Caching.Redis.Core组件(通过Nuget获取),在Startup.cs代码ConfigureServices方法中添加Redis中间件服务。

            // Redis
services.AddDistributedRedisCache(option =>
{
//redis 数据库连接字符串
option.Configuration = Configuration.GetConnectionString("RedisConnection");
//redis 实例名
option.InstanceName = "master";
});

Redis的地址获取的是appsettings.json配置中的配置项。

{
"ConnectionStrings": {
...,
"RedisConnection": "192.168.1.16:6379"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}

五、重点难点

主要是总结下碰到的各种坑以及解决方案。

1、Session共享的需要DataProtection以及相关的配置支持

ASP.NET CORE对Session进行了加密,为了能够在多个分布式应用中实现共享,则需要使用相同的加密Key。实现共享的方式有多种,这里采用自定义XmlRepository来实现。

 public class CustomXmlRepository : IXmlRepository
{
private readonly string keyContent = @""; //使用前,插入key内容 public virtual IReadOnlyCollection<XElement> GetAllElements()
{
return GetAllElementsCore().ToList().AsReadOnly();
} private IEnumerable<XElement> GetAllElementsCore()
{
yield return XElement.Parse(keyContent);
}
public virtual void StoreElement(XElement element, string friendlyName)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
StoreElementCore(element, friendlyName);
} private void StoreElementCore(XElement element, string filename)
{
}
}

CustomXmlRepository

之后,在Startup.cs中启用DataProtection中间件,并进行配置。

             // Set data protection.
services.AddDataProtection(configure =>
{
configure.ApplicationDiscriminator = "WebApplication";
})
.SetApplicationName("WebApplication")
.AddKeyManagementOptions(options =>
{
//配置自定义XmlRepository
options.XmlRepository = new CustomXmlRepository();
})
.ProtectKeysWithCertificate(new System.Security.Cryptography.X509Certificates.X509Certificate2("webapp.crt"));

Startup.cs

ASP.NET Identity实现分布式Session,Docker+Nginx+Redis+ASP.NET CORE Identity的更多相关文章

  1. ASP.NET WebApi 基于分布式Session方式实现Token签名认证

    一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...

  2. ASP.NET WebApi 基于分布式Session方式实现Token签名认证(发布版)

    一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...

  3. Centos8 Docker+Nginx部署Asp.Net Core Nginx正向代理与反向代理 负载均衡实现无状态更新

    首先了解Nginx 相关介绍(正向代理和反向代理区别) 所谓代理就是一个代表.一个渠道: 此时就涉及到两个角色,一个是被代理角色,一个是目标角色,被代理角色通过这个代理访问目标角色完成一些任务的过程称 ...

  4. asp.netcore 自动挡Docker Nginx Redis(滴滴滴,自动挡)

    前言 上一章介绍了Docker通过多条命令创建启动运行Docker容器,由此可见这样一个个去创建单独的容器也是相当麻烦的,比如要在某个复杂项目中用DB.缓存.消息等等,这样我们还要去一个个再创建,为此 ...

  5. Spring Boot 分布式Session状态保存Redis

    在使用spring boot做负载均衡的时候,多个app之间的session要保持一致,这样负载到不同的app时候,在一个app登录之后,而打到另外一台服务器的时候,session丢失. 常规的解决方 ...

  6. (38)Spring Boot分布式Session状态保存Redis【从零开始学Spring Boot】

    [本文章是否对你有用以及是否有好的建议,请留言] 在使用spring boot做负载均衡的时候,多个app之间的session要保持一致,这样负载到不同的app时候,在一个app登录之后,而访问到另外 ...

  7. docker+nginx+redis部署前后端分离项目!!!

    介绍本文用的经典的前后端分离开源项目.项目的拉取这些在另一篇博客!!! 其中所需要的前后端打包本篇就不做操作了!!不明白的去看另一篇博客!!! 地址:http://www.cnblogs.com/ps ...

  8. ASP.NET Core中间件实现分布式 Session

    1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件的配置 1.2. 依赖注入中间件 1.3. Cookies ...

  9. ASP.NET Core中间件实现分布式 Session(转载)

    ASP.NET Core中间件实现分布式 Session 1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件 ...

随机推荐

  1. 《N诺机试指南》(一)数组妙用

    题目A: 大家思路有可能是这样: 将输入数据全部存储到一个数组里,然后可以冒泡排序一波,从小到大排序 那么怎么找到重复次数呢:我是这样想的,新定义数组b,原数组a,首先b[0] = a[0],定义指针 ...

  2. 105、Java中String类之利用indexOf()方法判断子字符串是否存在

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  3. ROS-5 : 自定义消息

    自定义消息一般存储在功能包的msg文件夹下的.msg文件中,这些定义可告诉ROS这些数据的类型和名称,以便于在ROS 节点中使用.添加完这些自定义消息后,ROS会将其转为等效的C++节点,从而可在其他 ...

  4. python-python基础1(变量、判断、循环、模块、数据运算)

    一.变量 name=input("name:") age=input("age:") job=input("job:") info=''' ...

  5. IDEA激活方法(Linux和Windows通用)

    一.前言 idea是一款十分智能的编程软件,有能力的同志们还是尽量支持正版. 二.激活流程 话不多说,开始教程 2.1 下载激活工具包 链接:https://pan.baidu.com/s/1nj3w ...

  6. Golang:GOROOT、GOPATH、GOBIN变量的含义

    背景 移植完了go以后,在配置变量的时候不太清楚这些变量的含义,找了有关的资料. 使用 go env可以获取 go 有关的环境变量,下面是我的go环境: GO111MODULE="" ...

  7. Centos7下 Oracle11G自动备份

    1.创建备份目录: [root@Centos ~]# mkdir -p /home/oracle/backup 2.设置目录权限: [root@Centos ~]# chown -R oracle:o ...

  8. 我的Grunt之旅-序章

    时间:2018-03-05 13:52  事件:安装Grunt 相关网址: grunt官网:https://gruntjs.com node.js下载地址 :https://nodejs.org/en ...

  9. NPOI读取excel 空行

    if (sheet.GetRow(i) != null) 每行判断一下,避免出错.真是蛋疼.

  10. 移动 web 开发问题和优化小结

    1.前言 到目前为止,互联网行业里,手机越来越智能化,移动端占有的比例越来越高,尤其实在电商,新闻,广告,游戏领域.用户要求越来越高,网站功能越来越好,效果越来越炫酷,这就要求我们产品质量越来越高,w ...