Asp.Net Core Identity 隐私数据保护的实现

(编辑:jimmy 日期: 2024/10/8 浏览:2)

前言

Asp.Net Core Identity 是Asp.Net Core 的重要组成部分,他为 Asp.Net Core 甚至其他 .Net Core 应用程序提供了一个简单易用且易于扩展的基础用户管理系统框架。它包含了基本的用户、角色、第三方登录、Claim等功能,使用 Identity Server 4 可以为其轻松扩展 OpenId connection 和 Oauth 2.0 相关功能。网上已经有大量相关文章介绍,不过这还不是 Asp.Net Core Identity 的全部,其中一个就是隐私数据保护。

正文

乍一看,隐私数据保护是个什么东西,感觉好像知道,但又说不清楚。确实这个东西光说很难解释清楚,那就直接上图:

Asp.Net Core Identity 隐私数据保护的实现

这是用户表的一部分,有没有发现问题所在?用户名和 Email 字段变成了一堆看不懂的东西。仔细看会发现这串乱码好像还有点规律:guid + 冒号 +貌似是 base64 编码的字符串,当然这串字符串去在线解码结果还是一堆乱码,比如 id 为 1 的 UserName :svBqhhluYZSiPZVUF4baOQ== 在线解码后是²ðj"htmlcode">

 //注册Identity服务(使用EF存储,在EF上下文之后注册)
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
 //...
 options.Stores.ProtectPersonalData = true; //在这里启用隐私数据保护
})
//...
.AddPersonalDataProtection<AesProtector, AesProtectorKeyRing>(); //在这里配置数据加密器,一旦启用保护,这里必须配置,否则抛出异常

其中的AesProtector 和AesProtectorKeyRing 需要自行实现,微软并没有提供现成的类,至少我没有找到,估计也是这个功能冷门的原因吧。.Neter 都被微软给惯坏了,都是衣来伸手饭来张口。有没有发现AesProtectorKeyRing 中有KeyRing 字样?钥匙串,恭喜你猜对了,guid 就是这个钥匙串中一把钥匙的编号。也就是说如果加密的钥匙被盗,但不是全部被盗,那用户信息还不会全部泄露。微软这一手可真是狠啊!

接下来看看这两个类是什么吧。

AesProtector 是 ILookupProtector 的实现。接口包含两个方法,分别用于加密和解密,返回字符串,参数包含字符串数据和上面那个 guid,当然实际只要是字符串就行, guid 是我个人的选择,生成不重复字符串还是 guid 方便。

AesProtectorKeyRing 则是 ILookupProtectorKeyRing 的实现。接口包含1、获取当前正在使用的钥匙编号的只读属性,用于提供加密钥匙;2、根据钥匙编号获取字符串的索引器(我这里就是原样返回的。。。);3、获取所有钥匙编号的方法。

AesProtector

 class AesProtector : ILookupProtector
  {
    private readonly object _locker;

    private readonly Dictionary<string, SecurityUtil.AesProtector> _protectors;

    private readonly DirectoryInfo _dirInfo;

    public AesProtector(IWebHostEnvironment environment)
    {
      _locker = new object();

      _protectors = new Dictionary<string, SecurityUtil.AesProtector>();

      _dirInfo = new DirectoryInfo($@"{environment.ContentRootPath}\App_Data\AesDataProtectionKey");
    }

    public string Protect(string keyId, string data)
    {
      if (data.IsNullOrEmpty())
      {
        return data;
      }

      CheckOrCreateProtector(keyId);

      return _protectors[keyId].Protect(Encoding.UTF8.GetBytes(data)).ToBase64String();
    }

    public string Unprotect(string keyId, string data)
    {
      if (data.IsNullOrEmpty())
      {
        return data;
      }

      CheckOrCreateProtector(keyId);

      return Encoding.UTF8.GetString(_protectors[keyId].Unprotect(data.ToBytesFromBase64String()));
    }

    private void CheckOrCreateProtector(string keyId)
    {
      if (!_protectors.ContainsKey(keyId))
      {
        lock (_locker)
        {
          if (!_protectors.ContainsKey(keyId))
          {
            var fileInfo = _dirInfo.GetFiles().FirstOrDefault(d => d.Name == $@"key-{keyId}.xml") "key")"encryption")"masterKey")"key")"encryption")"iv")"key")"encryption")"BlockSize")"key")"encryption")"KeySize")"key")"encryption")"FeedbackSize")"key")"encryption")"Padding")"key")"encryption")"Mode")"htmlcode">
class AesProtectorKeyRing : ILookupProtectorKeyRing
  {
    private readonly object _locker;
    private readonly Dictionary<string, XDocument> _keyRings;
    private readonly DirectoryInfo _dirInfo;

    public AesProtectorKeyRing(IWebHostEnvironment environment)
    {
      _locker = new object();
      _keyRings = new Dictionary<string, XDocument>();
      _dirInfo = new DirectoryInfo($@"{environment.ContentRootPath}\App_Data\AesDataProtectionKey");

      ReadKeys(_dirInfo);
    }

    public IEnumerable<string> GetAllKeyIds()
    {
      return _keyRings.Keys;
    }

    public string CurrentKeyId => NewestActivationKey(DateTimeOffset.Now)"key")"id")"key")"id")".xml"))
      {
        using (var stream = fileInfo.OpenRead())
        {
          XDocument xmlDoc = XDocument.Load(stream);

          _keyRings.TryAdd(xmlDoc.Element("key")"id")"key")"activationDate")"key")"expirationDate")"key")"activationDate")"key")"expirationDate")"1.0", "utf-8", "yes");

            XElement key = new XElement("key");
            key.SetAttributeValue("id", masterKeyId);
            key.SetAttributeValue("version", 1);

            XElement creationDate = new XElement("creationDate");
            creationDate.SetValue(now);

            XElement activationDate = new XElement("activationDate");
            activationDate.SetValue(now);

            XElement expirationDate = new XElement("expirationDate");
            expirationDate.SetValue(now.AddDays(90));

            XElement encryption = new XElement("encryption");
            encryption.SetAttributeValue("BlockSize", 128);
            encryption.SetAttributeValue("KeySize", 256);
            encryption.SetAttributeValue("FeedbackSize", 128);
            encryption.SetAttributeValue("Padding", PaddingMode.PKCS7);
            encryption.SetAttributeValue("Mode", CipherMode.CBC);

            SecurityUtil.AesProtector protector = new SecurityUtil.AesProtector();
            XElement masterKey = new XElement("masterKey");
            masterKey.SetValue(protector.GenerateKey().ToBase64String());

            XElement iv = new XElement("iv");
            iv.SetValue(protector.GenerateIV().ToBase64String());

            xmlDoc.Add(key);
            key.Add(creationDate);
            key.Add(activationDate);
            key.Add(expirationDate);
            key.Add(encryption);
            encryption.Add(masterKey);
            encryption.Add(iv);

            xmlDoc.Save(
              $@"{dirInfo.FullName}\key-{masterKeyId}.xml");

            _keyRings.Add(masterKeyId, xmlDoc);

            return xmlDoc;
          }

          return NewestActivationKey(now);
        }
      }

      return NewestActivationKey(now);
    }

    private XDocument NewestActivationKey(DateTimeOffset now)
    {
      return _keyRings.Where(item =>
          DateTimeOffset.Parse(item.Value.Element("key")"activationDate")"key")"expirationDate")"key")"expirationDate")"text-align: center">Asp.Net Core Identity 隐私数据保护的实现

Asp.Net Core Identity 隐私数据保护的实现

  本文地址:https://www.cnblogs.com/coredx/p/12210232.html

  完整源代码:Github

  里面有各种小东西,这只是其中之一,不嫌弃的话可以Star一下。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

一句话新闻
一文看懂荣耀MagicBook Pro 16
荣耀猎人回归!七大亮点看懂不只是轻薄本,更是游戏本的MagicBook Pro 16.
人们对于笔记本电脑有一个固有印象:要么轻薄但性能一般,要么性能强劲但笨重臃肿。然而,今年荣耀新推出的MagicBook Pro 16刷新了人们的认知——发布会上,荣耀宣布猎人游戏本正式回归,称其继承了荣耀 HUNTER 基因,并自信地为其打出“轻薄本,更是游戏本”的口号。
众所周知,寻求轻薄本的用户普遍更看重便携性、外观造型、静谧性和打字办公等用机体验,而寻求游戏本的用户则普遍更看重硬件配置、性能释放等硬核指标。把两个看似难以相干的产品融合到一起,我们不禁对它产生了强烈的好奇:作为代表荣耀猎人游戏本的跨界新物种,它究竟做了哪些平衡以兼顾不同人群的各类需求呢?