.net core2.0下使用Identity改用dapper存储数据(实例讲解)

时间:2022-01-09 08:21:08

前言、

已经好多天没写博客了,鉴于空闲无聊之时又兴起想写写博客,也当是给自己做个笔记。过了这么些天,我的文笔还是依然那么烂就请多多谅解了。今天主要是分享一下在使用.net core2.0下的实际遇到的情况。在使用webapi时用了identity做用户验证。官方文档是的是用EF存储数据来使用dapper,因为个人偏好原因所以不想用EF。于是乎就去折腾。改成使用dapper做数据存储。于是就有了以下的经验。

一、使用Identity服务

先找到Startup.cs 这个类文件 找到 ConfigureServices 方法

?
1
2
3
4
5
6
services.AddIdentity<ApplicationUser, ApplicationRole>().AddDefaultTokenProviders();//添加Identity
services.AddTransient<IUserStore<ApplicationUser>, CustomUserStore>();
services.AddTransient<IRoleStore<ApplicationRole>, CustomRoleStore>();
string connectionString = Configuration.GetConnectionString("SqlConnectionStr");
services.AddTransient<SqlConnection>(e => new SqlConnection(connectionString));
services.AddTransient<DapperUsersTable>();

然后在 Configure 方法 的 app.UseMvc() 前加入下列代码,net core 1.0的时候是app.UseIdentity() 现在已经弃用改为以下方法。

?
1
2
//使用验证
app.UseAuthentication();

这里的 ApplicationUser 是自定义的一个用户模型 具体是继承 IdentityUser 继承它的一些属性

?
1
2
3
4
5
6
public class ApplicationUser :IdentityUser
 {
  public string AuthenticationType { get; set; }
  public bool IsAuthenticated { get; set; }
  public string Name { get; set; }
 }

这里的 CustomUserStore 是自定义提供用户的所有数据操作的方法的类它需要继承三个接口:IUserStore,IUserPasswordStore,IUserEmailStore

IUserStore<TUser>接口是在用户存储中必须实现的唯一接口。 它定义了用于创建、 更新、 删除和检索用户的方法。

IUserPasswordStore<TUser>接口定义实现以保持经过哈希处理的密码的方法。 它包含用于获取和设置工作经过哈希处理的密码,以及用于指示用户是否已设置密码的方法的方法。

IUserEmailStore<TUser>接口定义实现以存储用户电子邮件地址的方法。 它包含用于获取和设置的电子邮件地址和是否确认电子邮件的方法。

这里跟.net core 1.0的实现接口方式有点不同。需要多实现 IUserEmailStore 才能不报错

具体代码如下。以供大家参考。

CustomUserStore

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using Microsoft.AspNetCore.Identity;
using System;
using System.Threading.Tasks;
using System.Threading;
 
namespace YepMarsCRM.Web.CustomProvider
{
 /// <summary>
 /// This store is only partially implemented. It supports user creation and find methods.
 /// </summary>
 public class CustomUserStore : IUserStore<ApplicationUser>,
  IUserPasswordStore<ApplicationUser>,
  IUserEmailStore<ApplicationUser>
 {
  private readonly DapperUsersTable _usersTable;
 
  public CustomUserStore(DapperUsersTable usersTable)
  {
   _usersTable = usersTable;
  }
 
  #region createuser
  public async Task<IdentityResult> CreateAsync(ApplicationUser user,
   CancellationToken cancellationToken = default(CancellationToken))
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
 
   return await _usersTable.CreateAsync(user);
  }
  #endregion
 
  public async Task<IdentityResult> DeleteAsync(ApplicationUser user,
   CancellationToken cancellationToken = default(CancellationToken))
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
 
   return await _usersTable.DeleteAsync(user);
 
  }
 
  public void Dispose()
  {
  }
 
  public Task<ApplicationUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
  {
   throw new NotImplementedException();
  }
 
  public async Task<ApplicationUser> FindByIdAsync(string userId,
   CancellationToken cancellationToken = default(CancellationToken))
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (userId == null) throw new ArgumentNullException(nameof(userId));
   Guid idGuid;
   if (!Guid.TryParse(userId, out idGuid))
   {
    throw new ArgumentException("Not a valid Guid id", nameof(userId));
   }
 
   return await _usersTable.FindByIdAsync(idGuid);
 
  }
 
  public async Task<ApplicationUser> FindByNameAsync(string userName,
   CancellationToken cancellationToken = default(CancellationToken))
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (userName == null) throw new ArgumentNullException(nameof(userName));
 
   return await _usersTable.FindByNameAsync(userName);
  }
 
  public Task<string> GetEmailAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
 
   return Task.FromResult(user.Email);
  }
 
  public Task<bool> GetEmailConfirmedAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   throw new NotImplementedException();
  }
 
  public Task<string> GetNormalizedEmailAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   throw new NotImplementedException();
  }
 
  public Task<string> GetNormalizedUserNameAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   throw new NotImplementedException();
  }
 
  public Task<string> GetPasswordHashAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
 
   return Task.FromResult(user.PasswordHash);
  }
 
  public Task<string> GetUserIdAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
 
   return Task.FromResult(user.Id.ToString());
  }
 
  public Task<string> GetUserNameAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
 
   return Task.FromResult(user.UserName);
  }
 
  public Task<bool> HasPasswordAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   throw new NotImplementedException();
  }
 
  public Task SetEmailAsync(ApplicationUser user, string email, CancellationToken cancellationToken)
  {
   throw new NotImplementedException();
  }
 
  public Task SetEmailConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken cancellationToken)
  {
   throw new NotImplementedException();
  }
 
  public Task SetNormalizedEmailAsync(ApplicationUser user, string normalizedEmail, CancellationToken cancellationToken)
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
   if (normalizedEmail == null) throw new ArgumentNullException(nameof(normalizedEmail));
 
   user.NormalizedEmail = normalizedEmail;
   return Task.FromResult<object>(null);
  }
 
  public Task SetNormalizedUserNameAsync(ApplicationUser user, string normalizedName, CancellationToken cancellationToken)
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
   if (normalizedName == null) throw new ArgumentNullException(nameof(normalizedName));
 
   user.NormalizedUserName = normalizedName;
   return Task.FromResult<object>(null);
  }
 
  public Task SetPasswordHashAsync(ApplicationUser user, string passwordHash, CancellationToken cancellationToken)
  {
   cancellationToken.ThrowIfCancellationRequested();
   if (user == null) throw new ArgumentNullException(nameof(user));
   if (passwordHash == null) throw new ArgumentNullException(nameof(passwordHash));
 
   user.PasswordHash = passwordHash;
   return Task.FromResult<object>(null);
 
  }
 
  public Task SetUserNameAsync(ApplicationUser user, string userName, CancellationToken cancellationToken)
  {
   throw new NotImplementedException();
  }
 
  public Task<IdentityResult> UpdateAsync(ApplicationUser user, CancellationToken cancellationToken)
  {
   return _usersTable.UpdateAsync(user);
  }
 }
}

二、使用使用dapper做数据存储

接着就是使用dapper做数据存储。该类的方法都是通过 CustomUserStore 调用去操作数据库的。具体代码如下。根据实际的用户表去操作dapper即可。

DapperUsersTable

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;
using System;
using Dapper;
using YepMarsCRM.Enterprise.DataBase.Model;
using YepMarsCRM.Enterprise.DataBase.Data;
 
namespace YepMarsCRM.Web.CustomProvider
{
 public class DapperUsersTable
 {
  private readonly SqlConnection _connection;
  private readonly Sys_AccountData _sys_AccountData;
  public DapperUsersTable(SqlConnection connection)
  {
   _connection = connection;
   _sys_AccountData = new Sys_AccountData();
  }
 
  private Sys_Account ApplicationUserToAccount(ApplicationUser user)
  {
   return new Sys_Account
   {
    Id = user.Id,
    UserName = user.UserName,
    PasswordHash = user.PasswordHash,
    Email = user.Email,
    EmailConfirmed = user.EmailConfirmed,
    PhoneNumber = user.PhoneNumber,
    PhoneNumberConfirmed = user.PhoneNumberConfirmed,
    LockoutEnd = user.LockoutEnd?.DateTime,
    LockoutEnabled = user.LockoutEnabled,
    AccessFailedCount = user.AccessFailedCount,
   };
  }
 
  #region createuser
  public async Task<IdentityResult> CreateAsync(ApplicationUser user)
  {
   int rows = await _sys_AccountData.InsertAsync(ApplicationUserToAccount(user));
   if (rows > 0)
   {
    return IdentityResult.Success;
   }
   return IdentityResult.Failed(new IdentityError { Description = $"Could not insert user {user.Email}." });
  }
  #endregion
 
  public async Task<IdentityResult> DeleteAsync(ApplicationUser user)
  {
   //string sql = "DELETE FROM Sys_Account WHERE Id = @Id";
   //int rows = await _connection.ExecuteAsync(sql, new { user.Id });
 
   int rows = await _sys_AccountData.DeleteForPKAsync(ApplicationUserToAccount(user));
 
   if (rows > 0)
   {
    return IdentityResult.Success;
   }
   return IdentityResult.Failed(new IdentityError { Description = $"Could not delete user {user.Email}." });
  }
 
 
  public async Task<ApplicationUser> FindByIdAsync(Guid userId)
  {
   string sql = "SELECT * FROM Sys_Account WHERE Id = @Id;";
   return await _connection.QuerySingleOrDefaultAsync<ApplicationUser>(sql, new
   {
    Id = userId
   });
  }
 
 
  public async Task<ApplicationUser> FindByNameAsync(string userName)
  {
   string sql = "SELECT * FROM Sys_Account WHERE UserName = @UserName;";
 
   return await _connection.QuerySingleOrDefaultAsync<ApplicationUser>(sql, new
   {
    UserName = userName
   });
 
   //var user = new ApplicationUser() { UserName = userName, Email = userName, EmailConfirmed = false };
   //user.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(user, "test");
   //return await Task.FromResult(user);
  }
 
  public async Task<IdentityResult> UpdateAsync(ApplicationUser applicationUser)
  {
   var user = ApplicationUserToAccount(applicationUser);
   var result = await _sys_AccountData.UpdateForPKAsync(user);
   if (result > 0)
   {
    return IdentityResult.Success;
   }
   return IdentityResult.Failed(new IdentityError { Description = $"Could not update user {user.Email}." });
  }
 }
}

三、使用UserManager、SignInManager验证操作

新建一个 AccountController 控制器 并在构造函数中获取 依赖注入的对象 UserManager 与 SignInManager 如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Authorize]
  public class AccountController : Controller
 {
  private readonly UserManager<ApplicationUser> _userManager;
  private readonly SignInManager<ApplicationUser> _signInManager;
  private readonly ILogger _logger;
 
public AccountController(UserManager<ApplicationUser> userManager,
   SignInManager<ApplicationUser> signInManager,
   ILoggerFactory loggerFactory)
  {
   _userManager = userManager;
   _signInManager = signInManager;
   _logger = loggerFactory.CreateLogger<AccountController>();
  }
 }

SignInManager 是提供用户登录登出的API ,UserManager 是提供用户管理的API。

接着来实现一下简单的登录登出。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/// <summary>
  /// 登录
  /// </summary>
  [HttpPost]
  [AllowAnonymous]
  public async Task<IActionResult> Login(ReqLoginModel req)
  {
   var json = new JsonResultModel<object>();
   if (ModelState.IsValid)
   {
    var result = await _signInManager.PasswordSignInAsync(req.UserName, req.Password, isPersistent: true, lockoutOnFailure: false);
    if (result.Succeeded)
    {
     json.code = "200";
     json.message = "登录成功";
    }
    else
    {
     json.code = "400";
     json.message = "登录失败";
    }
    if (result.IsLockedOut)
    {
     json.code = "401";
     json.message = "账户密码已错误3次,账户被锁定,请30分钟后再尝试";
    }
   }
   else
   {
    var errorMessges = ModelState.GetErrorMessage();
    json.code = "403";
    json.message = string.Join(",", errorMessges);
   }
   return json.ToJsonResult();
  }
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// <summary>
  /// 登出
  /// </summary>
  /// <returns></returns>
  [HttpPost]
  public async Task<IActionResult> LogOut()
  {await _signInManager.SignOutAsync();
   var json = new JsonResultModel<object>()
   {
    code = "200",
    data = null,
    message = "登出成功",
    remark = string.Empty
   };
   return json.ToJsonResult();
  }

四、使用Identity配置

在 ConfigureServices 方法中加入

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
services.Configure<IdentityOptions>(options =>
   {
    // 密码配置
    options.Password.RequireDigit = false;//是否需要数字(0-9).
    options.Password.RequiredLength = 6;//设置密码长度最小为6
    options.Password.RequireNonAlphanumeric = false;//是否包含非字母或数字字符。
    options.Password.RequireUppercase = false;//是否需要大写字母(A-Z).
    options.Password.RequireLowercase = false;//是否需要小写字母(a-z).
    //options.Password.RequiredUniqueChars = 6;
 
    // 锁定设置
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);//账户锁定时长30分钟
    options.Lockout.MaxFailedAccessAttempts = 3;//10次失败的尝试将账户锁定
    //options.Lockout.AllowedForNewUsers = true;
 
    // 用户设置
    options.User.RequireUniqueEmail = false; //是否Email地址必须唯一
   });
 
   services.ConfigureApplicationCookie(options =>
   {
    // Cookie settings
    options.Cookie.HttpOnly = true;
    //options.Cookie.Expiration = TimeSpan.FromMinutes(30);//30分钟
    options.Cookie.Expiration = TimeSpan.FromHours(12);//12小时
    options.LoginPath = "/api/Account/NotLogin"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
    //options.LogoutPath = "/api/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
    //options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied
    options.SlidingExpiration = true;
   });

五、其他

在实现的过程中遇到一些小状况。例如Identity不生效。是因为未在app.UseMvc() 之前使用造成的。 如果未登录会造成跳转。后来查看了.net core Identity 的源码后 发现 如果是ajax情况下 不会跳转而时 返回401的状态码页面。

然后就是Idenetity的密码加密 是用 PasswordHasher 这个类去加密的。如果想用自己的加密方式。只能通过继承接口去更改原本的方式。然后大致说到这么些。也当是给自己做做笔记。做得不好请大家多给点意见。多多谅解。谢谢。

以上这篇.net core2.0下使用Identity改用dapper存储数据(实例讲解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:http://www.cnblogs.com/JinJi-Jary/archive/2017/11/22/7879024.html