【译】Asp.Net Identity与Owin,到底谁是谁?
送给正在学习Asp.Net Identity的你 :-)
原文出自 trailmax 的博客AspNet Identity and Owin. Who is who.
Recently I have found an excellent question on Stackoverflow. The OP asks why does claim added to Idenetity after calling AuthenticationManager.SignIn
still persist to the cookie.
最近我在StackOverflow发现一个非常好的(妙啊)问题,OP提出的问题是,在调用AuthenticationManager.SignIn
之后再向Identity添加Claim ,这些后添加的Claim仍然被保存在了Cookies里。
The sample code was like this:
代码是这样的
ClaimsIdentity identity = UserManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie );
var claim1 = new Claim(ClaimTypes.Country, "Arctica");
identity.AddClaim(claim1);
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = true }, identity );
var claim2 = new Claim(ClaimTypes.Country, "Antartica");
identity.AddClaim(claim2);
Yeah, why does claim2 is available after cookie is already set.
噢耶,为什么在已经设置cookie之后claim2仍然是有效的呢?
After much digging I have discovered that AspNet Identity framework does not set the cookie. OWIN does. And OWIN is part of Katana Project which has open source code. Having the source code available is always nice – you can find out yourself why things work or don’t work the way you expect.
在我深度挖掘之后,我发现 AspNet Identity framework 并没有真的去设置cookie,是Owin做的。Owin是开源项目Katana的一部分(译者注:katana微软对于Owin的实现,Owin的实现还有其它的)。能看源代码总是一件很棒的事,因为你能自己去寻找为什么事情是这样的或者为什么结果和预期的不一致的答案。
In this case I have spent a few minutes navigating Katana project and how AuthenticationManager works. Turned out that SingIn method does not set a cookie. It saves Identity objects into memory until time comes to set response cookies. And then claims are converted to a cookie and everything magically works :-)
在这件事里,我花了几分钟时间找到Katana的源码寻找AuthenticationManager
的工作原理。原来SignIng
方法并没有设置Cookie。直到设置相应cookie之前,它只是将Identity对象放在了内存里。然后 claims被转换成cookie,并且,神奇的事情发生了:-D(应该是这么翻译吧...)
译者注:下面是SignIn的源码,源码地址
public void SignIn(AuthenticationProperties properties, params ClaimsIdentity[] identities)
{
AuthenticationResponseRevoke priorRevoke = AuthenticationResponseRevoke;
if (priorRevoke != null)
{
// Scan the sign-outs's and remove any with a matching auth type.
string[] filteredSignOuts = priorRevoke.AuthenticationTypes
.Where(authType => !identities.Any(identity => identity.AuthenticationType.Equals(authType, StringComparison.Ordinal)))
.ToArray();
if (filteredSignOuts.Length < priorRevoke.AuthenticationTypes.Length)
{
if (filteredSignOuts.Length == 0)
{
AuthenticationResponseRevoke = null;
}
else
{
AuthenticationResponseRevoke = new AuthenticationResponseRevoke(filteredSignOuts);
}
}
}
AuthenticationResponseGrant priorGrant = AuthenticationResponseGrant;
if (priorGrant == null)
{
AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identities), properties);
}
else
{
ClaimsIdentity[] mergedIdentities = priorGrant.Principal.Identities.Concat(identities).ToArray();
if (properties != null && !object.ReferenceEquals(properties.Dictionary, priorGrant.Properties.Dictionary))
{
// Update prior properties
foreach (var propertiesPair in properties.Dictionary)
{
priorGrant.Properties.Dictionary[propertiesPair.Key] = propertiesPair.Value;
}
}
AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(mergedIdentities), priorGrant.Properties);
}
}
This sparked another question. At the moment Identity does not have open source, but what is the role of OWIN in Identity and how Claims work here?
这引发了另一个问题,此时 Identity还没有开源,但是Identity中OWIN的Role是什么,并且,Claims是如何工作(译者注:发挥作用)的?
Turns out that Identity framework deals only with user persistence, password hashing, validating if the password is correct, sending out email tokens for password reset, etc. But Identity does not actually authenticate users or create cookies. Cookies are handled by OWIN.
原来Identity framework做的事只有用户持久化存储、密码Hash、验证密码是否是正确的、为重置密码发送带有Token的电子邮件等等。但是identity 没有真正的认证用户或者创建Cookie。Owin在处理Cookie。
Take a look on this code for signing in:
我们来看一下SignIng的代码:
public async Task SignInAsync(Microsoft.Owin.Security.IAuthenticationManager authenticationManager, ApplicationUser applicationUser, bool isPersistent)
{
authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
ClaimsIdentity identity = await UserManager.CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new Microsoft.Owin.Security.AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
Identity only creates ClaimsIdentity
which you can study on ReferenceSource site. And ClaimsIdentity
is part of .Net framework, not some nuget-package from the interwebz. Then this claimsIdentity is passed to OWIN AuthenticationManager
where a callback is set to assign cookies on when time comes to write headers on the response.
Identity只是创建了ClaimsIdentity
,你可以在ReferenceSource (译者注:查看.Net源码的地方,微软官办,没保存的同学快快收藏吧!)上学习他的代码。而且ClaimsIdentity
是.Net Freamwork的一部分,不是从互联网上下载的Nuget包(实在不懂interwebz是什么意思 Orz)。然后claimsIdentity
被传给了OWIN AuthenticationManager
,(断句)OWIN AuthenticationManager
是设置在派送Cookie(assign cookies)上的一个Callcack回调,(断句)这个assign cookies是放在一个什么东西上,当到了向Response写Header的时候就搞这个东西(这句话太难翻译了,你一定要看看原话,我不保证这句话翻译正确 (:逃)
So far, so good, we have 3 parts here: Identity framework creating a ClaimsIdentity
, OWIN creating a cookie from this ClaimsIdentity
. And .Net framework which holds the class for ClaimsIdentity
.
目前看来还不错。我们知道了这里有3个部分:
- Identity framework 创建了一个
ClaimsIdentity
, - Owin为
ClaimsIdentity
创建了cookie - 持有
ClaimsIdentity
的class的是.Net framework
When in your classes you access ClaimsPrincipal.Current
, you only use .Net framework, no other libraries are used. And this is very handy!
当你在你的代码中访问ClaimsPrincipal.Current
时,你只用的了.Net framework,没有其它库(library)被用到,这是非常方便的。
Default Claims
Identity framework does a nice thing for you. By default it adds a number of claims to a principal when user is logged in. Here is the list:
Identity framework帮你做了很多有用的事。默认情况下,在用户登录的时候,她会添加一些claims到principal(译者注:看一下Mvc中的HttpContext.User
的类型,就是IPrincipal
)。接下来是列表:
User.Id
is added as claim type “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier” or byClaimTypes.NameIdentifier
User.Id
的claims类型是“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier” 或者通过ClaimTypes.NameIdentifier
指定(译者注:ClaimTypes.NameIdentifier
的定义如下public const string NameIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
)Username is added as claim type “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name” or by ClaimTypes.Name
UserName 的claims类型是“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name” 或者由ClaimTypes.Name指定
“ASP.NET Identity” is saved as claim type “http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider”. This is useful if you are using OpenId to do the authentication. Not much use if you are using only users stored in our database. See this page for more details.
“ASP.NET Identity”对应的claim类型type是“http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider”。如果你使用OpenId来做认证 (authentication),那么它很有用,如果你只是用你自己的数据库来存储用户信息,那么它对你没有太大帮助。相关信息请查阅此页面。
Guid containing User’s Security Stamp is persisted in claim with type “AspNet.Identity.SecurityStamp“. Security Stamp is basically a snapshot of user state. If password/method of authentication, email, etc. is changed, Security Stamp is changed. This allows to “logout everywhere” on credentials change. Read more about what is security stamp in Hao Kung’s answer.
包含用户Security Stamp的Guid被存在type为“AspNet.Identity.SecurityStamp”的claim里。 Security Stamp 总的来说是用户状态(user state)快照(snapshot),如果用于认证的 密码/method (译者注:用于认证的其他方式)、email等等这些信息改变了。Security Stamp也会变。这就允许当credentials(凭证)改变时,在所有已登录的地方登出。了解更多请关于Security Stamp 请看Hao Kung(看起来像个中国人)在SO上的答案。
The most useful claims added by default are role. All roles assigned to the user are saved as ClaimTypes.Role or by “http://schemas.microsoft.com/ws/2008/06/identity/claims/role“. So next time you need to check current user’s roles, check the claims. This does not hit the database and is very quick. And in fact, if you call .IsInRole("RoleName") on ClaimsPrincipal, the framework goes into claims and checks if Role claims with this value is assigned.
默认添加的最有用的claims就是role了,所有赋予这个用户的的role都被存储为type为 ClaimTypes.Role 或者 “http://schemas.microsoft.com/ws/2008/06/identity/claims/role”。所以以后你想检查当前用户的Role你就可以直接检查这些claims。(译者注:在登陆时存储在Identity数据库中的所有关于user的role,以及claim会被转换成claim。IsInRole方法也是在判断claim,详情可查阅源代码)。这些操作都不访问数据库所以非常快。而且事实上,如果你使用.IsInRole("RoleName") on ClaimsPrincipal,框架会去检查claim中的role claim是否含有指定的值。
You can find the list of framework claim types on .Net Reference site. However, this list is not complete. You can make up your own claim types as you are pleased – this is just a string.
你能够在.Net Reference网站上找到claim type的列表。然而,这个列表并不完整。如果你喜欢,你完全可以制造你自己的claim type,因为它只是一个字符串而已。
If you want to add your own claim types, I recommend to use your own notation for the claim types. Something like “MyAppplication:GroupId” and keep all the claim types in one class as constants:
如果你想添加你自己的claim type ,我建议你在claim type上加上你自己独特的标记。比如:“MyAppplication:GroupId”,然后将它们作为 constants存在某个类里面。
public class MyApplicationClaimTypes
{
public string const GroupId = "MyAppplication:GroupId";
public string const PersonId = "MyAppplication:PersonId";
// other claim types
}
This way you can always find where the claims are used and will not clash with the framework claim types. Unless the claim you use matches framework claim types exactly, like ClaimTypes.Email.
这种方式让你能够找到哪里引用了这些claims,并且不会和framework中的claim type发生冲突。除非你使用的claim type的framework中的claim type精确匹配,例如:ClaimTypes.Email
。
Adding default claims 添加默认的claims
I always add user’s email to the list of claims. I do that on user sign-in, same way the first code snippet adds claim1 and claim2:
我总是使用和第一个添加claim1和claim2相同的方式在用户登录时将用户的email添加到claims列表里。
public async Task SignInAsync(IAuthenticationManager authenticationManager, ApplicationUser applicationUser, bool isPersistent)
{
authenticationManager.SignOut(
DefaultAuthenticationTypes.ExternalCookie,
DefaultAuthenticationTypes.ApplicationCookie);
var identity = await this.CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
// using default claim type from the framework
identity.AddClaim(new Claim(ClaimTypes.Email, applicationUser.Email));
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
You can add your default claims to all users here as well. But there is IClaimsIdentityFactory class that is assigned in UserManager. There is only one method there:
你可可以向所有用户添加默认的claims。但是在UserManager
中有一个IClaimsIdentityFactory
的类,它只定义了一个方法。
public interface IClaimsIdentityFactory<TUser, TKey> where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
/// <summary>
/// Create a ClaimsIdentity from an user using a UserManager
/// </summary>
Task<ClaimsIdentity> CreateAsync(UserManager<TUser, TKey> manager, TUser user, string authenticationType);
}
Default AspNet Identity implementation creates ClaimsIdentity, adds the default claims described above, adds claims stored in the database for the user: IdentityUserClaims. You can override this implementation and slip-in your own logic/claims:
默认的Asp.Net Identity实现创建ClaimsIdentity,添加之前提到的默认claims,添加存储在satabase中的属于这个用户的claims:IdentityUserClaims
。你可以重写这个实现类,然后放进你自己的 logic/claims.
public class MyClaimsIdentityFactory : ClaimsIdentityFactory<ApplicationUser, string>
{
public override async Task CreateAsync(UserManager<ApplicationUser, string> userManager, ApplicationUser user, string authenticationType)
{
var claimsIdentity = await base.CreateAsync(userManager, user, authenticationType);
claimsIdentity.AddClaim(new Claim("MyApplication:GroupId", "42"));
return claimsIdentity;
}
}
and assign it in UserManager:
将它赋值给UserManager:
public UserManager(MyDbContext dbContext)
: base(new UserStore<ApplicationUser>(dbContext))
{
// other configurations
// Alternatively you can have DI container to provide this class for better application flexebility
this.ClaimsIdentityFactory = new MyClaimsIdentityFactory();
}
正文结束
补充一些译者的话
如果你注意到文中提到登陆的代码authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
在新的Mvc项目中勾选个人身份认证时所生成的基本代码已经变成了 var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
,我试过使用文中的方法,是可以正常登陆的。
因为最终SignInManager的PasswordSignInAsync方法几经辗转也调用了IAuthenticationManager实例的Sign方法完成登陆。
【译】Asp.Net Identity与Owin,到底谁是谁?的更多相关文章
- 译:Asp.Net Identity与Owin,到底谁是谁?
送给正在学习Asp.Net Identity的你 :-) 原文出自 trailmax 的博客AspNet Identity and Owin. Who is who. Recently I have ...
- ASP.NET Identity & OWIN 学习资料
有关 ASP.NET Identity 的更多细节: http://www.asp.net/identity 从一个空项目中添加 ASP.NET Identity 和 OWIN 支持: http:// ...
- VS2013中web项目中自动生成的ASP.NET Identity代码思考
vs2013没有再分webform.mvc.api项目,使用vs2013创建一个web项目模板选MVC,身份验证选个人用户账户.项目会生成ASP.NET Identity的一些代码.这些代码主要在Ac ...
- 从Membership 到 .NET4.5 之 ASP.NET Identity
我们前面已经讨论过了如何在一个网站中集成最基本的Membership功能,然后深入学习了Membership的架构设计.正所谓从实践从来,到实践从去,在我们把Membership的结构吃透之后,我们要 ...
- ASP.NET Identity入门系列教程(一) 初识Identity
摘要 通过本文你将了解ASP.NET身份验证机制,表单认证的基本流程,ASP.NET Membership的一些弊端以及ASP.NET Identity的主要优势. 目录 身份验证(Authentic ...
- 向空项目添加 ASP.NET Identity
安装 AspNet.Identity 程序包 Microsoft.AspNet.Identity.Core 包含 ASP.NET Identity 核心接口Microsoft.AspNet.Ident ...
- asp.net identity 介绍
Asp.Net Identity 设计目标 微软在 asp.net 2.0 引入了 membership,为 asp.net 应用程序提供身份验证和授权功能.membership 假定用户在网站注册, ...
- ASP.NET Identity 简介
翻译自:http://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity ,略有改动. 背景:A ...
- ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇
在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identity,因为它已经对OWIN 有了良好的集成. 在这篇文章中,我主要关注ASP. ...
随机推荐
- 基于 Java Web 的毕业设计选题管理平台--测试报告与用户手册
一.测试报告 1.兼容性测试 功能 描述 效果 Chrome浏览器 FireFox浏览器 IE浏览器 war 端浏览器 管理员登录 管理员用户登录功能 弹出“登录成功”对话框,进入到毕业设计选题管理平 ...
- Java 笔记——MyBatis 生命周期
1.MyBatis 的生命周期 MyBatis的核心组件分为4个部分. SqlSessionFactoryBuilder (构造器): 它会根据配置或者代码来生成SqISessionFactory,采 ...
- XenServer DVSC
DVSC: Distribute Virtual Swithc Controller(分布式虚拟交换机控制器). DVSC是一种逻辑的集中式的虚拟交换机,它能让你控制来自于不同XenServer Ho ...
- github建仓库注意
在导入新的项目工程时,github建仓库时不要选择readme文件初始化仓库
- Linux 使用NC命令永久监听本地端口
感谢: 冰点阳光 Linux可以使用nc命令来测试网络端口是否正常,类似于telnet命令,但也可以用nc命令来监听本地端口,支持TCP.UDP协议,当我们测试NTP服务网络策略是否正常时,可以使用到 ...
- Spring之事务操作(配置文件)
UserDao.java package helloworld.tx; import org.springframework.jdbc.core.JdbcTemplate; public class ...
- BZOJ 3170 松鼠聚会(切比雪夫距离转曼哈顿距离)
题意 有N个小松鼠,它们的家用一个点x,y表示,两个点的距离定义为:点(x,y)和它周围的8个点即上下左右四个点和对角的四个点,距离为1.现在N个松鼠要走到一个松鼠家去,求走过的最短距离. 思路 题目 ...
- oralce下载
oracle的官网网址:oracle.com 打开之后选择中文 然后在页面的下方找到下载和试用链接 点击进入,选择数据库下载,在页面中找到如下部分点击进入即可下载对应版本的oracle
- spring boot 系列之六:深入理解spring boot的自动配置
我们知道,spring boot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子: Spring的JdbcTemplate是不是在Classpath里面?如果是,并 ...
- idea log4j 用法
1.导入jar包 这里用的maven导入 <!-- LOGGING begin --> <dependency> <groupId>org.slf4j</gr ...