Cookies, Claims and Authentication in ASP.NET Core(转载)
Most of the literature concerning the theme of authentication in ASP.NET Core focuses on the use of the ASP.NET Identity framework. In that context, things don’t seem to have changed much or, more precisely, all the changes that occurred in the infrastructure have been buried in the folds of the framework so that it looks nearly the same on the surface.
If you look at user authentication in ASP.NET Core outside the comfortable territory of ASP.NET Identity, you might find it quite different from what it was in past versions. ASP.NET Identity is a full-fledged, comprehensive, big framework that's overkill if all you need is to authenticate users via plain credentials from a simple database table. In this case, you'll see that the overall approach to authentication is still based on familiar concepts such as principal, login form, challenge and authorization attributes, except that the way you implement them is radically different. In this month's column, I'll explore the cookie authentication API as made available in ASP.NET Core, including the core facts of external authentication.
Foundation of ASP.NET Authentication
In ASP.NET, user authentication involves the use of cookies. Any users that attempt to visit a private page are redirected to a login page if they don't carry a valid authentication cookie. The login page, after having verified provided creden-tials, emits the cookie, which then travels with any subsequent requests from that user through the same browser until it expires. This is the same basic workflow you might know from past versions of ASP.NET. In ASP.NET Core, it only looks different because of the different middleware and the different configuration of the runtime environment.
There are two major changes in ASP.NET Core for those coming from an ASP.NET Web Forms and ASP.NET MVC background. First, there's no longer a web.config file, meaning that configuration of the login path, cookie name and expiration is retrieved differently. Second, the IPrincipal object—the object used to model user identity — is now based on claims rather than the plain user name. To enable cookie authentication in a brand-new ASP.NET Core 1.x application, you first reference the Microsoft.AspNetCore.Authentication.Cookies package and then add the code snippet in Figure 1.
Figure 1 Registering Middleware for Cookie Authentication
// This code is for ASP.NET Core 1.x
public void Configure(IApplicationBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = "Cookies",
CookieName = "YourAppCookieName",
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromMinutes(),
SlidingExpiration = true,
ReturnUrlParameter = "original",
AccessDeniedPath = new PathString("/Account/Denied")
});
}
Most of the information that classic ASP.NET MVC applications stored in the <authentication> section of the web.config file are configured as middleware options. The snippet in Figure 1 comprehends canonical options you might want to choose. Figure 2 explains each in more detail.
Figure 2 Cookie Authentication Options
Property | Description |
AccessDeniedPath | Indicates the path where an authenticated user will be redirected if the provided identity doesn’t have permission to view the requested resource. The same as getting an HTTP 403 status code. |
AutomaticAuthenticate | Indicates the middleware runs on every request and attempts to validate cookie and build an identity object from content. |
AutomaticChallenge | Indicates the middleware redirects the browser to a login page if the user isn’t authenticated or to the access denied page if the user is authenticated but not authorized on the requested resource. |
AuthenticationScheme | Name of the middleware. This property works in conjunction with AutomaticChallenge to selectively pick up the authentication middleware on a per-request basis. |
CookieName | Name of the authentication cookie being created. |
ExpireTimeSpan | Sets the expiration time of the authentication cookie. Whether the time has to be intended as absolute or relative is determined by the value of the SlidingExpiration property. |
LoginPath | Indicates the path where an anonymous user will be redirected to sign in with her own credentials. |
ReturnUrlParameter | Name of the parameter being used to pass the originally requested URL that caused the redirect to the login page in case of anonymous users. |
SlidingExpiration | Indicates whether the ExpireTimeSpan value is absolute or relative. In the latter case, the value is considered as an interval and the middleware will reissue the cookie if more than half the interval has elapsed. |
注意上面这个表格的很多属性从ASP.NET Core 2.X开始,应该在Startup类的ConfigureServices方法中进行设置,例如下面就演示了如何在ASP.NET Core 2.X中设置Cookie认证的Cookie名字:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//注册Cookie认证服务
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(option =>
{
option.Cookie.Name = "AspNetCookieAuth";//设置Cookie认证的Cookie名字
}); services.AddMvc();
}
Note that path properties are not of type string. LoginPath and AccessDeniedPath are of type PathString, which, compared to the plain String type, provides correct escaping when building a request URL.
The overall design of the user authentication workflow in ASP.NET Core gives you an unprecedented amount of flexi-bility. Every aspect of it can be customized at will. As an example, let's see how you can control the authentication work-flow being used on a per-request basis.
Dealing with Multiple Authentication Schemes
By setting AutomaticChallenge to false, you instruct the middleware not to react to the [Authorize] challenges by per-forming, say, a redirect. If no middleware will handle the challenge an exception is thrown. Automatic challenge was the norm in past versions of ASP.NET and there was almost nothing you could do about it.
In ASP.NET Core, you can register multiple and distinct pieces of authentication middleware and determine either algorithmically or via configuration which middleware has to be used for each request. When multiple authentication middleware is used, Automatic Authenticate can be true on multiple middleware. AutomaticChallenge, instead, should only be enabled on zero or one middleware. For more details, see bit.ly/2tS07Sm.
Common examples of authentication middleware are cookie-based authentication, bearer authentication, authentication through social networks, or an identity server and whatever else you can ever think to implement. Suppose you have the following code in the Configure method of your startup class:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "Cookies",
LoginPath = new PathString("/Account/Login/"),
AutomaticAuthenticate = true
});
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
AuthenticationScheme = "Bearer",
AutomaticAuthenticate = true
}
Be aware that there are constants to be used in place of magic strings like "Cookies" just to limit typos. In particular, the string "Cookies" can be replaced as below:
AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme
Note that UseIdentityServerAuthentication isn't part of the ASP.NET Core framework but belongs to the Identity Server framework (see github.com/IdentityServer). To choose the authentication scheme on a per-request basis, you use a new attribute on the Authorize attribute that in ASP.NET MVC marks actions as subject to authentication and authorization:
[Authorize(ActiveAuthenticationSchemes = "Bearer")]
public class ApiController : Controller
{
// Your API action methods here
...
}
The net effect of the code snippet is that all public endpoints of the sample ApiController class are subject to the identity of the user as authenticated by the bearer token.
Modeling the User Identity
In ASP.NET, the IPrincipal interface defines the software contract that defines the core of the user identity. The logged user is exposed through the User property of the HttpContext controller property. IPrincipal has the same implementation in ASP.NET 4.x (including ASP.NET MVC) and ASP.NET Core. However, in ASP.NET Core the default principal object isn't GenericPrincipal, but the new ClaimsPrincipal type. The difference is relevant.
GenericPrincipal wraps up one key piece of user information—the user name—even though custom user data can be added to the authentication ticket encrypted in the cookie. Over the years, the sole user name has become too little for the needs of modern applications. The role of the user, as well as some other chunks of information, most noticeably picture and display name, appear absolutely required today, forcing every realistic application to create its own custom principal type or query user information for each and every request using the user name as the key. ClaimsPrincipal just brilliantly solves the problem.
A claim is a key/value pair that describes a property of the logged user. The list of properties is up to the application, but includes name and role at the very minimum. Claims, in other words, are the ASP.NET Core way to model identity information. Claims can be read from any source, whether databases, cloud or local storage, even hardcoded. The Claim class is as simple as this:
public class Claim
{
public string Type { get; }
public string Value { get; } // More properties ...
}
The login process in ASP.NET Core passes through three classes: Claim, ClaimIdentity and ClaimsPrincipal.
Collecting the list of claims is a simple matter of populating an array of Claim objects:
public Claims[] LoadClaims(User user)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Role, user.Role),
new Claim("Picture", user.Picture)
};
return claims;
}
The name of the claim is a plain descriptive name rendered as a string. However, most common claim types have been grouped as constants into the ClaimTypes class. Before you create the principal, which is required to call the authentication workflow completed, you must get hold of an identity object:
var identity = new ClaimsIdentity(claims, "Password");
The first argument is self-explanatory—the list of claims associated with the identity being created. The second argument is a string that refers to the authentication scheme required to verify the identity. The string "Password" is a reminder of what will be required by the system for a user to prove her identity. The string "Password" is informational only and not a syntax element.
Another interesting aspect of the previous example is that an explicit user name is not strictly required. Any claim, regardless of the declared type, can be used to name the user. The following code snippet shows another equivalent way to have a new identity object:
var identity = new ClaimsIdentity(claims,
CookieAuthenticationDefaults.AuthenticationScheme,
"Nickname",
ClaimTypes.Role);
The new identity has "Cookies" as the authentication scheme and Nickname is the name of the claim in the provided list to be used to provide the name of the user. Role, instead, is the name of the claim in the same list determining the role. If not specified, the last two parameters default to ClaimTypes.Name and ClaimTypes.Role. Finally, you create the principal from the identity. It's worth noting, though, that a principal may have multiple identities. If it sounds weird, think that different areas of the same application might need different information to authenticate the user in much the same way an ID is required to identify yourself at some hotel desks and an electronic key to get into the elevator. The ClaimsPrincipal class has both an Identities property (a collection) and an Identity property. The latter is only a reference to the first item in the collection.
External Authentication
ASP.NET Core supports external authentication via identity providers from the ground up. Most of the time, all you do is install the appropriate NuGet package for the task. To rely on Twitter for authenticating users, you bring in the Microsoft.AspNetCore.Authentication.Twitter package and install the related middleware:
app.UseTwitterAuthentication(new TwitterOptions()
{
AuthenticationScheme = "Twitter",
SignInScheme = "Cookies",
ConsumerKey = "...",
ConsumerSecret = "..."
});
The SignInScheme property is the identifier of the authentication middleware that will be used to persist the resulting identity. In the example, an authentication cookie will be used. To see its effects, you first add a controller method to call into Twitter:
public async Task TwitterAuth()
{
var props = new AuthenticationProperties
{
RedirectUri = "/"
};
await HttpContext.Authentication.ChallengeAsync("Twitter", props);
}
Next, once Twitter has successfully authenticated the user, the SignInScheme property instructs the application on what to do next. A value of "Cookies" is acceptable if you want a cookie out of the claims returned by the external provider (Twitter, in the example). If you want to review and complete the information through, say, an intermediate form, then you have to break the process in two, introducing a temporary sign-in scheme. In addition to the standard cookie middleware you have the following:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "ExternalCookie",
AutomaticAuthenticate = false
});
app.UseTwitterAuthentication(new TwitterOptions
{
AuthenticationScheme = "Twitter",
SignInScheme = "ExternalCookie"
});
When the external provider returns, a temporary cookie is created using the ExternalCookie scheme. Having set the redirect path appropriately, you have a chance to inspect the principal returned by Twitter and edit it further:
var props = new AuthenticationProperties
RedirectUri = "/account/external"
};
To complete the workflow you also need to sign in in the cookies scheme and sign out of temporary scheme (ExternalCookie):
public async Task<IActionResult> External()
{
var principal = await HttpContext
.Authentication
.AuthenticateAsync("ExternalCookie");
// Edit the principal
... await HttpContext.Authentication.SignInAsync("Cookies", principal);
await HttpContext.Authentication.SignOutAsync("ExternalCookie "); return View();
}
ExternalCookie, as well as cookies, are just internal identifiers and can be renamed as long as they remain consistent throughout the application.
Wrapping Up
In ASP.NET Core many things seem to be radically different, but in the end most of the concepts you might know from ASP.NET remain unchanged. You still need to have an authentication cookie created, and you can still control the name of the cookie and the expiration date. External authentication is supported and login pages have the same structure as before. However, configuration and underlying working of the authentication infrastructure are different, while retaining their previous flexibility.
Everything stated in this article refers to ASP.NET Core 1.x. There are a few things that will work differently in ASP.NET Core 2.0. In particular, authentication middleware is now exposed as services and must be configured on Configure-Services:
services.AddCookieAuthentication(options =>
{
Options.LoginPath = new PathString("/Account/Login"),
options.AutomaticAuthenticate = true,
options.AutomaticChallenge = true,
options.AuthenticationScheme = "Cookies",
...
});
In the Configure method of the Startup class of ASP.NET Core 2.0 applications, you just declare your intention to use authentication services without any further options:
app.UseAuthentication();
Also note that the SignInAsync method you use in your code to create the authentication cookie is also exposed from the HttpContext object directly, instead of passing through an intermediate Authentication property as shown in the last code snippet for ASP.NET Core 1.x.
Cookies, Claims and Authentication in ASP.NET Core(转载)的更多相关文章
- 5.3Role和Claims授权「深入浅出ASP.NET Core系列」
希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,码字辛苦,如果你吃了蛋觉得味道不错,希望点个赞,谢谢关注. Role授权 这是一种Asp.Net常用的传统的授权方法,当我们在 ...
- ASP.NET CORE中使用Cookie身份认证
大家在使用ASP.NET的时候一定都用过FormsAuthentication做登录用户的身份认证,FormsAuthentication的核心就是Cookie,ASP.NET会将用户名存储在Cook ...
- ASP.NET Core Authentication系列(一)理解Claim, ClaimsIdentity, ClaimsPrincipal
前言 首先我们来看一下在ASP.NET时代,Authentication是如何使用的.下面介绍的是System.Web.Security.FormsAuthentication: // 登录 Syst ...
- ASP.NET Core Authentication系列(二)实现认证、登录和注销
前言 在上一篇文章介绍ASP.NET Core Authentication的三个重要概念,分别是Claim, ClaimsIdentity, ClaimsPrincipal,以及claims-bas ...
- ASP.NET Core 中的那些认证中间件及一些重要知识点
前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...
- [转]ASP.NET Core 中的那些认证中间件及一些重要知识点
本文转自:http://www.qingruanit.net/c_all/article_6645.html 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系 ...
- ASP.NET Core 使用Cookie验证身份
ASP.NET Core 1.x提供了通过Cookie 中间件将用户主体序列化为一个加密的Cookie,然后在后续请求中验证Cookie并重新创建主体,并将其分配给HttpContext.User属性 ...
- ASP.NET Core 认证与授权[4]:JwtBearer认证
在现代Web应用程序中,通常会使用Web, WebApp, NativeApp等多种呈现方式,而后端也由以前的Razor渲染HTML,转变为Stateless的RESTFulAPI,因此,我们需要一种 ...
- ASP.NET Core 身份验证(一)
前言 这篇文章我想带领大家了解一下 ASP.NET Core 中如何进行的身份验证,在开始之前强烈建议还没看过我写的 Identity 系列文章的同学先看一下. Identity 入门系列文章: Id ...
随机推荐
- Cocos Creator—定制H5游戏首页loading界面
Cocos Creator从1.0版本发布到现在也有一年多了,按理说一些常见的问题网上都有解决方案,例如"如何自定义首页加载进度条界面"这种普遍需求,应该所有人都会遇到的,因此也有 ...
- 基于开源CA系统ejbca community 6.3.1.1构建私有CA管理数字证书
最后更新于2017年01月24日 一.为什么 为什么写这篇文章?ca是什么?数字证书是什么?ejbca又是什么? 让我们从http与https说起.http是超文本传输协议(HyperText Tra ...
- 【Hadoop篇】--Hadoop常用命令总结
一.前述 分享一篇hadoop的常用命令的总结,将常用的Hadoop命令总结如下. 二.具体 1.启动hadoop所有进程start-all.sh等价于start-dfs.sh + start-yar ...
- .NET Core微服务之基于IdentityServer建立授权与验证服务(续)
Tip: 此篇已加入.NET Core微服务基础系列文章索引 上一篇我们基于IdentityServer4建立了一个AuthorizationServer,并且继承了QuickStartUI,能够成功 ...
- 【工利其器】必会工具之(三)systrace篇(2)
systrace工具打开路径 以AndroidStudio(后面简写为AS),在顶部菜单栏中 Tools>Android>Android Device Monitor 打开后看到如下界面, ...
- 高性能消息队列NSQ
前言 最近我再网上寻找使用golang实现的mq,因为我知道golang一般实现的应用部署起来很方便,所以我就找到了一个叫做nsq的mq,其实它并不能完全称为队列,但是它的轻量和性能的高效,让我真的大 ...
- Virtual Box虚拟机Ubuntu系统安装及基本配置
Linux简介 什么是 Linux? Linux:世界上不仅只有一个 Windows 操作系统,还有 Linux.mac.Unix 等操作系统.桌面操作系统下 Windows 是霸主,而 Linux ...
- Hibernate框架_搭建第一个Hibernate框架
一.eclipse搭建 A.创建动态web项目 New-->Dynamic web project(web project) B.导入jar包 1.数据库驱动包 2.hibernate开发必须j ...
- 戏说程序猿之cannot find the object
“别开玩笑了,程序员哪里需要对象!” 程序员难找对象原因无非如下: 1.工作时间长,恋爱时间少 2.性格偏于内向,不主动 3.不注意个人形象 程序员爱情观: 爱情就是死循环,一旦执行就陷进去了: 爱上 ...
- 备忘录模式 Memento 快照模式 标记Token模式 行为型 设计模式(二十二)
备忘录模式 Memento 沿着脚印,走过你来时的路,回到原点. 苦海翻起爱恨 在世间难逃避命运 相亲竟不可接近 或我应该相信是缘份 一首<一生所爱>触动了多少 ...