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. 中间件 ...
随机推荐
- Day9 - J - 吉哥系列故事——恨7不成妻 HDU - 4507
单身! 依然单身! 吉哥依然单身! DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌! 吉哥观察了214和77这两个数,发现: 2+1+4=7 7+7=7*2 77=7 ...
- Day3-L-Cup HDU2289
The WHU ACM Team has a big cup, with which every member drinks water. Now, we know the volume of the ...
- JavaScript之this的用法
本文我们介绍下js中this的用法. 由上图可得,默认this指向window,而在node.js中this默认指向global. 由上图可得: 1.原型链为o->MyClass.prototy ...
- IDEA spring mvc整合mybatis
准备工作 IDEA 2019.3.1 MySql 8.0.17 Tomcat 7.0.9 开始步骤 一.创建一个项目,添加Web支持 点击菜单:File->NEW->Project 选择左 ...
- 前端学习笔记系列一:3 Vue中的nextTick
一.示例 先来一个示例了解下关于Vue中的DOM更新以及nextTick的作用. 模板 <div class="app"> <div ref="msgD ...
- MongoDB分片技术原理和高可用集群配置方案
一.Sharding分片技术 1.分片概述 当数据量比较大的时候,我们需要把数分片运行在不同的机器中,以降低CPU.内存和Io的压力,Sharding就是数据库分片技术. MongoDB分片技术类似M ...
- 配置antMatchers(HttpMethod.GET,"/**").permitAll()当时仍然会校验
.antMatchers(HttpMethod.GET,"/**").permitAll() .anyRequest().authenticated() .and() .addFi ...
- NO34 awk
- arm linux 移植 PHP
背景: PHP 是世界上最好的语言. host平台 :Ubuntu 16.04 arm平台 : 3531d arm-gcc :4.9.4 php :7.1.30 zlib :1.2.11 libxml ...
- SPFA和堆优化的Dijk
朴素dijkstra时间复杂度$O(n^{2})$,通过使用堆来优化松弛过程可以使时间复杂度降到O((m+n)logn):dijkstra不能用于有负权边的情况,此时应使用SPFA,两者写法相似. 朴 ...