使用唯一ID和数据字段创建令牌

时间:2017-04-06 21:34:57

标签: c#

我正在使用ASP.NET Core 1.1,我需要创建一个包含数据的唯一令牌。

因此每个令牌将由以下组成:UniqueID + Data1 + Data2 + ... + DataN。

UniqueId是一个Guid,Data对象可以是Int32,String等类型:

DateTime expires = DateTime.Now.AddHours(24);
Int32 userId = user.Id;
Boolean enable = true;

方法可能是这样的:

public String GenerateToken(Guid id, params[] Object data) {

  Byte[] idBin = id.ToByteArray();

  // 1. Convert each object to Byte array
  // 2. Concat all byte arrays into tokenData string

  String token = Convert.ToBase64String(tokenData.ToArray());

  // 3. Encrypt token

  return encryptedToken;

}

所以我遇到的主要问题是:

  1. 将每个对象转换为Byte数组 我知道如何转换特定类型而不是对象。
  2. 将所有字节数组Concat转换为tokenData字符串
  3. 加密令牌
  4. 这是创建令牌的最佳方式吗?令牌将以URL发送。

    我怎样才能解决1到3的问题?

4 个答案:

答案 0 :(得分:2)

保持原始方法我做到了:

public String GenerateToken(Guid id, params object[] allData)
{
    byte[] idBin = id.ToByteArray();
    byte[] total = new byte[] { };

    total.Concat(idBin);

    foreach (var data in allData)
    {
        BinaryFormatter bf = new BinaryFormatter();
        using (MemoryStream ms = new MemoryStream())
        {
            bf.Serialize(ms, data);
            total.Concat(ms.ToArray());
        }
    }

    String token = Convert.ToBase64String(total);

    return token;
}

这会将所有额外参数转换为1个连续字节数组。

我唯一排除的是加密,因为已经有一百万个例子:How to Encrypt and Decrypt(提示读过第一个答案

答案 1 :(得分:1)

我猜您正在使用加密,因此用户无法查看或修改此令牌中的数据。

  1. 您希望将对象序列化为字节数组。最好的方法是保持对象的输入而不是使用对象数组。然后使用像BinaryFormatter或protobuf(更好)的序列化库。 C# and .NET: How to serialize a structure into a byte[] array, using BinaryWriter?

  2. 它将使用类型化标记内容和序列化库自动完成。

  3. 要保护您的令牌,您必须使用MachineKey.Protect

  4. 示例:

    public String GenerateToken(TokenContent data)
    {
        byte[] data;
        using(var ms = new MemoryStream())
        {
            Serializer.Serialize(ms, cust);
            data = ms.ToArray();
        }
        var encryptedData = MachineKey.Protect(data, "TokenDataUrl");
        var token = Convert.ToBase64String(encryptedData);
        return token;
    }
    
    public TokenContent ReadToken(string token)
    {
        byte[] encryptedData = Convert.FromBase64String(token);
        var data = MachineKey.Unprotect(encryptedData , "TokenDataUrl");
        TokenContent content;
        using(var ms = new MemoryStream(data))
        {
            content = Serializer.Deserialize<TokenContent>(ms);
        }
        return content;
    }
    

答案 2 :(得分:1)

我肯定会考虑使用像Json.Net这样的东西。这将使您的所有序列化数据保持良好和跨平台。此外,由于您提到您使用的是Asp.Net Core,因此如果您想使用跨平台.Net标准库,则无法使用BinaryFormatter

举个例子,你可能会做类似的事情:

    public static string GenerateToken(Guid id, params object[] data)
    {
        var claims = new List<object>(data);

        claims.Add(new
        {
            id = id
        });

        string serialised = Newtonsoft.Json.JsonConvert.SerializeObject(claims);

        return serialised;
    }

所以你可以用以下方法调用方法:

GenerateToken(Guid.NewGuid(), "hello world!", 25, new { Test = "value" });

给你以下内容:

["hello world!",25,{"Test":"value"},{"id":"bf9e5d38-5ac4-4c6b-b68f-88136fc233cf"}]

可以只加密此字符串并将其传递给您的API,对其进行解密,然后将其反序列化为对象:

    public static object DeserialiseToken(string token)
    {
        object deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject(token);

        return deserialised;
    }

这将返回一个包含所有原始数据的对象。

请注意,因为我们使用params object[]作为参数,所以我们无法轻松创建键值对。当我们将变量传递给方法时,我们会丢失变量的原始名称,而且我们还没有真正了解应该调用data数组中每个条目的方法。例如,访问“你好世界!”字符串可能很乏味。

如果我们想稍后阅读,我们可能会遇到很难解释数据的问题。

改善它!

说了这么多,我想我们可以稍微改进一下。

我要做的第一件事就是为你的说法引入一个合适的模型。如果您可以保证您的令牌对于它们包含的数据都具有相同的“模型”,您可以拥有如下类:

public class Token
{
    public Guid Id { get; set; }

    public int UserId { get; set; }

    public bool Enable { get; set; }
}

并将其直接传递给Json.Net(或使用其他一些序列化器):

    string output = GenerateToken(new Token
    {
        Id = Guid.NewGuid(),
        Enable = false,
        UserId = 2062
    });

    public static string GenerateToken(Token claims)
    {
        string serialised = Newtonsoft.Json.JsonConvert.SerializeObject(claims);

        return serialised;
    }

当你回到反序列化json时,你可以将它直接映射到一个对象:

    public static Token DeserialiseToken(string token)
    {
        Token deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(token);

        return deserialised;
    }

您将拥有一个强类型对象,其中包含针对它们映射的所有声明。

您还应该考虑实际是否需要来加密您的令牌。一种流行的方法是JSON Web Token(JWT)标准,其中声明集是纯文本,但是与验证散列一起发送,其中声明与秘密一起被散列。

在用户修改声明的情况下,当令牌到达您的API时,它将使用该秘密重新声明声明,并且签名将不匹配,因此您将知道它已被篡改!

如果你没有在你的令牌中存储特别敏感的东西,那么这是一个非常好的方法。

答案 3 :(得分:0)

只是一个想法,但如果你想序列化(和反序列化)对象(任何C#.net对象)而不必为它们编写序列化路由,并假设Json作为输出格式是正常的,你可以使用NewtonSoft Json.Net:

http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_SerializeObject.htm

var output = JsonConvert.SerializeObject(object);

如果你想为这个对象创建一个唯一的哈希,你可以像这里一样使用SHA256:Hashing a string with Sha256