8.1标识框架

简介: ASP.NET Core提供了标识框架,采用RBAC(基于角色的访问控制),内置了对用户、角色等表的管理及相关接口,框架中提供了`IdentityUser<TKey>`和`IdentityRole<TTKey>`两个实体类型,Tkey为主键类型。

8.1标识框架

ASP.NET Core提供了标识框架,采用RBAC(基于角色的访问控制),内置了对用户、角色等表的管理及相关接口,框架中提供了IdentityUser<TKey>IdentityRole<TTKey>两个实体类型,Tkey为主键类型。

使用步骤:

1. NuGet安装Microsoft.AspNetCore.Identity.EntityFrameworkCore

2. 创建用户实体类和角色实体类

//每次直接使用IdentityUser<long>都要说明主键类型,所以直接继承

//IdentityUser类中已经内置了用户名、密码、邮箱等属性,如果想增加自己的属性,也可以使用继承

publicclassUser : IdentityUser<long>

{

    publicDateTimeCreationTime { get; set; }//增加创建时间和昵称两个自定义属性

    publicstring?NickName { get; set; }

}

 

publicclassRole : IdentityRole<long>

{

}

除了IdentityUser和IdentityRole,标识框架中还有IdentityRoleClaim、IdentityUserToken等实体类,这些实体类都有默认的表名,如果要修改,可以使用IEntityTypeConfiguration来对实体类进行配置

3. 创建标识上下文类,继承自IdentityDbContext

//泛型参数分别代表用户类型、角色类型、主键类型

publicclassIdDbContext : IdentityDbContext<User, Role, long>

{

    publicIdDbContext(DbContextOptions<IdDbContext>options)

        : base(options)

    {

    }

    protectedoverridevoidOnModelCreating(ModelBuildermodelBuilder)

    {

        base.OnModelCreating(modelBuilder);

        modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);

    }

}

可以通过这个类操作数据库,但是标识框架提供了RoleManagerUserManager类简化对数据库的操作,这些类封装了对IdentityDbContext的操作。

标识框架中的方法有执行失败的可能,所以有些方法可以通过Task<IdentityResult>的返回结果来验证是否失败,IdentityResult的Succeeded属性表示是否操作成功,如果失败,则可以从Errors属性中获取错误信息,

RoleManager常用方法:

方法 说明
Task<IdentityResult> CreateAsync(TRole role) 创建角色
Task<IdentityResult> DeleteAsync(TRole role) 删除角色
Task<bool> RoleExistsAsync(string roleName) 指定名字的角色是否存在
Task<TRole> FindByNameAsync(string roleName) 根据角色名字获取角色对象

UserManager常用方法:

方法 说明
Task<IdentityResult> CreateAsync(TUser user,string password) 创建用户
Task<IdentityResult> UpdateAsync(TUser user) 更新用户
Task<IdentityResult> DeleteAsync(TUser user) 删除用户
Task<IUser> FindByIdAsync(string userId) 根据Id查找用户
Task<IUser> FindByNameAsync(string userName) 根据name查找用户
Task<Bool> CheckPasswordAsync(TUser user,string password) 检查用户密码是否正确,如果失败则调用AccessFailedAsync记录失败次数
Task<IdentityResult> ChangePasswordAsync(TUser user,string currentPassword,string newPassword) 修改密码
Task<string> GeneratePasswordResetTokenAsync(TUser user) 生成令牌,用来重置密码
Task<IdentityResult> ResetPasswordAsync(TUser user,string token,string newPassword) 重置密码
Task<IdentityResult> AddToRoleAsync(TUser user,string role) 为用户增加角色
Task<IdentityResult> RemoveFromRoleAsync(TUser user,string role) 为用户删除角色
Task<IList<string>> GetRolesAsync(TUser user) 用户所拥有的所有角色
Task<bool> IsInRoleAsync(TUser user,string role) 判断用户是否具有某个角色
Task<bool> IsLockedOutAsync(TUser user) 判断用户是否被锁定
Task<DataTimeOffset?> GetLockoutEndDataAsync(TUser user) 获取锁定时间
Task<DataTimeOffset> SetLockoutEndDataAsync(TUser user,DataTimeOffset? lockoutEnd) 设置用户锁定时间
Task<IdentityResult> AccessFailedAsync(TUser user) 记录用户登陆失败次数,多次失败应当锁定一段时间

4. 向容器中注册服务

IServiceCollectionservices=builder.Services;

//对IdDbContext进行设置

services.AddDbContext<IdDbContext>(opt=> {

   stringconnStr=builder.Configuration.GetConnectionString("Default");

   opt.UseSqlServer(connStr);

});

services.AddDataProtection();

//添加标识框架的一些重要基础服务,如密码几位,是否要求有大小写

services.AddIdentityCore<User>(options=>

{

   options.Password.RequireDigit=false;

   options.Password.RequireLowercase=false;

   options.Password.RequireNonAlphanumeric=false;

   options.Password.RequireUppercase=false;

   options.Password.RequiredLength=6;

   //密码重置时所需要令牌

   options.Tokens.PasswordResetTokenProvider=TokenOptions.DefaultEmailProvider;

   //账户验证时所需要令牌(注册)

   options.Tokens.EmailConfirmationTokenProvider=TokenOptions.DefaultEmailProvider;

});

varidBuilder=newIdentityBuilder(typeof(User), typeof(Role), services);

//注册各种服务

idBuilder.AddEntityFrameworkStores<IdDbContext>()

   .AddDefaultTokenProviders()

   .AddRoleManager<RoleManager<Role>>()

   .AddUserManager<UserManager<User>>();

5. 使用Add-MigrationUpdate-database生成数据库

6. 编写控制器代码,对角色、用户进行操作

publicclassTest1Controller : ControllerBase

{

   privatereadonlyILogger<Test1Controller>logger;//注册日志

   privatereadonlyRoleManager<Role>roleManager;

   privatereadonlyUserManager<User>userManager;

   publicTest1Controller(ILogger<Test1Controller>logger,

       RoleManager<Role>roleManager, UserManager<User>userManager)

   {

       this.logger=logger;

       this.roleManager=roleManager;

       this.userManager=userManager;

   }

    [HttpPost]

    publicasyncTask<ActionResult>CreateUserRole()

    {

        boolroleExists=awaitroleManager.RoleExistsAsync("admin");//判断admin账户是否存在

        if (!roleExists)

        {

            Rolerole=newRole { Name="Admin"};

            varr=awaitroleManager.CreateAsync(role);

            if (!r.Succeeded)//框架会存在创建失败的情况,一般都要进行判断是否成功

            {

                returnBadRequest(r.Errors);

            }

        }

        Useruser=awaitthis.userManager.FindByNameAsync("yzk");//查找用户

        if (user==null)

        {

           //EmailConfirmed设置为true

           //使用邮箱注册时,发送验证码到邮箱,用户输入验证码后才能确认这个邮箱可用,EmailConfirmed属性表示邮箱是否确认过

           //如果邮箱确认是存在的,则可以像下面 这样直接使用

           //如果创建用户的时候不确定邮箱是否可用,则需要先调用UserManager的GenerateEmailConfirmationTokenAsync创建

           //一个字符串作为“确认令牌”,服务器将确认令牌发送到用户邮箱,用户在输入确认令牌的时候,调用UserManager的

           //ConfirmEmailAsync方法来验证令牌

            user=newUser{UserName="yzk",Email="yangzhongke8@gmail.com",EmailConfirmed=true};

            varr=awaituserManager.CreateAsync(user, "123456");//创建用户

            if (!r.Succeeded)

            {

                returnBadRequest(r.Errors);

            }

            r=awaituserManager.AddToRoleAsync(user, "admin");//增加角色

            if (!r.Succeeded)

            {

                returnBadRequest(r.Errors);

            }

        }

        returnOk();

    }

}

7. 编写登陆请求的操作方法

publicrecordLoginRequest(stringUserName,stringPassword);

 

[HttpPost]

publicasyncTask<ActionResult>Login(LoginRequestreq)

{

    stringuserName=req.UserName;

    stringpassword=req.Password;

    varuser=awaituserManager.FindByNameAsync(userName);

    if (user==null)

    {

        returnNotFound($"用户名不存在{userName}");

    }

    if (awaituserManager.IsLockedOutAsync(user))

    {

        returnBadRequest("LockedOut");

    }

    varsuccess=awaituserManager.CheckPasswordAsync(user, password);//验证密码是否正确

    if (success)

    {

        returnOk("Success");

    }

    else

    {

       //密码错误则记录一次登陆失败,达到次数后就锁定账户一段时间,防止暴力破解

       //失败次数和锁定时间可以在AddIdentityCore中设定

       //option.Lockout.DefaultLockoutTimesSpan和option.Lockout.MaxFailedAccessAttempts来修改

        varr=awaituserManager.AccessFailedAsync(user);

        if (!r.Succeeded)

        {

            returnBadRequest("AccessFailed failed");

        }

        returnBadRequest("Failed");

    }

}

实现密码重置

发送重置密码的请求

publicrecordSendResetPasswordTokenRequest(stringEmail);

 

[HttpPost]

publicasyncTask<IActionResult>SendResetPasswordToken(

            SendResetPasswordTokenRequestreq)

{

    stringemail=req.Email;

    varuser=awaituserManager.FindByEmailAsync(email);

    if (user==null)

    {

        returnNotFound($"邮箱不存在{email}");

    }

   //生成密码令牌

    stringtoken=awaituserManager.GeneratePasswordResetTokenAsync(user);

    logger.LogInformation($"向邮箱{user.Email}发送Token={token}");

    returnOk();

}

 

重置密码

publicrecordVerifyResetPasswordRequest(stringEmail,stringtoken,stringnewPassword);

 

publicasyncTask<IActionResult>VerifyResetPassword(

            SendResetPasswordTokenRequestreq)

{

    stringemail=req.Email;

    varuser=awaituserManager.FindByEmail(email);

    stringtoken=req.Token;

    stringpassword=req.NewPassword;

    varr=awaituserManager.ResetPasswordAsync(user,token,password);//重置密码

    returnOk();

}

 


相关文章
|
6月前
|
存储 算法 数据库
【C++ 软件设计思路】学习C++中如何生成唯一标识符:从UUID到自定义规则
【C++ 软件设计思路】学习C++中如何生成唯一标识符:从UUID到自定义规则
346 0
|
5月前
|
XML 搜索推荐 数据格式
资源描述框架的用途及实际应用解析
**RDF(资源描述框架)**是一种用于机器理解网络资源的框架,使用XML编写。它通过URI标识资源,用属性描述资源,便于计算机应用程序处理信息。RDF在语义网上促进信息的确切含义和自动处理,使得网络信息可被整合。RDF语句由资源、属性和属性值组成。RDF文档包括`&lt;rdf:RDF&gt;`根元素和`&lt;rdf:Description&gt;`元素,后者用`about`属性标识资源。RDF还支持容器(如`&lt;Bag&gt;`、`&lt;Seq&gt;`和`&lt;Alt&gt;`)来描述集合。RDFS是RDF的扩展,提供描述类和属性的框架,而达布林核心是一组预定义属性,用于描述文
166 0
|
6月前
|
前端开发
|
6月前
|
前端开发
开发指南002-前后端信息交互规范-返回类ResponseResult
返回类有两个,一般返回类ResponseResult和分页返回类PageResult,本篇介绍ResponseResult
|
小程序 前端开发 Java
java如何利用JWT和注解,自定义参数的方式优雅实现小程序用户Id管理
在我们的开发项目中,经常需要用到用户ID,比如在小程序商城系统中,我们将商品加入购物车,这时前端就需要发送请求,携带上用户的ID。基本上很多种请求操作都需要携带用户ID,如果每个请求都需要我们往data中添加id的话,那样需要写很多重复代码,并且代码也不美观;所以我们可以利用JWT跟注解的方式来实现;
201 0
|
API 图形学
U3D客户端框架之封装 DeviceUtil 获取设备唯一ID和设备型号
U3D客户端框架之封装 DeviceUtil 获取设备唯一ID和设备型号
|
JSON Java 数据格式
SpringMVC中请求参数的绑定【绑定机制、支持的数据类型、使用要求、乱码解决】(超详细)
SpringMVC中请求参数的绑定【绑定机制、支持的数据类型、使用要求、乱码解决】(超详细)
SpringMVC中请求参数的绑定【绑定机制、支持的数据类型、使用要求、乱码解决】(超详细)
|
开发框架 前端开发 .NET
Api:无法检索元数据,mvc:未将对象引用设置到对象的实例
Api:无法检索元数据,mvc:未将对象引用设置到对象的实例
Api:无法检索元数据,mvc:未将对象引用设置到对象的实例
【TP5项目统一规范】修改之前的获取数据信息
【TP5项目统一规范】修改之前的获取数据信息
206 0
【TP5项目统一规范】修改之前的获取数据信息
接口中可以包含的组成部分
接口中可以包含的组成部分   1.抽象方法   2.常量   3.默认方法(JDK8)   4.静态方法(JDK8)   5.私有方法(JDK9) 1.抽象方法   public abstract 返回值类型 方法名称(参数类型 参数名称);   注意:     1.接口中的抽象方法,修饰符如果自己写必须是:public abstract     2.接口中的抽象方法,修饰符可以省略不写,默认就是:public abstract     3.抽象方法只有方法头,不能有方法体大括号。
1514 0