[ASP.NET MVC] ASP.NET Identity登入技术应用
[ASP.NET MVC] ASP.NET Identity登入技术应用
情景
ASP.NET Identity是微软所贡献的开源项目,用来提供ASP.NET的验证、授权等等机制。在ASP.NET Identity里除了提供最基础的:用户注册、密码重设、密码验证等等基础功能之外,也提供了进阶的:Cookie登入、Facebook登入、Google登入等等进阶功能。套用这些功能模块,让开发人员可以快速的在ASP.NET站台上,提供验证、授权等等机制。

但是在企业中,开发人员常常会遇到一种开发情景就是:企业里已经有一套既有身分系统,这个系统提供了:用户注册、密码重设、密码验证等等功能,新开发的ASP.NET站台,必须要串接既有身分系统来提供验证、授权等等机制。这个既有身分系统,可能是大卖场会员管理系统、也可能是银行帐户管理系统,它们的注册审核机制有一套严谨并且固定的流程。
在这样的开发情景中,开发人员可能会选择透过程序接口、OAuth机制等等方式,将既有身分系统整合成为ASP.NET Identity的验证提供者。这样两个系统之间的整合,除了有一定的高技术门坎之外。在整合之后,两个系统之间互相重迭的功能模块,操作流程的冲突该如何处理,也是一个需要额外考虑的复杂问题。

一个好消息是,ASP.NET Identity拥有高度模块化的软件架构。在ASP.NET Identity中,将Cookie登入、Facebook登入、Google登入等等功能模块,切割为独立的ASP.NET Security套件。开发人员完全可以直接套用ASP.NET Security套件,快速整合既有的身分系统,就可以提供ASP.NET站台所需的验证、授权等等机制。本篇文章介绍如何套用ASP.NET Security来整合既有身分系统,用以提供ASP.NET站台所需的验证、授权等等机制。主要为自己留个纪录,也希望能帮助到有需要的开发人员。

范例
范例程序代码:下载地址
开发
开始套用ASP.NET Security之前,先建立一个空白的MVC项目,来提供一个新的ASP.NET站台。并且变更预设的Web服务器URL为:「http://localhost:41532/」,方便完成后续的开发步骤。


再来在MVC项目里加入三个ASP.NET Security的NuGet套件参考:Microsoft.AspNet.Authentication、Microsoft.AspNet.Authentication.Cookies、Microsoft.AspNet.Authentication.Facebook。

接着建立AccountController以及相关的View,用以提供登入页面,让使用者可以选择使用哪种模式登入系统。
public class AccountController : Controller
{
// Methods
public IActionResult Login(string returnUrl = null)
{
// ViewData
this.ViewData["ReturnUrl"] = returnUrl;
// Return
return View();
}
}
再来在MVC项目内加入下列程序代码,用以挂载与设定后续要使用的两个CookieAuthenticationMiddleware。(关于程序代码的相关背景知识,请参阅技术剖析说明:ASP.NET Identity登入技术剖析)
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Authentication
services.AddAuthentication(options =>
{
options.SignInScheme = IdentityOptions.Current.ExternalCookieAuthenticationScheme;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Authentication
app.UseCookieAuthentication(options =>
{
options.AuthenticationScheme = IdentityOptions.Current.ApplicationCookieAuthenticationScheme;
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.LoginPath = new PathString("/Account/login");
});
app.UseCookieAuthentication(options =>
{
options.AuthenticationScheme = IdentityOptions.Current.ExternalCookieAuthenticationScheme;
options.AutomaticAuthenticate = false;
options.AutomaticChallenge = false;
options.LoginPath = null;
});
}
}
最后在MVC项目内,建立ExistingIdentitySystem这个类别用来仿真既有身分系统。为了方便理解系统,ExistingIdentitySystem里的PasswordSignIn(密码登入)、ExternalSignIn(第三方登入ex:FB登入)等方法都直接回传成功讯息,而GetUserById(取得使用者)这个方法则是直接回传固定的用户信息。(在正式环境开发时,上述方法可以实作为透过WebAPI、或是直接连通数据库等等方式,与既有身分系统取得相关信息。)
public class ExistingIdentitySystem
{
// Methods
public ExistingUser GetUserById(string userId)
{
// Result
var user = new ExistingUser();
user.Id = "Clark.Lab@hotmail.com";
user.Name = "Clark";
user.Birthday = DateTime.Now;
// Return
return user;
}
public bool PasswordSignIn(string userId, string password)
{
// Return
return true;
}
public bool ExternalSignIn(string userId, string externalProvider)
{
switch (externalProvider)
{
case "Facebook": return true;
default:
return true;
}
}
}
public class ExistingUser
{
// Properties
public string Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
开发 - Facebook Authentication
完成上述步骤后,接着着手开发Facebook验证。首先开发人员可以到Facebook开发者中心(https://developers.facebook.com/),注册一个新的APP账号。(测试用的Site URL为先前步骤定义的:「http://localhost:41532/」)

接着在MVC项目内加入下列程序代码,用以挂载与设定FacebookAuthenticationMiddleware。在这其中AppId、AppSecret是Facebook开发者中心提供的APP账号数据,而Scope、UserInformationEndpoint两个参数则是定义要额外取得用户的E-Mail信息。
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Authentication
app.UseFacebookAuthentication(options =>
{
options.AppId = "770764239696406";
options.AppSecret = "2eecc0b9ef785e43bcd4779e2803ba0f";
options.Scope.Add("email");
options.UserInformationEndpoint = "https://graph.facebook.com/v2.5/me?fields=id,name,email";
});
}
}
再来打开AccountController加入下列程序代码以及对应的View,用以提供ASP.NET站台处理Facebook这类的第三方登入(ExternalLogin)。在这其中,ExternalLogin用来发起一个验证挑战(Challenge),系统会依照externalProvider参数,来决定是要向Facebook或是其他第三方系统做验证。
当用户通过验证后,系统会调用ExternalLoginCallback来处理验证结果。在ExternalLoginCallback里会取得验证结果中FBUser的UserId,用来与ExistingIdentitySystem做验证。如果验证通过,会接着从ExistingIdentitySystem取得对应的ExistingUser、再转换为APPUser来真正登入系统。(关于程序代码的相关背景知识,请参阅技术剖析说明:ASP.NET Identity登入技术剖析)
public class AccountController : Controller
{
public IActionResult ExternalLogin(string externalProvider, string returnUrl = null)
{
// AuthenticationProperties
var authenticationProperties = new AuthenticationProperties();
authenticationProperties.Items.Add("ExternalProvider", externalProvider);
authenticationProperties.RedirectUri = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
// Return
return new ChallengeResult(externalProvider, authenticationProperties);
}
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
{
// AuthenticateContext
var authenticateContext = new AuthenticateContext(IdentityOptions.Current.ExternalCookieAuthenticationScheme);
await this.HttpContext.Authentication.AuthenticateAsync(authenticateContext);
// AuthenticateInfo
string userId = authenticateContext.Principal.FindFirst(ClaimTypes.Email).Value;
string externalProvider = authenticateContext.Properties["ExternalProvider"] as string;
// Login
var existingIdentitySystem = new ExistingIdentitySystem();
if (existingIdentitySystem.ExternalSignIn(userId, externalProvider) == false)
{
throw new InvalidOperationException();
}
// ExistingUser
var existingUser = existingIdentitySystem.GetUserById(userId);
if (existingUser == null) throw new InvalidOperationException();
// ApplicationUser
var applicationIdentity = new ClaimsIdentity(IdentityOptions.Current.ApplicationCookieAuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
applicationIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, existingUser.Id));
applicationIdentity.AddClaim(new Claim(ClaimTypes.Name, existingUser.Name));
var applicationUser = new ClaimsPrincipal(applicationIdentity);
// Cookie
await this.HttpContext.Authentication.SignInAsync(IdentityOptions.Current.ApplicationCookieAuthenticationScheme, applicationUser);
await this.HttpContext.Authentication.SignOutAsync(IdentityOptions.Current.ExternalCookieAuthenticationScheme);
// Return
return Redirect(returnUrl);
}
}
开发 - Password Authentication
完成上述步骤后,接着着手开发Password验证。打开AccountController加入下列程序代码以及对应的View,用以提供ASP.NET站台处理Password验证。在这其中,PasswordLogin会接收用户输入的账号密码,用来与ExistingIdentitySystem做验证。如果验证通过,会接着从ExistingIdentitySystem取得ExistingUser、再转换为APPUser来真正登入系统。(关于程序代码的相关背景知识,请参阅技术剖析说明:ASP.NET Identity登入技术剖析)
public class AccountController : Controller
{
public async Task<IActionResult> PasswordLogin(string userId, string password, string returnUrl = null)
{
// Login
var existingIdentitySystem = new ExistingIdentitySystem();
if (existingIdentitySystem.PasswordSignIn(userId, password) == false)
{
throw new InvalidOperationException();
}
// ExistingUser
var existingUser = existingIdentitySystem.GetUserById(userId);
if (existingUser == null) throw new InvalidOperationException();
// ApplicationUser
var applicationIdentity = new ClaimsIdentity(IdentityOptions.Current.ApplicationCookieAuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
applicationIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, existingUser.Id));
applicationIdentity.AddClaim(new Claim(ClaimTypes.Name, existingUser.Name));
var applicationUser = new ClaimsPrincipal(applicationIdentity);
// Cookie
await this.HttpContext.Authentication.SignInAsync(IdentityOptions.Current.ApplicationCookieAuthenticationScheme, applicationUser);
await this.HttpContext.Authentication.SignOutAsync(IdentityOptions.Current.ExternalCookieAuthenticationScheme);
// Return
return Redirect(returnUrl);
}
}
使用
完成开发步骤后,当系统执行到打上[Authorize]标签的Controller或是Action时,就会跳转到Login页面。
public class HomeController : Controller
{
[Authorize]
public IActionResult Contact()
{
ViewData["Message"] = "Hello " + User.Identity.Name + "!";
return View();
}
}


使用 - Facebook Authentication
在Login页面,当使用者选择使用Facebook验证,系统会跳转到Facebook页面进行验证与授权。完成验证授权的相关步骤后,使用者就可以进入被打上[Authorize]标签的Controller或是Action。



使用 - Password Authentication
在Login页面,当使用者选择使用Password验证,系统会使用Login页面上输入的账号密码来进行验证与授权。完成验证授权的相关步骤后,使用者就可以进入被打上[Authorize]标签的Controller或是Action。


范例
范例程序代码:下载地址
[ASP.NET MVC] ASP.NET Identity登入技术应用的更多相关文章
- [ASP.NET MVC] ASP.NET Identity登入技术剖析
[ASP.NET MVC] ASP.NET Identity登入技术剖析 前言 ASP.NET Identity是微软所贡献的开源项目,用来提供ASP.NET的验证.授权等等机制.本篇文章介绍ASP. ...
- [ASP.NET MVC] ASP.NET Identity学习笔记 - 原始码下载、ID型别差异
[ASP.NET MVC] ASP.NET Identity学习笔记 - 原始码下载.ID型别差异 原始码下载 ASP.NET Identity是微软所贡献的开源项目,用来提供ASP.NET的验证.授 ...
- [Asp.net MVC]Asp.net MVC5系列——添加视图
目录 系列文章 概述 添加视图 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 概述 在这一部分我们添加一个新的控制器HelloWorldController类, ...
- [Asp.net MVC]Asp.net MVC5系列——在模型中添加验证规则
目录 概述 在模型中添加验证规则 自定义验证规则 伙伴类的使用 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 [Asp.net MVC]Asp.net MVC5 ...
- [Asp.net MVC]Asp.net MVC5系列——添加模型
目录 概述 添加模型 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 [Asp.net MVC]Asp.net MVC5系列——添加视图 概述 在本节中我们将追加 ...
- [Asp.net MVC]Asp.net MVC5系列——从控制器访问模型中的数据
目录 概述 从控制器访问模型中的数据 强类型模型与@model关键字 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 [Asp.net MVC]Asp.net M ...
- [Asp.net MVC]Asp.net MVC5系列——添加数据
目录 概述 显示添加数据时所用表单 处理HTTP-POST 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 [Asp.net MVC]Asp.net MVC5系列 ...
- [Asp.net MVC]Asp.net MVC5系列——布局视图
目录 系列文章 概述 布局视图 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 [Asp.net MVC]Asp.net MVC5系列——添加视图 [Asp.net M ...
- Asp.net MVC]Asp.net MVC5系列——Routing特性
目录 概述 路由特性 使用路由 可选参数和参数的默认值 路由前缀 默认路由 路由约束 自定义路由约束 路由名 区域(Area) 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列— ...
随机推荐
- PHP分布式中Redis实现Session
方法一:找到配置文件php.ini,修改为下面内容,保存并重启服务 session.save_handler = redis session.save_path = "tcp://127.0 ...
- HTTP Status 500 - The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application
j 今天下午一直报这个问题,google了半天没有找到答案.百度了下,说是 tomcat的 lib文件夹下缺少jstl1.2,因为项目里面用的也是 jstl1.2和 standard-1.1.2.ja ...
- Windows下安装python2和python3双版本
现在大家常用的桌面操作系统有:Windows.Mac OS.ubuntu,其中Mac OS 和 ubuntu上都会自带python.这里我们只介绍下Windows(我用的Win10)环境下的pytho ...
- [ASP.NET] 如果将缓存“滑动过期时间”设置为1秒会怎样?
今天编写了一个采用ASP.NET Caching的组件,在为它编写Unit Test的过程中发现了一个有趣的问题,接下来我通过一个简单的实例说明这个问题.我们在一个控制台应用中编写了如下一段程序,这个 ...
- javascript之一切皆为对象2
其实呢,“函数function”和“对象object”之间还有这么一句话:对象是通过函数来创建的,而函数却又是一种对象. 这个函数是一种对象,上节中“Javascript之一切皆为对象1”也清楚的阐述 ...
- LTP随笔——本地调用ltp之ltp4j
关于ltp本地调用的相关参考请见LTP的Git项目:https://github.com/HIT-SCIR 以下以/home/lion/Desktop路径为例下面教程中出现的具体路径以你实际配置的为准 ...
- Android随笔之——闹钟制作铺垫之AlarmManager详解
说实话,之前写的两篇博客Android广播机制Broadcast详解.Android时间.日期相关类和方法以及现在要写的,都算是为之后要写的闹钟应用做铺垫,有兴趣的话,大家可以去看看前两篇博客. 一. ...
- UWP开发之Mvvmlight实践一:如何在项目中添加使用Mvvmlight(图文详解)
最近一直在做UWP开发,为了节省成本等等接触到MVVMlight,觉得有必要发点时间研究它的用法与实现原理才行.如果有问题的地方或者有好的建议欢迎提出来. 随着移动开发的热门,Mvvmlight在An ...
- 构建自己的PHP框架--构建缓存组件(2)
上一篇博客中使用文件实现了缓存组件,这一篇我们就使用Redis来实现一下,剩下的如何使用memcache.mysql等去实现缓存我就不一一去做了. 首先我们需要安装一下 redis 和 phpredi ...
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...