转载请注入出处: https://home.cnblogs.com/u/zhiyong-ITNote/

dotnet core中提供了一个新的身份验证框架Identity,它不同于dot net下的身份验证。在这个框架里面,有一个生成token的功能,也就是我们常说的令牌,令牌的作用有哪些?
Token值介绍
token 值: 登录令牌.利用 token 值来判断用户的登录状态.类似于 MD5 加密之后的长字符串.
用户登录成功之后,在后端(服务器端)会根据用户信息生成一个唯一的值.这个值就是 token 值.
基本使用:
在服务器端(数据库)会保存这个 token 值,以后利用这个 token 值来检索对应的用户信息,并且判断用户的登录状态.
用户登录成功之后,服务器会将生成的 token 值返回给 客户端,在客户端也会保存这个 token 值.(一般可以保存在 cookie 中,也可以自己手动确定保存位置(比如偏好设置.)).
以后客户端在发送新的网络请求的时候,会默认自动附带这个 token 值(作为一个参数传递给服务器.).服务器拿到客户端传递的 token 值跟保存在 数据库中的 token 值做对比,以此来判断用户身份和登录状态.
判断登录状态:
如果客户端没有这个 token 值,意味着没有登录成功过,提示用户登录.
如果客户端有 token 值,一般会认为登录成功.不需要用户再次登录(输入账号和密码信息).
token 值扩展:
token 值有失效时间:
一般的 app ,token值得失效时间都在 1 年以上.
特殊的 app :银行类 app /支付类 app :token值失效时间 15 分钟左右.
一旦用户信息改变(密码改变),会在服务器生成新的 token 值,原来的 token值就会失效.需要再次输入账号和密码,以得到生成的新的 token 值.
唯一性判断: 每次登录,都会生成一个新的token值.原来的 token 值就会失效.利用时间来判断登录的差异性.

至此也就说完了其作用。那么Identity框架是如何为我们提供这个能力的呢?我们又该如何使用呢?首先请你看下 asp.net core中的数据保护模块,这是Identity框架实现token的基础。
var token = await userManager.GenerateUserTokenAsync(user, "Default", "passwordless-auth");
我们通过这句代码来生成token,userManager是Identity框架的用户管理类UserManager的实例对象。

当然首先我们需要实现相关的依赖注入,我就不说了。Identity默认生成的token是基于DataProtectorTokenProvider类的,我们在依赖注入的时候,其实就引用了这个类,先看下AddDefaultTokenProviders的源码:

public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder)
{
var userType = builder.UserType;
var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(userType);
var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(userType);
var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(userType);
var authenticatorProviderType = typeof(AuthenticatorTokenProvider<>).MakeGenericType(userType);
return builder.AddTokenProvider(TokenOptions.DefaultProvider, dataProtectionProviderType)
.AddTokenProvider(TokenOptions.DefaultEmailProvider, emailTokenProviderType)
.AddTokenProvider(TokenOptions.DefaultPhoneProvider, phoneNumberProviderType)
.AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType);
}

然后我们看下DataProtectorTokenProvider类的部分源码:

public class DataProtectorTokenProvider<TUser> : IUserTwoFactorTokenProvider<TUser> where TUser : class
{
/// <summary>
/// Initializes a new instance of the <see cref="DataProtectorTokenProvider{TUser}"/> class.
/// </summary>
/// <param name="dataProtectionProvider">The system data protection provider.</param>
/// <param name="options">The configured <see cref="DataProtectionTokenProviderOptions"/>.</param>
public DataProtectorTokenProvider(IDataProtectionProvider dataProtectionProvider, IOptions<DataProtectionTokenProviderOptions> options)
{
if (dataProtectionProvider == null)
{
throw new ArgumentNullException(nameof(dataProtectionProvider));
}
Options = options?.Value ?? new DataProtectionTokenProviderOptions();
// Use the Name as the purpose which should usually be distinct from others
Protector = dataProtectionProvider.CreateProtector(Name ?? "DataProtectorTokenProvider");
} public virtual async Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var ms = new MemoryStream();
var userId = await manager.GetUserIdAsync(user);
using (var writer = ms.CreateWriter())
{
writer.Write(DateTimeOffset.UtcNow);
writer.Write(userId);
writer.Write(purpose ?? "");
string stamp = null;
if (manager.SupportsUserSecurityStamp)
{
stamp = await manager.GetSecurityStampAsync(user);
}
writer.Write(stamp ?? "");
}
var protectedBytes = Protector.Protect(ms.ToArray());
return Convert.ToBase64String(protectedBytes);
}
public virtual async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user)
{
try
{
var unprotectedData = Protector.Unprotect(Convert.FromBase64String(token));
var ms = new MemoryStream(unprotectedData);
using (var reader = ms.CreateReader())
{
var creationTime = reader.ReadDateTimeOffset();
var expirationTime = creationTime + Options.TokenLifespan;
if (expirationTime < DateTimeOffset.UtcNow)
{
return false;
} var userId = reader.ReadString();
var actualUserId = await manager.GetUserIdAsync(user);
if (userId != actualUserId)
{
return false;
}
var purp = reader.ReadString();
if (!string.Equals(purp, purpose))
{
return false;
}
var stamp = reader.ReadString();
if (reader.PeekChar() != -)
{
return false;
} if (manager.SupportsUserSecurityStamp)
{
return stamp == await manager.GetSecurityStampAsync(user);
}
return stamp == "";
}
}
// ReSharper disable once EmptyGeneralCatchClause
catch
{
// Do not leak exception
}
return false;
}
}

可以看到,默认的情况下,使用的就是asp.net core Data Provider生成token的。我们来看看这个DataProtectorTokenProviderOptions配置类的信息:

public class DataProtectionTokenProviderOptions
{
public string Name { get; set; } = "DataProtectorTokenProvider"; public TimeSpan TokenLifespan { get; set; } = TimeSpan.FromDays();
}

默认的情况下,token过期时间是一天,也就是说,如果我们需要修改过期时间的话,完全可以自己来:

services.Configure<DataProtectionTokenProviderOptions>(
x => x.TokenLifespan = TimeSpan.FromMinutes());

刚刚也说了,默认情况下Identity框架给我们提供的token是基于Data Protect的,那么我们可以切换另外的token提供方式。只需继承自TotpSecurityStampBasedTokenProvider<TUser>类就可以了。该类源码地址:
https://github.com/aspnet/Identity/blob/c7276ce2f76312ddd7fccad6e399da96b9f6fae1/src/Core/TotpSecurityStampBasedTokenProvider.cs#L21 该类与DataProtectorTokenProvider类一样都是继承自IUserTwoFactorTokenProvider接口来实现的,其方法都是一样的,只是该类的token生成算法不再是Data Protect中的token生成算法了,而是hash算法,因此这里也是一个可拓展点,我们还可以提供自己的想要的加密算法来生成token。

public class PasswordlessLoginTotpTokenProvider<TUser> : TotpSecurityStampBasedTokenProvider<TUser>
where TUser : class
{
public override Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user)
{
return Task.FromResult(false);
} public override async Task<string> GetUserModifierAsync(string purpose, UserManager<TUser> manager, TUser user)
{
var email = await manager.GetEmailAsync(user);
return "PasswordlessLogin:" + purpose + ":" + email;
}
} public static class CustomIdentityBuilderExtensions
{
public static IdentityBuilder AddPasswordlessLoginTotpTokenProvider(this IdentityBuilder builder)
{
var userType = builder.UserType;
var totpProvider = typeof(PasswordlessLoginTotpTokenProvider<>).MakeGenericType(userType);
return builder.AddTokenProvider("PasswordlessLoginTotpProvider", totpProvider);
}
} public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityDbContext>()
.AddDefaultTokenProviders()
.AddPasswordlessLoginTotpTokenProvider(); // Add the custom token provider
} var token = await userManager.GenerateUserTokenAsync(
user, "PasswordlessLoginTotpProvider", "passwordless-auth");

这就是我们的使用了。在这里提一下。TotpSecurityStampBasedTokenProvider类的默认生命周期是9分钟,一旦超过9分钟,token就会变化。

大概的理了下自己接下来的实现需要用到的技术,不能为了实现而实现,而是要想清楚需要的技术,同时去学习这些技术,然后再去实现。其实这个需求是我自己加的,因为之前看官方文档的时候,对数据保护很懵逼,只知道可以用来生成token加密,却不知道实际的用途,因此自己深入研究查找资料,借鉴别人的博客来学习,还是google好啊,百度真...就不爆粗口了。
整理下自己需要实现得需求思路:
我们可以在邮箱验证的时候用到token,也可以在整个的网站中用到,第一个就先不说了,说说第二个:
我们一般可以将用户登陆之后,根据用户名什么的生成token,然后放到服务端也就是数据库了,我们给它有效期(比如连续记住我十天之类的)表示这个用户已经登录过了。那么客户端呢?也就是我们的浏览器,我们可以将cookie存入客户端浏览器,但是一旦我们关闭了浏览器那么cookie也就不存在了,因此不可行。可以存入到本地磁盘中。同时,一般来讲,网站的url都很长,一般都是将token拼接在了url里面,如果你觉得url太长了,我们可以在中间件中写入到http头部字段中,根据http头部字段。当然你可以在ActionFilter以及ResultFilter中写入http头部。

回头看看能不能整理出来一个实现,到时候再放上来代码。

参考资料:

Implementing custom token providers for passwordless authentication in ASP.NET Core Identity

Implementing Medium's Passwordless Authentication using ASP.NET Core Identity

转载请注入出处: https://home.cnblogs.com/u/zhiyong-ITNote/

ASP.NET Core的Data Protect(数据保护)的学习和应用的更多相关文章

  1. 用ASP.NET Core构建可检测的高可用服务--学习笔记

    摘要 随着现代化微服务架构的发展,系统故障的定位与快速恢复面临着诸多挑战,构建可检测的服务,帮助线上保障团队时刻掌控应用的运行状况越来越重要.本次分享会讲解如何让 ASP .NET Core 应用与现 ...

  2. .NET 云原生架构师训练营(ASP .NET Core 整体概念推演)--学习笔记

    演化与完善整体概念 ASP .NET Core 整体概念推演 整体概念推演到具体的形式 ASP .NET Core 整体概念推演 ASP .NET Core 其实就是通过 web framework ...

  3. ASP.NET Core WebApi AspNetCoreRateLimit 限流中间件学习

    AspNetCoreRateLimit介绍: AspNetCoreRateLimit是ASP.NET核心速率限制框架,能够对WebApi,Mvc中控制限流,AspNetCoreRateLimit包包含 ...

  4. ASP.NET Core 数据保护(Data Protection)【中】

    前言 上篇主要是对 ASP.NET Core 的 Data Protection 做了一个简单的介绍,本篇主要是介绍一下API及使用方法. API 接口 ASP.NET Core Data Prote ...

  5. ASP.NET Core 数据保护(Data Protection)【上】

    前言 上一篇博客记录了如何在 Kestrel 中使用 HTTPS(SSL), 也是我们目前项目中实际使用到的. 数据安全往往是开发人员很容易忽略的一个部分,包括我自己.近两年业内也出现了很多因为安全问 ...

  6. [转]Session and application state in ASP.NET Core

    本文转自:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state By Rick Anderson and Steve ...

  7. [翻译] ASP.NET Core 3.0 的新增功能

    ASP.NET Core 3.0 的新增功能 全文翻译自微软官方文档英文版 What's new in ASP.NET Core 3.0 本文重点介绍了 ASP.NET Core 3.0 中最重要的更 ...

  8. ASP.NET Core 使用Cookie验证身份

    ASP.NET Core 1.x提供了通过Cookie 中间件将用户主体序列化为一个加密的Cookie,然后在后续请求中验证Cookie并重新创建主体,并将其分配给HttpContext.User属性 ...

  9. 学习ASP.NET Core Razor 编程系列十七——分组

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

随机推荐

  1. SpringBoot全局日志管理(AOP)

    1.在pom.xml中引入aop的jar包 <dependency> <groupId>org.springframework.boot</groupId> < ...

  2. vue之指令

    一.什么是VUE? 它是构建用户界面的JavaScript框架(让它自动生成js,css,html等) 二.怎么使用VUE? 1.引入vue.js 2.展示HTML <div id=" ...

  3. Nginx详解二十五:Nginx架构篇之Nginx常见的问题

    Nginx常见的问题 1.相同server_name多个虚拟主机优先级访问,是按读取文件的优先级来排序 在/opt/app/下准备3个code文件夹,下面放入3个html文件,里面的内容分别是code ...

  4. ajax-简单参数方法实现阴影效果

    注: 简单参数 (按照参数的数量和位置传递参数) 使用时按照位置.数量传递 shadow.js函数 //简单参数实现方式/** slices:阴影* opacity:透明度* zIndex:层级* * ...

  5. AI学习吧-结算中心

    结算中心流程 在结算中心中,主要是对用户添加到购物车商品的结算,由于用户可能添加了多个课程,但是,结算时会选择性的进行支付.在结算时会选中课程id,和对应的价格策略.在后台,首先会对用户进行校验,验证 ...

  6. js 对象(object)合并

    var obj1 = { name:'lisi', checked:'true' }; var obj2 = { name:'zhangsan', age:18 }; Object.assign(ob ...

  7. openssl中RSA数字签名的使用

    参考: OpenSSL命令行工具验证数字签名 客户端需要生成一对密钥,服务器需要生成一对密钥,分别记为client_private.pem/client_public.pem和server_priva ...

  8. GAN-生成对抗网络原理

    最近一直在看GAN,我一直认为只有把博客看了一遍,然后再敲一遍.这样才会有深刻的感悟. GAN(生成式对抗网络)(GAN, Generative Adversarial Networks )是一种深度 ...

  9. JSP中三大指令

    JSP指令概述 JSP指令的格式:<%@指令名 attr1=”” attr2=”” %>,一般都会把JSP指令放到JSP文件的最上方,但这不是必须的.  JSP中的指令共有三个:page. ...

  10. -webkit-,-moz-,-ms-,-o-具体指什么了?

    -webkit-,-moz-,-ms-,-o-具体指什么了? -webkit-,-moz-,-ms-,-o-是指浏览器私有前缀名. 那为什么要有私有前缀呢? 因为制定HTML和CSS标准的组织W3C动 ...