一、前言

这方面的资料很多,重复的写没必要,但是最近一直在学习身份验证和授权相关东东,为了成体系还是写一篇,主要是从概念上理解identity系统。

参考:https://www.cnblogs.com/r01cn/p/5179506.html

二、概述

几乎所有系统都包含用户、角色、权限、登录、注册等等,以前我们通常是自己来实现,定义各种实体、以及对应的Repository、Service类,其实这些功能早在.net 2.0就已有实现,并且一直在进化,要么是因为大家不了解,要么是因为觉得扩展性不强,最终使用微软提供的用户管理的人不多,这里就不扯以前的事了。现在的asp.net core延续了.net framework最后一次实现的Identity,暂且称为“标识系统”吧。我的理解是它主要提供用户管理、角色管理等功能,并且提供了相关的类与身份验证系统结合,还包括持久化和UI,还提供了很多扩展点,当然预留了很多扩展点,也提供了默认实现,就使用起来。用还是不用了解之后再做决定也不迟。

身份验证、授权、用户/角色管理这三个系统是独立的,只是授权依赖身份验证,你要判断某人能干啥,至少得让他先登录吧。所以identity系统不是必须的,但是用它比自己实现更方便。另一个原因一些开源框架对于用户、角色、权限、登录这些东西往往也是直接使用的微软原生的,只是做了些许扩展和集成。

主要概念包括:

  • 用户实体:IdentityUser,对应到用户数据表
  • 用户数据操作接口:UserStore,对用户进行CRUD和其它功能的  与数据库操作相关的功能
  • 用户管理器:UserManager,对用户进行CRUD和相关操作的业务逻辑类,
  • 登录器:SignInManager提供登录相关功能,实现identity系统和身份验证系统的结合
  • 角色实体:类别用户
  • 角色数据操作接口:类别用户
  • 角色管理器:类比用户

默认使用EF做数据库相关操作,当然非常容易扩展。

三、IdentityUser

系统中通常有两种用户,

  • 一个是asp.net框架中的当前登录用户,也就是HttpContext.User,它是ClaimsPrincipal类型。它里面相对来说属性比较少,只包含用户id和与授权判断相关数据,比如角色
  • 一个是我们做用户管理时的用户实体。它包含完整的用户信息

比如像“手机号”这个字段在用户实体里肯定是有的,但是没必要放进当前登录用户,因为这个字段不是每次请求都需要的。只要那些被平凡访问的字段放进当前登录用户才比较合适。

这个用户就是用户实体,对应到数据库的用户表。用下半身也能想到它大概包含哪些属性:id、姓名、账号、密码、邮箱、性别、出生年月、民族、籍贯、地址、电话、巴拉巴拉.....

大概了解了啥是用户,我们来看看它比较特殊的几个点

3.1、继承结构

public class IdentityUser<TKey>    public class IdentityUser : IdentityUser<string>

因为在设计identity系统时,不晓得将来的开发人员希望用什么类型作为主键,所以定义成泛型。又由于大部分时候可能使用字符串(可能是对应guid),默认有个实现。

3.2、特殊字段

NormalizedUserName:标准化的用户名,默认是直接将UserName属性转换为大写,主要可能用来方便提升查询性能的。类似的还有Email与NormalizedEmail的关系
EmailConfirmed:是否已做了邮箱确认。我们注册用户时会向用户发送一封邮件,用户进入邮箱点击邮件中的地址进行激活,此字典就是标识是否已激活了,类似的还有手机号确认PhoneNumberConfirmed
SecurityStamp:当在后台修改用户的跟安全相关的信息,比如修改了密码,更新用户到数据库会自动更新此值,这样用户下次登录时可以判断此值变化了,要求用户重新登录。
ConcurrencyStamp:跟上面有点类似,不过主要是做并发冲突的,是针对整个用户所有字段更新的,比如有两个现成都在修改同一个用户,会比对此字段是否另一个用户也在修改,如果是则其中一个线程修改失败
AccessFailedCount:登录失败次数,多次登录失败会锁定的功能会用到此字段。

3.3、额外概念

UserClaims:当用户做第三方登录(比如:微信、google)时,可能会获取第三方中关于用户的额外字段,这些字段就是存储在用户的Claim中的,所以这是一个一对多关系,一个用户拥有多个第三方Claim
UserLogins:用户第三方登录,记录某用户绑定的第三方登录的信息,比如openid啥的,所以也是一对多关系,一个用户可能绑定多个第三方登录
UserTokens:用户登录 发送手机验证码、或邮箱激活用的验证码之类的,也是一对多

3.x、如何扩展

定义IdentityUser子类、定义UserManager子类、定义RoleManager子类 ,由于这些还没说,所以后面讲吧

四、跟用户相关的数据操作接口Store

定义一堆接口来对用户进行管理,这些功能都是跟数据库操作相关的。看看这些接口是干啥用的:

  • IUserLoginStore:管理用户绑定第三方登录的数据操作相关功能,比如获取绑定的第三方登录列表
  • IUserClaimStore:用户做第三方登录获取的用户的第三方账号相关字段值
  • IUserPasswordStore:专门针对用户密码的处理,比如设置获取某用户的密码
  • IUserSecurityStampStore:参考
  • IUserEmailStore:针对用户邮箱地址的处理
  • 略....

为毛要分开定义呢?还是那个话你可以对单独的功能进行扩展。

可以看到整个继承体系使用了很多泛型,目的是方便我们将来进行扩展,比如:我们将来可能使用int作为用户的主键、也可能自定义一个用户子类扩展更多字段、也可能继承Role实现更多属性、也可能不使用EF,也可能使用EF,但是希望用自己的DBContext等等。。。。

为了将来扩展时尽量写更少的代码,所以实现了抽象类,也提供了默认子类,所以将来如果没有特殊需求,对用户管理的数据操作就完全不用自己写,有特殊需求是简单实现一个子类就ok拉,最最最复杂的情况我们才需要自己来实现这个接口

默认情况下使用UserStore这个类,它实现上面说的所有接口。

五、UserManager

用户管理的业务逻辑类

内部使用IUserStore

最奇葩的是内部针对数据库操作的上面说了分开接口定义的,但是在这个类内部使用时只注入了IUserStore,然后对特殊步骤的处理是直接拿这个强转的。。所以将来扩展时我们必须重写单独那个方法,而不是只替换Store

AspNetUserManager<TUser>子类提供identity框架与asp.net框架结合的,主要就体现当前请求的CancellationToken的处理

待补充...........

六、SignInManager

用来结合identity系统和 身份验证系统的,主要提供登录、注销相关功能。登录又分为多种:账号密码登录、第三方登录、双因素登录

七、启动配置

7.1、AddDefaultIdentity

  1. 注册身份验证需要的核心服务;设置默认身份验证方案为“Identity.Appliction”,设置默认登录使用的身份验证方案为"Identity.External"。
  2. 注册多个身份验证验证方案,其中就包含我们最常用的基于cookie的身份验证方案以及第三方登录、双因素登录等对应的身份验证方案
  3. 注册标识系统跟用户管理相关的各种服务(这些服务主要在UserManager中被使用)
 1public static IdentityBuilder AddDefaultIdentity<TUser>(this IServiceCollection services, Action<IdentityOptions> configureOptions) where TUser : class
{
services.AddAuthentication(o =>
{
o.DefaultScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies(o => { }); return services.AddIdentityCore<TUser>(o =>
{
o.Stores.MaxLengthForKeys = ;
configureOptions?.Invoke(o);
})
.AddDefaultUI()
.AddDefaultTokenProviders();
}

AddDefaultIdentity

7.2、AddIdentityCookies

这里对应上面的步骤2,上面源码第8行

添加多个基于基于cookie的身份验证方案,具体如下:

  1. 名称=“Identity.Appliction”;  选项类型=CookieAuthenticationOptions;  身份验证处理器类型=CookieAuthenticationHandler
  2. 名称=Identity.External”;      选项类型=CookieAuthenticationOptions;  身份验证处理器类型=CookieAuthenticationHandler
  3. 名称=“Identity.TwoFactorRememberMe”;    选项类型=CookieAuthenticationOptions;  身份验证处理器类型=CookieAuthenticationHandler
  4. 名称=“Identity.TwoFactorUserId”;      选项类型=CookieAuthenticationOptions;  身份验证处理器类型=CookieAuthenticationHandler

可以看到这4个身份验证方案的身份验证处理器和选项对象的类型都是一样的,只是方案名称不同,当然选项的值也不同。
第1个身份验证方案是我们最常用的基于cookie的身份验证,且它是默认身份验证方案,意思是将来请求抵达时身份验证中间件将尝试从cookie中获取用户标识。
第2个身份验证方案是跟第三方登录相关的,第3、4个是跟双因素登录(估计类似手机验证码登录)相关的,后期再说。

 public static class IdentityCookieAuthenticationBuilderExtensions
{
public static IdentityCookiesBuilder AddIdentityCookies(this AuthenticationBuilder builder)
=> builder.AddIdentityCookies(o => { }); public static IdentityCookiesBuilder AddIdentityCookies(this AuthenticationBuilder builder, Action<IdentityCookiesBuilder> configureCookies)
{
var cookieBuilder = new IdentityCookiesBuilder();
cookieBuilder.ApplicationCookie = builder.AddApplicationCookie();
cookieBuilder.ExternalCookie = builder.AddExternalCookie();
cookieBuilder.TwoFactorRememberMeCookie = builder.AddTwoFactorRememberMeCookie();
cookieBuilder.TwoFactorUserIdCookie = builder.AddTwoFactorUserIdCookie();
configureCookies?.Invoke(cookieBuilder);
return cookieBuilder;
} public static OptionsBuilder<CookieAuthenticationOptions> AddApplicationCookie(this AuthenticationBuilder builder)
{
builder.AddCookie(IdentityConstants.ApplicationScheme, o =>
{
o.LoginPath = new PathString("/Account/Login");
o.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
};
});
return new OptionsBuilder<CookieAuthenticationOptions>(builder.Services, IdentityConstants.ApplicationScheme);
} public static OptionsBuilder<CookieAuthenticationOptions> AddExternalCookie(this AuthenticationBuilder builder)
{
builder.AddCookie(IdentityConstants.ExternalScheme, o =>
{
o.Cookie.Name = IdentityConstants.ExternalScheme;
o.ExpireTimeSpan = TimeSpan.FromMinutes();
});
return new OptionsBuilder<CookieAuthenticationOptions>(builder.Services, IdentityConstants.ExternalScheme);
} public static OptionsBuilder<CookieAuthenticationOptions> AddTwoFactorRememberMeCookie(this AuthenticationBuilder builder)
{
builder.AddCookie(IdentityConstants.TwoFactorRememberMeScheme, o => o.Cookie.Name = IdentityConstants.TwoFactorRememberMeScheme);
return new OptionsBuilder<CookieAuthenticationOptions>(builder.Services, IdentityConstants.TwoFactorRememberMeScheme);
} public static OptionsBuilder<CookieAuthenticationOptions> AddTwoFactorUserIdCookie(this AuthenticationBuilder builder)
{
builder.AddCookie(IdentityConstants.TwoFactorUserIdScheme, o =>
{
o.Cookie.Name = IdentityConstants.TwoFactorUserIdScheme;
o.ExpireTimeSpan = TimeSpan.FromMinutes();
});
return new OptionsBuilder<CookieAuthenticationOptions>(builder.Services, IdentityConstants.TwoFactorUserIdScheme);
}
}

7.2.1、OptionsBuilder<TOptions>

若要完全掌握asp.net core的选项模型请参考:https://www.cnblogs.com/artech/p/inside-asp-net-core-06-01.html

通常我们定义一样选项模型的赋值逻辑是在Startup中通过如下方式来定义

 public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(opt => {
//选项对象的赋值逻辑
//opt...
});
services.PostConfigure<CookiePolicyOptions>(opt => {
//选项对象的覆盖逻辑
//opt...
});

若我们开发一个功能组件,可以调用方一个机会可以对我们组件进行配置,通常我们提供一个选项对象,调用方可以通过上面的方式来定义选项对象的赋值逻辑,我们组件内部通过依赖注入直接使用这个选项对象就可以了。更好的方式是我们的组件向外暴露一个OptionsBuilder<TOptions>,其中TOptions就是选项类型。下面直接通过如何使用它来理解它的用途。假设我们的组件向调用方暴露一个OptionsBuilder<MyModule>

 var optBuilder = //我们的模块提供一个扩展方法返回OptionsBuilder<MyModuleOptions>
optBuilder.Configre(opt=>{
opt.XXXX = xxx..;
//.....
});

当然也提供了PostConfigure,可以发现返回的对象已经指明了选项对象的类型,因此后续的调用无需提供泛型参数,所以调用方会觉得这种方式更简单;而且此Builder对象是专门针对我们的模块的选项对象,所以更具相关性。

OptionsBuilder<TOptions>是一个通用类,所不同功能模块的不同选项对象都可以使用它。

7.2.2、IdentityCookiesBuilder

 public class IdentityCookiesBuilder
{
public OptionsBuilder<CookieAuthenticationOptions> ApplicationCookie { get; set; }
public OptionsBuilder<CookieAuthenticationOptions> ExternalCookie { get; set; }
public OptionsBuilder<CookieAuthenticationOptions> TwoFactorRememberMeCookie { get; set; }
public OptionsBuilder<CookieAuthenticationOptions> TwoFactorUserIdCookie { get; set; }
}

7.2.3、如何为每种身份验证方案设置选项

添加一个身份验证方案通常需要:设置方案名、指定身份验证处理器类型、配置身份验证选项对象。根据上面的说明及源码我们发现AddDefaultIdentity中的AddIdentityCookies只是添加了4个身份验证方案,分别设置了名称,并因为AddCookie,所以都是添加的CookieAuthenticationHandler作为身份验证处理器的,那么选项对象如何配置呢?其实就是调用AddIdentityCookies丢入一个委托进行配置的,只是AddDefaultIdentity中没有给了一个空委托 .AddIdentityCookies(o => { }); ,完全可以通过类似如下代码进行配置

 .AddIdentityCookies(o => {
//配置默认身份验证的选项
o.ApplicationCookie.Configre(opt=>{}
opt.....//配置
);
//配置第三方登录的相关选项
o.ExternalCookie.Configre(opt=>{}
opt.....//配置
);
....双因素登录...
});

遗憾的是AddDefaultIdentity没有给我机会来配置这些选项。可能之所以叫AddDefault的原因,其实安全可以在参数中多提供一个委托,内部做下回调,从而允许我们可以进行配置。话说回来我们完全可以不使用AddDefaultIdentity,而是手动去Startup中注册相关服务并配置选项。类似下面这样

 public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"))); services.AddAuthentication(o =>
{
o.DefaultScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
}).AddApplicationCookie().Configure(opt=> {
//配置此方案的选项
});
services.AddIdentityCore<IdentityUser>(o =>
{
o.Stores.MaxLengthForKeys = ;
})
.AddDefaultUI()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>();

另外如果我们不配置则这些选项对象将使用默认值,参考:asp.net core 3.x 身份验证-2启动阶段的配置

7.3、AddIdentityCore

对应总步骤3,注册Identity(标识系统)跟用户管理相关的服务。具体参考下面的源码:

 public static IdentityBuilder AddIdentityCore<TUser>(this IServiceCollection services, Action<IdentityOptions> setupAction) where TUser : class
{
// Services identity depends on
services.AddOptions().AddLogging(); // Services used by identity
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser>>();
services.TryAddScoped<UserManager<TUser>>();
if (setupAction != null)
{
services.Configure(setupAction);
}
return new IdentityBuilder(typeof(TUser), services);
}

7.3.1、IdentityOptions

Identity系统中会用到各种配置选项就是通过它来提供的,它相当于一个选项对象的容器,里面包含各种子选项对象,看看源码

 public class IdentityOptions
{
public ClaimsIdentityOptions ClaimsIdentity { get; set; } = new ClaimsIdentityOptions();
public UserOptions User { get; set; } = new UserOptions();
public PasswordOptions Password { get; set; } = new PasswordOptions();
public LockoutOptions Lockout { get; set; } = new LockoutOptions();
public SignInOptions SignIn { get; set; } = new SignInOptions();
public TokenOptions Tokens { get; set; } = new TokenOptions();
public StoreOptions Stores { get; set; } = new StoreOptions();
}

这个选项对象的赋值逻辑是通过AddDefaultIdentity的委托参数传进来的,这意味着我们配置时可以通过如下方式来配置Identity系统的选项:

 services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.Password.RequiredLength = ;//密码相关配置
options.SignIn.RequireConfirmedEmail = true;//登录相关配置
//其它相关配置....
})
.AddEntityFrameworkStores<ApplicationDbContext>();

在登录管理器SignInManager、UserManager等多个组件都会使用到此选项对象。

7.3.2、IdentityBuilder

AddIdentityCore方法只是注册与用户管理相关服务,这是一种最小注册,如果还希望使用更多Identity系统提供的功能还需要注册其它相关服务,注册这些服务的方法就定义在IdentityBuilder,其实完全可以使用services进行注册,只是使用IdentityBuilder更简单,也跟具相关性,因为这些注册方法都是跟Identity系统相关的。

AddIdentityCore的返回类型是IdentityBuilder、也作为AddDefaultIdentity的最终返回对象。因此我们可以在调用AddDefaultIdentity后继续做其它服务的配置,例如在Startup中这样:

services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoleManager<RoleManager<IdentityUser>>()//注册角色管理器
.AddEntityFrameworkStores<ApplicationDbContext>();//注册基于ef的数据操作类

IdentityBuilder完整源码:

 public class IdentityBuilder
{
public IdentityBuilder(Type user, IServiceCollection services)
{
UserType = user;
Services = services;
}
public IdentityBuilder(Type user, Type role, IServiceCollection services) : this(user, services)
=> RoleType = role;
public Type UserType { get; private set; }
public Type RoleType { get; private set; }
public IServiceCollection Services { get; private set; }
private IdentityBuilder AddScoped(Type serviceType, Type concreteType)
{
Services.AddScoped(serviceType, concreteType);
return this;
}
public virtual IdentityBuilder AddUserValidator<TValidator>() where TValidator : class
=> AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(TValidator));
public virtual IdentityBuilder AddClaimsPrincipalFactory<TFactory>() where TFactory : class
=> AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(TFactory));
public virtual IdentityBuilder AddErrorDescriber<TDescriber>() where TDescriber : IdentityErrorDescriber
{
Services.AddScoped<IdentityErrorDescriber, TDescriber>();
return this;
}
public virtual IdentityBuilder AddPasswordValidator<TValidator>() where TValidator : class
=> AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(TValidator));
public virtual IdentityBuilder AddUserStore<TStore>() where TStore : class
=> AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(TStore));
public virtual IdentityBuilder AddTokenProvider<TProvider>(string providerName) where TProvider : class
=> AddTokenProvider(providerName, typeof(TProvider));
public virtual IdentityBuilder AddTokenProvider(string providerName, Type provider)
{
if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).GetTypeInfo().IsAssignableFrom(provider.GetTypeInfo()))
{
throw new InvalidOperationException(Resources.FormatInvalidManagerType(provider.Name, "IUserTwoFactorTokenProvider", UserType.Name));
}
Services.Configure<IdentityOptions>(options =>
{
options.Tokens.ProviderMap[providerName] = new TokenProviderDescriptor(provider);
});
Services.AddTransient(provider);
return this;
}
public virtual IdentityBuilder AddUserManager<TUserManager>() where TUserManager : class
{
var userManagerType = typeof(UserManager<>).MakeGenericType(UserType);
var customType = typeof(TUserManager);
if (!userManagerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
{
throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "UserManager", UserType.Name));
}
if (userManagerType != customType)
{
Services.AddScoped(customType, services => services.GetRequiredService(userManagerType));
}
return AddScoped(userManagerType, customType);
}
public virtual IdentityBuilder AddRoles<TRole>() where TRole : class
{
RoleType = typeof(TRole);
AddRoleValidator<RoleValidator<TRole>>();
Services.TryAddScoped<RoleManager<TRole>>();
Services.AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(UserClaimsPrincipalFactory<,>).MakeGenericType(UserType, RoleType));
return this;
}
public virtual IdentityBuilder AddRoleValidator<TRole>() where TRole : class
{
if (RoleType == null)
{
throw new InvalidOperationException(Resources.NoRoleType);
}
return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(TRole));
}
public virtual IdentityBuilder AddPersonalDataProtection<TProtector, TKeyRing>()
where TProtector : class,ILookupProtector
where TKeyRing : class, ILookupProtectorKeyRing
{
Services.AddSingleton<IPersonalDataProtector, DefaultPersonalDataProtector>();
Services.AddSingleton<ILookupProtector, TProtector>();
Services.AddSingleton<ILookupProtectorKeyRing, TKeyRing>();
return this;
}
public virtual IdentityBuilder AddRoleStore<TStore>() where TStore : class
{
if (RoleType == null)
{
throw new InvalidOperationException(Resources.NoRoleType);
}
return AddScoped(typeof(IRoleStore<>).MakeGenericType(RoleType), typeof(TStore));
}
public virtual IdentityBuilder AddRoleManager<TRoleManager>() where TRoleManager : class
{
if (RoleType == null)
{
throw new InvalidOperationException(Resources.NoRoleType);
}
var managerType = typeof(RoleManager<>).MakeGenericType(RoleType);
var customType = typeof(TRoleManager);
if (!managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
{
throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "RoleManager", RoleType.Name));
}
if (managerType != customType)
{
Services.AddScoped(typeof(TRoleManager), services => services.GetRequiredService(managerType));
}
return AddScoped(managerType, typeof(TRoleManager));
}
}

IdentityBuilder

八、基于RazorPages的UI

IdentityServiceCollectionExtensions

AddIdentity  ConfigureApplicationCookie ConfigureExternalCookie

asp.net core 3.x Identity的更多相关文章

  1. asp.net core系列 46 Identity介绍

    一. Identity 介绍 ASP.NET Core Identity是一个会员系统,可为ASP.NET Core应用程序添加登录功能.可以使用SQL Server数据库配置身份以存储用户名,密码和 ...

  2. 坎坷路:ASP.NET Core 1.0 Identity 身份验证(中集)

    上一篇:<坎坷路:ASP.NET 5 Identity 身份验证(上集)> ASP.NET Core 1.0 什么鬼?它是 ASP.NET vNext,也是 ASP.NET 5,以后也可能 ...

  3. asp.net core系列 52 Identity 其它关注点

    一.登录分析 在使用identity身份验证登录时,在login中调用的方法是: var result = await _signInManager.PasswordSignInAsync(Input ...

  4. asp.net core系列 49 Identity 授权(上)

    一.概述 授权是指用户能够访问资源的权限,如页面数据的查看.编辑.新增.删除.导出.下载等权限.ASP.NET Core 授权提供了多种且灵活的方式,包括:Razor pages授权约定.简单授权.R ...

  5. asp.net core系列 48 Identity 身份模型自定义

    一.概述 ASP.NET Core Identity提供了一个框架,用于管理和存储在 ASP.NET Core 应用中的用户帐户. Identity添加到项目时单个用户帐户选择作为身份验证机制. 默认 ...

  6. asp.net core系列 47 Identity 自定义用户数据

    一.概述 接着上篇的WebAppIdentityDemo项目,将自定义用户数据添加到Identity DB,自定义扩展的用户数据类应继承IdentityUser类, 文件名为Areas / Ident ...

  7. asp.net core 同时添加Identity和Bearer认证

    是这样的,网上介绍的Oauth认证一般都是授权服务器和资源服务器分开,但是我只想在一个网站中使用asp.net core自带的Identity认证给用户访问网站用,同时提供一些api接口通过Token ...

  8. Asp.net core 学习笔记 ( identity server 4 JWT Part )

    更新 : id4 使用这个 DbContext 哦 dotnet ef migrations add identity-server-init --context PersistedGrantDbCo ...

  9. ASP.NET Core 身份认证 (Identity、Authentication)

    Authentication和Authorization 每每说到身份验证.认证的时候,总不免说提及一下这2个词.他们的看起来非常的相似,但实际上他们是不一样的. Authentication想要说明 ...

随机推荐

  1. 关于Error executing aapt的问题

    这两天装了ubuntu 14.0.4系统,在这个系统上装了eclipse的android开发环境.原以为一切顺利,结果开发环境装完导入工程后,工程提示有红叉. R文件不能自动生成,按R文件不能自动生成 ...

  2. 「 从0到1学习微服务SpringCloud 」12 Zuul的综合使用

    上次讲了Zuul的基本使用,这篇讲的是综合使用,比如过滤器,限流,鉴权等应用 这里继续使用api-getway这个项目 过滤器 实现token验证(前置过滤器) 1.新建一个类,继承ZuulFilte ...

  3. Thematic002.字符串专题

    目录 Trie字典树 KMP AC自动机 Manacher 回文自动机 后缀数组 后缀自动机 Trie字典树 概念 我们先来看看什么是Trie字典树 可以发现,这棵树的每一条边都有一个字符 有一些点是 ...

  4. Ubuntu中部署Django项目的配置与链接MySQL

    Django的简介 MVT模式的介绍创建项目的虚拟环境 本次使用的是pip安装 一.更新 sudo apt update 二.安装pip sudo apt install python3-pip 三. ...

  5. 工作笔记-- 源码安装nginx

    源码安装nginx 1.安装nginx的依赖包 [root@localhost ~]# yum -y install gcc gcc-c++ openssl openssl-devel pcre pc ...

  6. Linux系统终端session保持服务工具-Tmux

    Tmux是非常流行的终端复用软件,通过一个终端登录远程主机并运行tmux后,在其中可以开启多个控制台而无需再“浪费”多余的终端来连接这台远程主机.相对于Screen,它更加先进:支持屏幕切分,而且具备 ...

  7. POJ Protecting the Flowers

    点击打开题目 题目大意 奶牛要吃花,FJ来赶牛,将第i头牛赶走要2*ti分钟,奶牛每分钟吃di个单位花,求花的最小损失 先赶吃花多的,Wrong Answer QAQ 我们可以算一算损失 设sum=d ...

  8. Qt常用UI控件读取、写入方法

    本文用途:快速备忘,方便调用,写熟了自然就记下了. [1.标签label] 读取:ui->label->text() 写入:ui->label->setText("p ...

  9. Java之函数式接口@FunctionalInterface详解(附源码)

    Java之函数式接口@FunctionalInterface详解 函数式接口的定义 在java8中,满足下面任意一个条件的接口都是函数式接口: 1.被@FunctionalInterface注释的接口 ...

  10. 一次完整的OCR实践记录

    一.任务介绍 这次的任务是对两百余张图片里面特定的编号进行识别,涉及保密的原因,这里就不能粘贴出具体的图片了,下面粘贴出一张类似需要识别的图片. 假如说我的数据源如上图所示,那么我需要做的工作就是将上 ...