.net core2.0下使用Identity改用dapper存储数据(实例讲解)
前言、
已经好多天没写博客了,鉴于空闲无聊之时又兴起想写写博客,也当是给自己做个笔记。过了这么些天,我的文笔还是依然那么烂就请多多谅解了。今天主要是分享一下在使用.net core2.0下的实际遇到的情况。在使用webapi时用了identity做用户验证。官方文档是的是用ef存储数据来使用dapper,因为个人偏好原因所以不想用ef。于是乎就去折腾。改成使用dapper做数据存储。于是就有了以下的经验。
一、使用identity服务
先找到startup.cs 这个类文件 找到 configureservices 方法
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() 现在已经弃用改为以下方法。
//使用验证 app.useauthentication();
这里的 applicationuser 是自定义的一个用户模型 具体是继承 identityuser 继承它的一些属性
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
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
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 如下:
[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。
接着来实现一下简单的登录登出。
/// <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(); }
/// <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 方法中加入
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存储数据(实例讲解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。