参考文档:
1、https://docs.microsoft.com/zh-cn/aspnet/core/security/?view=aspnetcore-2.2
2、https://github.com/aspnet/AspNetCore.Docs/tree/live/aspnetcore/security/authentication/identity/sample/src
3、https://blog.youkuaiyun.com/sD7O95O/article/details/78623698
在Core2.2中,Identity的ORM默认使用了EF,但是这样,一些老项目迁移过程中就会遇到问题,因为很多项目还是使用的sql语句的方式访问数据库,这种情况下,显然不能重构项目为EF,不过,微软还是给Identity留下了自定义ORM的入口。
因为官方默认使用EF+sqlserver且使用了CodeFirst,所以下边的例子则为dapper+oracle,完全使用sql语句访问数据库,尽可能做到差异化。
一、数据库结构:
因为是简单demo,所以配置一个简单的数据库,一个表,两个列足矣
sys_user(userid varchar2,username varchar2,password varchar2)
二、新建一个core2.2项目:
测试一下,可以运行:
二、基础配置:
1、appsettings.json配置文件中配置连接字符串“OracleConStr”:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"OracleConStr": "Data Source=****;Persist Security Info=True;User ID=**;Password=**;"
}
2、使用NuGet安装所需类库:
1、Dapper
2、Oracle.ManagedDataAccess.Core
3、创建SysUser实体类
public class SysUser : IIdentity
{
//必须实现的接口
public string AuthenticationType { get; set; }
public bool IsAuthenticated { get; set; }
public string Name { get; set; }
//根据表自定义的字段
public string UserID { get; set; }
public string UserName { get; set; }
public string NormalizedUserName { get; set; }
public string Password { get; set; }
public string PasswordHash { get; set; }
}
4、创建表sysuser对应的Dal类SysUserDal,使用Dapper实现基本的增删改查
public class SysUserDal
{
private readonly OracleConnection _connection;
public SysUserDal(OracleConnection connection)
{
_connection = connection;
}
public async Task<IdentityResult> CreateAsync(SysUser user)
{
string sql = "insert into sys_user (userid,username,password) values (:userid, :username,:password)";
int rows = await _connection.ExecuteAsync(sql, new { userid = user.UserID, username = user.UserName, password = user.PasswordHash });
if (rows > 0)
{
return IdentityResult.Success;
}
return IdentityResult.Failed(new IdentityError { Description = $"Could not insert user {user.UserID}." });
}
public async Task<IdentityResult> DeleteAsync(SysUser user)
{
string sql = "delete from sys_user where userid = :userid";
int rows = await _connection.ExecuteAsync(sql, new { user.UserID });
if (rows > 0)
{
return IdentityResult.Success;
}
return IdentityResult.Failed(new IdentityError { Description = $"Could not delete user {user.UserID}." });
}
public async Task<IdentityResult> UpdateAsync(SysUser user)
{
string sql = "update sys_user set password=:password,username=:username where userid=:userid";
int rows = await _connection.ExecuteAsync(sql, new { userid = user.UserID, password = user.PasswordHash });
if (rows > 0)
{
return IdentityResult.Success;
}
return IdentityResult.Failed(new IdentityError { Description = $"Could not update user {user.UserID}." });
}
public async Task<SysUser> FindByIdAsync(string userId)
{
string sql = "select * from sys_user where userid = :userid";
return await _connection.QuerySingleOrDefaultAsync<SysUser>(sql, new
{
userid = userId
});
}
public async Task<SysUser> FindByNameAsync(string userName)
{
string sql = "select * from sys_user where username = :username";
return await _connection.QuerySingleOrDefaultAsync<SysUser>(sql, new
{
username = userName
});
}
}
4、创建SysuserRole
public class SysUserRole
{
public string UserID { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
5、创建CustomUserStore
public class CustomUserStore : IUserStore<SysUser>, IUserPasswordStore<SysUser>
{
private readonly SysUserDal _usersTable;
public CustomUserStore(SysUserDal usersTable)
{
_usersTable = usersTable;
}
#region createuser
public async Task<IdentityResult> CreateAsync(SysUser 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(SysUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null) throw new ArgumentNullException(nameof(user));
return await _usersTable.DeleteAsync(user);
}
public void Dispose()
{
}
public async Task<SysUser> FindByIdAsync(string userId,
CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
if (userId == null) throw new ArgumentNullException(nameof(userId));
return await _usersTable.FindByIdAsync(userId);
}
public async Task<SysUser> 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> GetNormalizedUserNameAsync(SysUser user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetPasswordHashAsync(SysUser user, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(user.Password);
}
public Task<string> GetUserIdAsync(SysUser user, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(user.UserID);
}
public Task<bool> CheckPasswordAsync(SysUser user, string pwd,
CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(user.Password == pwd);
}
public Task<bool> IsEmailConfirmedAsync(SysUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(true);
}
public Task<string> GetUserNameAsync(SysUser user, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(user.UserID);
}
public Task<bool> HasPasswordAsync(SysUser user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetNormalizedUserNameAsync(SysUser 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(SysUser 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(SysUser user, string userName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> UpdateAsync(SysUser user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
6、创建CustomRoleStore
public class CustomRoleStore : IRoleStore<SysUserRole>
{
public Task<IdentityResult> CreateAsync(SysUserRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> DeleteAsync(SysUserRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public void Dispose()
{
}
public Task<SysUserRole> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<SysUserRole> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedRoleNameAsync(SysUserRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetRoleIdAsync(SysUserRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetRoleNameAsync(SysUserRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetNormalizedRoleNameAsync(SysUserRole role, string normalizedName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetRoleNameAsync(SysUserRole role, string roleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> UpdateAsync(SysUserRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
7、StartUp注入:ConfigureServices方法中添加
string connectionString = Configuration["OracleConStr"];
services.AddTransient(e => new OracleConnection(connectionString));
services.AddTransient<SysUserDal>();
services.AddIdentity<SysUser, SysUserRole>().AddDefaultTokenProviders();
services.AddTransient<IUserStore<SysUser>, CustomUserStore>();
services.AddTransient<IRoleStore<SysUserRole>, CustomRoleStore>();
8、StartUp中Configure的UseMVC之前添加
app.UseAuthentication();
至此,基本的代码配置就完成了,下一步就是在controller中调用
三、controller+view使用Identity,因为是简单demo,所以代码比较简单,并不考虑实际使用场景
1、创建AccountController
public class AccountController : Controller
{
private readonly UserManager<SysUser> _userManager;
private readonly SignInManager<SysUser> _signInManager;
public AccountController(UserManager<SysUser> userManager,
SignInManager<SysUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> Login()
{
string userid = "test";
string pwd = "123ABCabc.";
var result = await _signInManager.PasswordSignInAsync(userid, pwd, false, false);
if (result.Succeeded)
{
//dosomething
}
else if (result.IsLockedOut)
{
//dosomething
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
}
return View();
}
public async Task<IActionResult> Register()
{
var user = new SysUser { UserID = "test", UserName = "测试" };
var result = await _userManager.CreateAsync(user, "123ABCabc.");
if (result.Succeeded)
{
//await _signInManager.SignInAsync(user, isPersistent: false);
}
else
{
//dosomething
}
return View();
}
public async Task<IActionResult> GetUser()
{
var res = await _userManager.GetUserAsync(HttpContext.User);
return View();
}
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
return View();
}
}
2、创建每个Action的cshtml
3、debug模式运行程序,在Account中的所有Action打断点,在SysUserDal所有方法打断点,使用浏览器地址栏调用Action,观察Identity工作流程,此处使用Register进行测试,不细看Debug过程,只看结果。
https://localhost:XXXXXX/Account/Register
观察数据库数据,发现数据已经被添加:
至此,可以证明Identity的ORM基础替换已经成功,此时的项目结构:
其他安全和权限类配置可根据使用场景阅读Microsoft Docs自定义开发