上一篇文章主要講了經銷商注冊的倉儲和領域邏輯的實現,我們先把應用服務協調完成經銷商注冊這部分暫停一下,后面文章統一講。
這篇文章主要講講經銷商登錄的倉儲和相關邏輯的實現。
在現代應用程序前后端分離的實現中,通常不是將用戶登錄的信息存儲在服務器端Session,因為會存在服務器Session無法傳遞的情況,也存在WebApi調用時
無法通過Authorize Attribute判斷用戶是否已經登錄並獲取用戶身份信息的問題。所以現代應用程序都是由服務器后端返回Token給客戶端,客戶端將Token存儲在客戶端
Session中,客戶端在請求后端接口時,帶上Token,服務器端就能夠識別客戶端是否經過身份驗證,而且可以直接拿到客戶端的身份。
要實現經銷商的登錄,主要由以下幾個步驟組成。
1.實現經銷商登錄時信息查詢的倉儲。
2.在應用服務中,單獨建立一個查詢文件夾放置經銷商登錄的查詢邏輯。
3.在登錄WebApi中,調用應用服務的查詢邏輯並分發Token。
1.實現經銷商登錄時信息查詢的倉儲:
public interface ILoginRepository { Guid UserLogin(string tel, string password); }
public class LoginEFCoreRepository : ILoginRepository { private readonly DbContext context; public LoginEFCoreRepository(DbContext context) { this.context = context; } public Guid UserLogin(string tel, string password) { var dealercontext = this.context as DealerEFCoreContext; var enpassword = MD5Encrption.GetMd5Str(password); var logindealer= dealercontext.Login.Where(p => p.Code == tel && p.Password == enpassword).FirstOrDefault(); if (logindealer != null) { return logindealer.DealerId; } return Guid.Empty; } }
2.應用服務中調用倉儲完成用戶登錄的查詢
public class UserLoginQuery:BaseAppSrv { private readonly IRepository irepository; private readonly ILoginRepository iloginrepository; public UserLoginQuery(IRepository irepository, ILoginRepository iloginrepository) { this.iloginrepository = iloginrepository; this.irepository = irepository; } public Guid Login(UserLoginDTO userlogindto) { try { using (irepository) { return iloginrepository.UserLogin(userlogindto.Telphone, userlogindto.Password); } } catch(Exception error) { throw error; } } }
3.在登錄WebApi中調用應用服務,並分發令牌
[AllowAnonymous] [HttpPost] [Route("UserLogin")] public ResultEntity<UserLoginResultDTO> UserLogin([FromBody] UserLoginDTO userlogindto) { var result = new ResultEntity<UserLoginResultDTO>(); var idealercontext = servicelocator.GetService<IDealerContext>(); var irepository = servicelocator.GetService<IRepository>(new ParameterOverrides { { "context", idealercontext } }); var iloginrepository = servicelocator.GetService<ILoginRepository>(new ParameterOverrides { { "context", idealercontext } }); UserLoginQuery userloginquery = new UserLoginQuery(irepository, iloginrepository); try { var dealerid = userloginquery.Login(userlogindto); if (dealerid != Guid.Empty) { var token = new JwtTokenBuilder() .AddSecurityKey(JwtSecurityKey.Create("msshcjsecretmsshcjsecret")) .AddSubject(userlogindto.Telphone) .AddIssuer("DDD1ZXSystem") .AddAudience("DDD1ZXSystem") .AddClaim("role", "NormalUser") .AddExpiry(600) .Build(); var userloginresultdto = new UserLoginResultDTO(); userloginresultdto.Tel = userlogindto.Telphone; userloginresultdto.DealerId = dealerid; userloginresultdto.Token = token.Value; result.IsSuccess = true; result.Data = userloginresultdto; result.Msg = "登錄成功!"; } else { result.ErrorCode = 300; result.Msg = "登錄失敗!"; } } catch (Exception error) { result.ErrorCode = 200; result.Msg = error.Message; } return result; }
這里的UserLoginDTO定義如下:
public class UserLoginDTO { public string Telphone { get; set; } public string Password { get; set; } }
這里的UserLoginResultDTO定義如下:
public class UserLoginResultDTO { public string Tel { get; set; } public Guid DealerId { get; set; } public string Token { get; set; } }
這里的JwtTokenBuilder定義如下:
public class JwtTokenBuilder { private SecurityKey securityKey = null; private string subject = ""; private string issuer = ""; private string audience = ""; private Dictionary<string, string> claims = new Dictionary<string, string>(); private int expiryInMinutes = 5; public JwtTokenBuilder AddSecurityKey(SecurityKey securityKey) { this.securityKey = securityKey; return this; } public JwtTokenBuilder AddSubject(string subject) { this.subject = subject; return this; } public JwtTokenBuilder AddIssuer(string issuer) { this.issuer = issuer; return this; } public JwtTokenBuilder AddAudience(string audience) { this.audience = audience; return this; } public JwtTokenBuilder AddClaim(string type,string value) { this.claims.Add(type, value); return this; } public JwtTokenBuilder AddExpiry(int expiryInMinutes) { this.expiryInMinutes = expiryInMinutes; return this; } public JwtToken Build() { var claims = new List<Claim> { new Claim(JwtRegisteredClaimNames.Sub,this.subject), new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()) }.Union(this.claims.Select(item => new Claim(item.Key, item.Value))); var token = new JwtSecurityToken(issuer: this.issuer, audience: this.audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(this.expiryInMinutes), signingCredentials: new SigningCredentials(this.securityKey, SecurityAlgorithms.HmacSha256)); return new JwtToken(token); } }
這里的BearerUserInfo定義如下:
public class BearerUserInfo:Controller { public string GetUserName() { var principal = HttpContext.User as ClaimsPrincipal; if (principal != null) { foreach(var claim in principal.Claims) { if (claim.Subject != null) { var subjectclaims = claim.Subject.Claims as List<Claim>; return subjectclaims[0].Value; } } } return null; } }
這里的JwtSecurityKey定義如下:
public static class JwtSecurityKey { public static SymmetricSecurityKey Create(string secret) { return new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); } }
這里的JwtToken定義如下:
public class JwtToken { private JwtSecurityToken token; public JwtToken(JwtSecurityToken token) { this.token = token; } public DateTime ValidTo => token.ValidTo; public string Value => new JwtSecurityTokenHandler().WriteToken(this.token); }
以上采用了.net core中關於OWIN的使用,具體不清楚的屬性和方法,可以參考OWIN中.net core的實現標准,這里就不累述了,具體可以參考微信公眾號中的視頻講解。
QQ討論群:309287205
DDD實戰進階視頻請關注微信公眾號: