您的位置:首页 > 其它

使用cookie来做身份认证

2018-08-28 01:21 609 查看
文章是msdn的官方文档,链接在这里。其实也有中文的文档,这里还是想做一个记录。

文章有asp.net core 2.x 和1.x 版本,我这里就忽略1.x了。

下面先说几点额外的东西有助于理解。

Authentication 和 Authorization

这里先讲一下Authentication和Authorization两个词的区别。

Authentication:认证。

Authorization:授权。

简单来说,认证是用来证明一个人的身份,比如说他是一个学生,一个老师,一个boss,那么就需要这么一个认证。授权是用来表示这个用户能做什么事情,比如admin可以修改删除数据,normal user只能查看数据。

Issuer 和 Audience

Issuer:发行者,这里来说就是 cookie 是谁分发的。

Audience:听众,这个 cookie 的受众是谁。

正文

就像你前面看到认证相关的主题,Asp.net core Identity 是一个创建用户和维护用户登录的完备的认证解决方案。但有时你可能也想要自己的基于cookie的认证方式。你可以在不使用Asp.net core Identity的情况下使用cookie来实现一种独立的认证服务。

示例源码在这里

因为我们这里只是做一个demo程序,所以写死一个假设的用户Maria Rodriguez到系统里面。邮箱相关的用户名是“maria.rodriguez@contoso.com”,密码任意。用户通过
Pages/Account/Login.cshtml.cs
文件中的
AuthenticateUser
方法做认证。现实环境中应该基于数据库。

更多如何从ASP.net Core 1.x 到2.0的信息参考这里.

想使用ASP.net Core Identity,参考这里.

配置

如果程序没有使用Microsoft.AspNetCore.App元程序包,给程序引用一下Microsoft.AspNetCore.Authentication.Cookies(版本≥2.1.0)。

ConfigureServices
中,通过
Authentication
AddCookie
方法添加一下认证服务。

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();

传给
AddAuthentication
AuthenticationScheme
值设置了程序默认使用的认证方案。

AuthenticationScheme
在你有多个 cookie 认证实例或者你系统用某种特定的方案来做认证的时候是非常有用的。设置成为
CookieAuthenticationDefaults.AuthenticationScheme
就表示用‘Cookies’来作为一个方案。你可以设置任意的 string 类型的值来区分不同的方案。

Configure
方法中,使用
UseAuthentication
来调用认证中间件用于设置
HttpContext.User
属性。应在
UseMvcWithDefaultRoute
UseMvc
方法之前调用
UseAuthentication
方法。

AddCookie 设置选项

大致是这么设置:

services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.ClaimsIssuer = "test";
options.ClaimsIssuer = "aa";
//以及其他...
});

具体详细的通过
CookieAuthenticationOptions
来设置相关的选项。着重看几个关键的设置,比如 AccessDeniedPath, LoginPath, LogoutPath, Domain, Name,
ExpireTimeSpan


选项描述
AccessDeniedPath
HttpContext.ForbidAsync
触发302时的跳转地址,默认
/Account/AccessDenied
ClaimsIssuer用于设置 cookie 的Issuer 属性。
Cookie.Domaincookie的有效域。默认是请求的服务器名。浏览器只会给符合的服务器发送 cookie。你可能会希望设置这个值来调整他的作用域。举个例子,设置成
.contoso.com
他的作用域就包括
contoso.com
,
www.contoso.com
,
staging.www.contoso.com
等。
Cookie.Expiration获取或设置cookie的有效期。core 2.1+不建议使用。建议是使用
ExpireTimeSpan
来设置 cookie 的失效时间。
Cookie.HttpOnly设置 cookie 是否是只能被服务器访问,默认 true,可以设置成 false 给客户端js 脚本访问,但是有可能会造成XSS(跨站脚本攻击)。
Cookie.Namecookie 的名字。
Cookie.Path用来隔离同一个服务器下面的不同站点。比如站点是运行在
/app1
下面,设置这个属性为
/app1
,那么这个 cookie 就只在 app1下有效。
Cookie.SameSite表示浏览器是否允许 cookie 被附加到相同的站点。有几种枚举:
SameSiteMode.Strict
,只允许相同的站点。
SameSiteMode.Lax
允许以安全的 http方式附加到不同站点或相同站点。为了支持 OAuth 认证,需要设置成
SameSiteMode.Lax
Cookie.SecurePolicy设置是否只允许 https。
DataProtectionProvider用于设置创建
TicketDataFormat
(在表格最后)
Events设置一些时间的处理程序。比如
OnSignedIn
,
OnSigningOut
等,默认是不做任何操作。
EventsTypeEvents的类型。
ExpireTimeSpan设置存储在 cookie 里面的认证票据的过期时间。服务端会验证加密的 ticket 的有效性。在设置了
IsPersistent
之后也能在 Set-Cookie 头里面返回。默认的过期时间是14天。
LoginPath
HttpContext.ChallengeAsync
方法触发302跳转时候的地址。假设设置成
/account/login
,比如当前访问
/secure
返回401,那么会跳转地址
/account/login?returnUrl=/secure
,当 login 页面生成一个新的登录身份之后,浏览器会跳转到 secure 页面。默认值是
/Account/login
LogoutPath登出地址。
ReturnUrlParameter登录或登出之后页面可以做一个跳转,这个跳转地址作为一个参数传过去,这个就用来设置这个参数的名字。
SessionStore用来保存跨站点请求的身份信息。设置了之后只有 session 的标识符会发送到客户端。当身份标识比较多的时候可以用。
SlidingExpiration滑动过期。标识一个有新的过期时间的新 cookie是否可以被动态的分发。可以在
SignInAsync
方法里面使用
AuthenticationProperties
。使用绝对的 cookie 有效期时间来增加应用的安全性。举个例子:
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddMinutes(20) });
TicketDataFormat
Validate验证当前 option是否是有效的。

Cookie Policy Middleware

Cookie 策略中间件。用于设置应用的 cookie的兼容性。和顺序有关,只会影响程序管道他后面的设置。如下方式使用。

app.UseCookiePolicy(cookiePolicyOptions);

CookiePolicyOptions提供了程序全局特性相关的设置。并且可以在 cookie 添加或者删除的时候挂钩一些处理程序。 有以下一些属性。

属性描述
HttpOnly设置 cookie 是否是只能通过服务器访问的。默认是
HttpOnlyPolicy.None
CheckConsentNeeded一个返回 bool 的函数,如果返回 true 会在弹出一个页面让用户确认使用 cookie
ConsentCookie(这个文档上也没说。。。)
MinimumSameSitePolicy同站点策略,默认是
SameSiteMode.Lax
, Asp.net Core2.0+ 可用。
OnAppendCookiecookie 被追加的时候调用。
OnDeleteCookiecookie 被删除的时候调用。
Secure标识 cookie 是否必须是https.

创建一个认证 cookie

创建一个包含用户信息的 cookie需要构造一个ClaimsPrincipal。用户信息会被序列化然后保存在cookie 里面。

用必要的 Claim来构造一个ClaimsIdentity,然后调用
SignInAsync
方法。

var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Email),
new Claim("FullName", user.FullName),
new Claim(ClaimTypes.Role, "Administrator"),
};

var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);

var authProperties = new AuthenticationProperties
{
//AllowRefresh = <bool>,
// Refreshing the authentication session should be allowed.

//ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
//cookie 的绝对过期时间,会覆盖ExpireTimeSpan的设置。

//IsPersistent = true,
//表示 cookie 是否是持久化的以便它在不同的 request 之间传送。设置了ExpireTimeSpan或ExpiresUtc是必须的。

//IssuedUtc = <DateTimeOffset>,
//  凭证认证的时间。

//RedirectUri = <string>
//http 跳转的时候的路径。
};

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);

SignInAsync
方法创建一个加密过的 cookie 然后把他添加到当前的 response 中。没有设置AuthenticationScheme的话会使用默认的 scheme。

加密是基于asp.net core 的Data Protection系统实现的,所以,如果程序是部署在多台机器或者做了负载均衡上的话,需要配置 data protection(和当年 asp.net 里面的类似。)

登出

SignOutAsync
用来登出当前用户并且删除 cookie。代码如下。

await HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);

登录和登出需要使用相同的方案名称。也就是一样的AuthenticationScheme。

对后台的改变作出反应

当 cookie 被创建之后,它就成了身份标识的唯一来源。即使在后台禁用了当前用户,因为 已经分发的cookie 无法知晓,所以用户依旧可以保持登录状态直到 cookie 失效。

ValidatePrincipal事件可以用来拦截或者覆盖 cookie 的身份验证。这可以减少被收回权限的用户对系统损害的风险。可以通过如下方式实现这个功能。

首先修改一下 SignInAsync 方法里面获取到的用户相关的 claim。

var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Email),
new Claim("LastChanged", {数据库的值})//增加一个LastChanged,然后记录一下值
};

var claimsIdentity = new ClaimsIdentity(
claims,
CookieAuthenticationDefaults.AuthenticationScheme);

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));

然后创建一个
CustomCookieAuthenticationEvents
继承自
CookieAuthenticationEvents


using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
private readonly IUserRepository _userRepository;

public CustomCookieAuthenticationEvents(IUserRepository userRepository)
{
// 从DI 里面获取用户相关的.
_userRepository = userRepository;
}

public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
var userPrincipal = context.Principal;

// 查找上面的LastChanged相关的claim.
var lastChanged = (from c in userPrincipal.Claims
where c.Type == "LastChanged"
select c.Value).FirstOrDefault();

if (string.IsNullOrEmpty(lastChanged) ||
!_userRepository.ValidateLastChanged(lastChanged))//调用的ValidateLastChanged来判断这个lastChanged 相关的额 cookie是否是一个有效的cookie
{
context.RejectPrincipal();//拒绝这个 cookie

await context.HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);// 自动登出
}
}
//其他的方法,都可以设置
public override Task SignedIn(CookieSignedInContext context)
{
return base.SignedIn(context);
}
}

然后通过
EventsType
来调用这个设置,然后注入这个
CustomCookieAuthenticationEvents


services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.EventsType = typeof(CustomCookieAuthenticationEvents);
});

services.AddScoped<CustomCookieAuthenticationEvents>();

考虑一种情况,如果说这个用户更新了之后不影响系统的安全,可以考虑替换
context.RejectPrincipal()
context.ReplacePrincipal
,并且设置
context.ShouldRenew=true
来无损的更新用户的principal。


上面的实现方法会在每个请求的时候都触发,所以会对系统的性能造成一定的影响。


持久化 cookie

你可能想要持久化 cookie 让他可以在浏览器的不同进程之间使用。cookie 的持久化应该用类似在界面上显示“记住我”的复选框,然后让用户点击的方式来实现。其他类似的机制也行。

下面的代码用来实现 cookie 持久化。

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = true
});

如果 cookie 在浏览器关闭期间过期了,浏览器会在下次启动的时候自动删除 cookie。

AuthenticationProperties
Microsoft.AspNetCore.Authentication
命名空间里面。

绝对过期时间

可以用
ExpiresUtc
来设置绝对过期时间,但必须同时设置
IsPersistent
,否者这个这个参数会被忽略,同时,这个 cookie 只是当前回话有效。

当在 SignInAsync 方法里面设置了
ExpiresUtc
,它会覆盖
CookieAuthenticationOptions
设置了的
ExpireTimeSpan


下面的代码设置了一个20min 有效期的持久化 cookie,其他有效期相关的设置都会被忽略。

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
});

最后就是感慨一下,msdn 好强大。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: