在打代码之前先说一下思路。

登录的的时候服务端生成加密的字符串(用户名、id、当前时间)并且存入客户端cookie中,服务端的缓存中。对客户端的每次请求进行拦截,解密保存在cookie中的加密字符串。查看是否已过期,如果已过期跳转到登录页,并且删除cookie与缓存中的数据。如未过期修改缓存中的时间,并进行下一步操作。

加密解密的代码

引入包:Microsoft.Extensions.Configuration(读配置文件的时候会用到,加密key与解密key最好存在配置文件中,从配置文件中读取)

    public class Encryption
{
private IConfiguration _configuration;
public Encryption(IConfiguration configuration)
{
_configuration = configuration;
} public byte[] EncrKey()
{
return ASCIIEncoding.ASCII.GetBytes(_configuration["MD5:EncrKey"]);
}
public byte[] DecrKey()
{
return ASCIIEncoding.ASCII.GetBytes(_configuration["MD5:DecrKey"]);
} /// <summary>
/// DES加密
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public async Task<string> Encrypt(string str)
{
MemoryStream ms = null;
CryptoStream cs = null;
StreamWriter sw = null; DESCryptoServiceProvider des = new DESCryptoServiceProvider();
try
{
var s= ASCIIEncoding.ASCII.GetBytes(_configuration["MD5:EncrKey"]);
ms = new MemoryStream();
cs = new CryptoStream(ms, des.CreateEncryptor(this.EncrKey(), this.DecrKey()), CryptoStreamMode.Write);
sw = new StreamWriter(cs);
sw.Write(str);
sw.Flush();
cs.FlushFinalBlock();
return Convert.ToBase64String(ms.GetBuffer(), , (int)ms.Length);
}
finally
{
if (sw != null) sw.Close();
if (cs != null) cs.Close();
if (ms != null) ms.Close();
}
} /// <summary>
/// DES解密
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
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.EncrKey(), this.DecrKey()), 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<AuthorizeFilter> _logger;
private readonly RedisContent _content;
private readonly Encryption _encryption;
public AuthorizeFilter(RedisContent content,
Encryption encryption,
ILogger<AuthorizeFilter> 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());
return;
}
}
}
}

编写控制器中的代码

        /// <summary>
/// 验证登录 颁发Token 存入cookie
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <returns></returns>
[AllowAuth]
public async Task<IActionResult> 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()[].id;
//加密 30分钟的有效期
var token = await _encryption.Encrypt(userName + "|" + DateTime.Now + "|" + uid + "|" + );
//存入redis中
_content.db.StringSet($"{userName}" + "-" + uid, token, TimeSpan.FromMinutes());
//存入cookie中
Response.Cookies.Append("userinfo", token);
return Json(new { success = true, msg = "登陆成功" });
}
else
{
return Json(new { success = false, msg = "用户名或密码输入错误" });
}
}

如有不足,还望见谅!

最新文章

  1. MVC5+EF6+AutoMapper+Bootstrap打造在线博客(1.0)
  2. Win7 64位下PowerDesigner连接64位Oracle11g数据库
  3. python核心编程第六章练习6-14
  4. CSAPP(前言)
  5. [51单片机] EEPROM 24c02 [I2C代码封装-保存实现流水灯]
  6. uva 558 Bellman_Ford
  7. 【Deep Learning学习笔记】Dynamic Auto-Encoders for Semantic Indexing_Mirowski_NIPS2010
  8. (转)How to build an Apple Push Notification provider server (tutorial)
  9. python教程,文章list
  10. sublime text 插件开发
  11. 从一般分布式设计看HDFS设计思想与架构
  12. iOS架构设计-URL缓存
  13. Code Complete
  14. IGMP协议
  15. JEECG 新手常见问题大全,入门必读
  16. lufylegend:图片的加载和显示
  17. mysql数据库字符集初步理解
  18. edge中断分析
  19. logstash5.x安装及简单运用
  20. The Definitive Guide To Django 2 学习笔记(九) 第五章 模型 (一)数据库访问

热门文章

  1. scrapy爬虫提取网页链接的两种方法以及构造HtmlResponse对象的方式
  2. matplotlib 中的一些参数设置
  3. python3正则提取字符串里的中文
  4. C# 基础知识系列- 4 面向对象
  5. Contest 154
  6. bugku论剑场web解题记录
  7. PMP备考日记(一)
  8. 3D画廊
  9. nltk 获取 gutenberg 语料,gensim 生成词库和 onehot 编码
  10. 技术大佬:我去,你竟然还在用 try–catch-finally