【发布时间】:2012-04-22 09:00:40
【问题描述】:
我想不使用内置的 WCF/c# 组件,
- 向 RESTful 服务验证客户端
- 在客户端的 API 调用中处理身份验证失败
这是一个教学练习:我意识到有内置的身份验证方法,我想从头开始这样做以了解它是如何工作的。
我有密码散列和检查逻辑以及验证密码的公开 REST 调用,但我不确定如何从那里继续。
背景
我正在努力为我的休息服务创建身份验证方法。
到目前为止,我已经成功地创建了密码、salt 的哈希值并存储了 salt,并且我已经成功地对用户进行了身份验证。但是,我不确定您将如何封装我所有的 wcf REST 请求,以便如果有任何请求(GET、POST),它会要求您登录,如果您没有登录。
因为我使用了自己的身份验证技术,而且我是 Web 服务和 C# 的新手,所以我真的不知道从哪里开始?
因此,我将向任何可以提供解决方案的人提供 300 个代表。
代码
这是我的休息服务:
[ServiceContract(Namespace = "http://tempuri.org")]
[XmlSerializerFormat]
public interface IService
{
.... all of my GET, POST, PUT and DELETE requests
{
[DataContract(Name="Student")]
[Serializable]
public class Student
{
[DataMember(Name = "StudentID")]
public string StudentID { get; set; }
[DataMember(Name = "FirstName")]
public string FirstName { get; set; }
[DataMember(Name = "LastName")]
public string LastName { get; set; }
[DataMember(Name = "Password")]
public string Password;
[DataMember(Name = "Salt")]
public byte[] Salt;
//note the use of public datamembers for password and salt, not sure how to implement private for this.
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
[Serializable]
public class Service: IService
{
#region Authentication, hash and salt
protected RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
public byte[] GenerateSalt() //Generate random salt for each password
{
byte[] salt = new byte[10000];
random.GetNonZeroBytes(salt);
return salt;
}
public static byte[] Hash(string value, byte[] salt) //hash and salt the password
{
return Hash(Encoding.UTF8.GetBytes(value), salt);
}
public static byte[] Hash(byte[] value, byte[] salt) // create hash of password
{
byte[] saltedValue = value.Concat(salt).ToArray();
return new SHA256Managed().ComputeHash(saltedValue); //initialise new isntance of the crypto class using SHA-256/32-byte (256 bits) words
}
public string AuthenticateUser(string studentID, string password) //Authentication should always be done server side
{
var result = students.FirstOrDefault(n => n.StudentID == studentID);
//find the StudentID that matches the string studentID
if (result != null)
//if result matches then do this
{
byte[] passwordHash = Hash(password, result.Salt);
string HashedPassword = Convert.ToBase64String(passwordHash);
//hash salt the string password
if (HashedPassword == result.Password)
//check if the HashedPassword (string password) matches the stored student.Password
{
return result.StudentID;
// if it does return the Students ID
}
}
return "Login Failed";
//if it doesnt return login failed
}
#endregion
我也是从控制台应用程序托管的,我没有 web.config 文件或 app.config 文件。而且因为我使用了自己的身份验证方法,所以我不确定基本身份验证是否可行。
我也不想为了保持服务 SOA 和无状态而保持会话。
控制台应用:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.Transport;
host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
Console.ReadLine();
}
}
}
请注意,在我的客户端,我做了一些非常基本的事情来进行身份验证:
private void Login_Click(object sender, RoutedEventArgs e)
{
//Authenticate user (GET Request)
string uri = string.Format("http://localhost:8000/Service/AuthenticateUser/{0}/{1}", textBox1.Text, passwordBox1.Password);
XDocument xDoc = XDocument.Load(uri);
string UserAuthenticationID = xDoc.Element("string").Value;
Int32 value;
if (Int32.TryParse(UserAuthenticationID, out value))
{
MainWindow authenticatedidentification = new MainWindow();
authenticatedidentification.SetLabel(UserAuthenticationID);
authenticatedidentification.Show();
this.Close();
}
else
{
label1.Content = UserAuthenticationID;
}
}
所以我不确定如果以上提到的任何其他内容必须携带到主应用程序,以便主应用程序访问这些休息请求。
【问题讨论】:
-
理想情况下,存储 3 个值 - 无论您将它们组合成一个二进制 blob,还是单独建模它们,您都需要它们 - a) 密码代码的版本,b) 密码盐,以及c) 密码哈希。存储 (a) 以便以后如果您需要切换到不同的算法,您可以并且可以知道您升级了哪些用户。存储 (b) 和 (c) 以便您可以执行身份验证。
-
为什么不使用已经内置了所有这些功能的 ASP.NET Membership? (msdn.microsoft.com/en-us/library/yh26yfzy.aspx)
-
我觉得我必须指出 Eric Lippert 关于编写自己的身份验证模块的建议:“让我给你所有关于滚动你自己的加密算法和安全系统的标准警告:不要。创建几乎安全但不太安全的安全系统非常非常容易。一个给你错误安全感的安全系统比根本没有安全系统更糟糕!”
-
Adam 承认您所说的非常正确,但请注意,出于学习目的,了解您在实施这些方法的内容和方式是一种很好的做法。
-
如果您希望服务安全地将用户/密码传递给服务,那么您需要使用 SSL。否则,您不妨只使用纯文本用户/密码。
标签: c# web-services rest authentication encryption