ASP.NET Identity实现分布式Session,Docker+Nginx+Redis+ASP.NET CORE Identity
零、背景介绍
在学习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的更多相关文章
- ASP.NET WebApi 基于分布式Session方式实现Token签名认证
一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...
- ASP.NET WebApi 基于分布式Session方式实现Token签名认证(发布版)
一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...
- Centos8 Docker+Nginx部署Asp.Net Core Nginx正向代理与反向代理 负载均衡实现无状态更新
首先了解Nginx 相关介绍(正向代理和反向代理区别) 所谓代理就是一个代表.一个渠道: 此时就涉及到两个角色,一个是被代理角色,一个是目标角色,被代理角色通过这个代理访问目标角色完成一些任务的过程称 ...
- asp.netcore 自动挡Docker Nginx Redis(滴滴滴,自动挡)
前言 上一章介绍了Docker通过多条命令创建启动运行Docker容器,由此可见这样一个个去创建单独的容器也是相当麻烦的,比如要在某个复杂项目中用DB.缓存.消息等等,这样我们还要去一个个再创建,为此 ...
- Spring Boot 分布式Session状态保存Redis
在使用spring boot做负载均衡的时候,多个app之间的session要保持一致,这样负载到不同的app时候,在一个app登录之后,而打到另外一台服务器的时候,session丢失. 常规的解决方 ...
- (38)Spring Boot分布式Session状态保存Redis【从零开始学Spring Boot】
[本文章是否对你有用以及是否有好的建议,请留言] 在使用spring boot做负载均衡的时候,多个app之间的session要保持一致,这样负载到不同的app时候,在一个app登录之后,而访问到另外 ...
- docker+nginx+redis部署前后端分离项目!!!
介绍本文用的经典的前后端分离开源项目.项目的拉取这些在另一篇博客!!! 其中所需要的前后端打包本篇就不做操作了!!不明白的去看另一篇博客!!! 地址:http://www.cnblogs.com/ps ...
- ASP.NET Core中间件实现分布式 Session
1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件的配置 1.2. 依赖注入中间件 1.3. Cookies ...
- ASP.NET Core中间件实现分布式 Session(转载)
ASP.NET Core中间件实现分布式 Session 1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件 ...
随机推荐
- Metasploit学习笔记——移动环境渗透测试
书364页配置假冒AP步骤,因为没有无线网卡,先跳过这个实验.
- IDEA报 : Lombok Requires Annotation Processing
Lombok Requires Annotation Processing Annotation processing seems to be disabled for the project &qu ...
- C语言表结构(1)
1.顺序表初始化实战: #include<stdio.h> #include<stdlib.h> #define OK 1 #define OVERFLOW 0 #define ...
- super關鍵字
super 关键字 我们知道,this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象. const proto = { foo: 'hello' ...
- 二十一 JDK注解&注解案例
什么是注解? 注解和接口,类属于同一个级别 注解可以在变量.方法.类上加载 注解可以有属性也可以没有属性 注解是有作用范围(源码.编译期间,运行期间) 源码期间:例如String类上@Author ...
- sqli-labs level 2
来到第第二关 首先在后面添加一个 单引号看下报错信息 发现这里多多了一个引号 尝试去掉单引号看下回显结果 : and 1=2 可以发现这里不需要添加单引号进行闭合,可以直接控制,所以接下来 ...
- shell教程——bash入门
创建shell文件 vim test.sh 写内容 #!/bin/bash echo "Hello World !" 使脚本具有执行权限 chmod +x ./test.sh 执行 ...
- 云时代架构阅读笔记十五——之前碰到的Java面试题
1.一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致. 2.Java有 ...
- spring#事件发布订阅
1. 如果在应用中发生了某些事件,事件会被拦截和处理就好了,这样就有了很大的灵活性,至少代码不会紧密的耦合在一起, 代码的解耦就是业务的解耦,业务A的代码不用手动的调用业务B的代码,业务B只需要监听相 ...
- 026、MySQL取字符串左边,取字符串右边,取字符串中间,取文本开始位置
#取文本左边 ); #田 ); #田攀 ); #田攀5 #取文本右边 ); # ); # ); #攀52 #取文本中间 '); #田攀 '); #攀5 #从字符串s中获取s1的开始位置 不忘初心,如果 ...