我正在使用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;
}
所以我遇到的主要问题是:
这是创建令牌的最佳方式吗?令牌将以URL发送。
我怎样才能解决1到3的问题?
答案 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)
我猜您正在使用加密,因此用户无法查看或修改此令牌中的数据。
您希望将对象序列化为字节数组。最好的方法是保持对象的输入而不是使用对象数组。然后使用像BinaryFormatter或protobuf(更好)的序列化库。 C# and .NET: How to serialize a structure into a byte[] array, using BinaryWriter?
它将使用类型化标记内容和序列化库自动完成。
要保护您的令牌,您必须使用MachineKey.Protect。
示例:
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