在打代码之前先说一下思路。
登录的的时候服务端生成加密的字符串(用户名、id、当前时间)并且存入客户端cookie中,服务端的缓存中。对客户端的每次请求进行拦截,解密保存在cookie中的加密字符串。查看是否已过期,如果已过期跳转到登录页,并且删除cookie与缓存中的数据。如未过期修改缓存中的时间,并进行下一步操作。
加密解密的代码
引入包:Microsoft.Extensions.Configuration(读配置文件的时候会用到,加密key与解密key最好存在配置文件中,从配置文件中读取)
public class Encryption { private IConfiguration _configuration; public Encryption(IConfiguration configuration) { _configuration = configuration; } public byte[] key() { return ASCIIEncoding.ASCII.GetBytes(_configuration["DES:rgbKey"]); } public byte[] vector() { return ASCIIEncoding.ASCII.GetBytes(_configuration["DES:rgbIV"]); } ////// DES加密 /// /// /// public async Task<string> Encrypt(string str) { MemoryStream ms = null; CryptoStream cs = null; StreamWriter sw = null; DESCryptoServiceProvider des = new DESCryptoServiceProvider(); try { ms = new MemoryStream(); cs = new CryptoStream(ms, des.CreateEncryptor(this.key(), this.vector()), CryptoStreamMode.Write); sw = new StreamWriter(cs); sw.Write(str); sw.Flush(); cs.FlushFinalBlock(); return Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length); } finally { if (sw != null) sw.Close(); if (cs != null) cs.Close(); if (ms != null) ms.Close(); } } /// /// DES解密 /// /// /// public async Task<string> Decrypt(string str) { MemoryStream ms = null; CryptoStream cs = null; StreamReader sr = null; DESCryptoServiceProvider des = new DESCryptoServiceProvider(); try { ms = new MemoryStream(Convert.FromBase64String(str)); cs = new CryptoStream(ms, des.CreateDecryptor(this.key(), this.vector()), CryptoStreamMode.Read); sr = new StreamReader(cs); return sr.ReadToEnd(); } finally { if (sr != null) sr.Close(); if (cs != null) cs.Close(); if (ms != null) ms.Close(); } } }
操作Redis
引入包:StackExchange.Redis
连接字符串最好也存入配置文件中
public class RedisContent { public StackExchange.Redis.ConnectionMultiplexer Redis = null; public StackExchange.Redis.IDatabase db = null; public RedisContent(IConfiguration configuration) { _configuration = configuration; this.Redis = ConnectionMultiplexer.Connect($"{_configuration["Redis:dbconn"]}"); this.db = this.Redis.GetDatabase(); } private IConfiguration _configuration; }
定义一个特性类,对于一些不需要认证的接口,加上这个特性即可。相当于微软的[AllowAnonymous]认证中间件。这里的话我们自己写一个。
public class AllowAuthAttribute : Attribute { }
添加过滤器AuthorizeFilter。上面封装的一些方法,全部以注入的形式进行使用。
public class AuthorizeFilter : Attribute, IAuthorizationFilter { private readonly ILogger_logger; private readonly RedisContent _content; private readonly Encryption _encryption; public AuthorizeFilter(RedisContent content, Encryption encryption, ILogger logger) { _content = content; _encryption = encryption; _logger = logger; } public void OnAuthorization(AuthorizationFilterContext context) { var isDefined = false; var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; if (controllerActionDescriptor != null) { //判断请求的控制器和方法有没有加上AllowAuthAttribute(不需要认证) isDefined = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true) .Any(a => a.GetType().Equals(typeof(AllowAuthAttribute))); } if (isDefined) return; if (context.HttpContext.Request.Path == "/") return; string value = string.Empty; context.HttpContext.Request.Cookies.TryGetValue("userinfo", out value); if (string.IsNullOrEmpty(value)) context.HttpContext.Response.Redirect("https://localhost:44300/"); else { //解密cookie var decryptValueArray = _encryption.Decrypt(value).Result.Split("|"); string user = _content.db.StringGet($"{decryptValueArray[0]}-{decryptValueArray[2]}"); if (string.IsNullOrEmpty(user) || !user.Equals(value)) { _logger.LogError($"Token已过期/有误! Url:{context.HttpContext.Request.Path}"); context.HttpContext.Response.Cookies.Delete("userinfo"); context.HttpContext.Response.Redirect("https://localhost:44300/"); } else { //重新设置key的时间 _content.db.KeyExpire($"{decryptValueArray[0]}-{decryptValueArray[2]}", TimeSpan.FromMinutes(30)); return; } } } }
编写控制器中的代码
////// 验证登录 颁发Token 存入cookie /// /// /// /// [AllowAuth] public async Task LoginUser(string userName, string password) { if (string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password)) return Json(new { success = false, msg = "用户名或密码不能为空" }); //带参查询 var data = await _userInfoServices.QueryUserInfo(userName, password); if (data.Any()) { //得到uid,将uid也带进token中加密 var uid = data.ToList()[0].id; //加密 30分钟的有效期 var token = await _encryption.Encrypt(userName + "|" + DateTime.Now + "|" + uid + "|" + 30); //存入redis中 _content.db.StringSet($"{userName}" + "-" + uid, token, TimeSpan.FromMinutes(30)); //存入cookie中 Response.Cookies.Append("userinfo", token); return Json(new { success = true, msg = "登陆成功" }); } else { return Json(new { success = false, msg = "用户名或密码输入错误" }); } }
如有不足,还望见谅!