苦于认证的方法

G G*_* Gr 6 c# authentication encryption rest web-services

我想,不使用内置的WCF/c#组件,

  1. 将客户端验证为RESTful服务
  2. 处理客户端API调用上的身份验证失败

这是一个教学练习:我意识到有内置的身份验证方法,我想从头开始这样做,以了解它是如何工作的.

我有密码哈希和检查逻辑以及验证密码的公开REST调用,但我不确定如何从那里开始.

背景

我正在努力为我的休息服务创建一个身份验证方法.

到目前为止,我已经设法创建了密码,盐的哈希并存储了盐,我已经设法验证用户.但是我不知道,这样如果任何请求(GET,POST),它要求你登录并在如果你的登录不你会怎么封装了所有我的WCF REST请求.

因为我使用自己的身份验证技术,而且我是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 
Run Code Online (Sandbox Code Playgroud)

我正在从一个控制台应用程序托管,我没有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();

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,在我的客户端,我做了一些非常基本的事情来进行身份验证:

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

所以我不确定如果有上述任何内容,还有什么东西必须进入主应用程序,以便主应用程序访问这些休息请求.

imi*_*ers 0

所以通常这样做的方式是

  1. 客户端通过身份验证服务调用提供一些凭据
  2. 该服务验证这些凭据并返还一些身份验证令牌。
  3. 后续调用将使用该令牌进行身份验证。

    这可以通过发送令牌(例如http 摘要身份验证)或更安全的方式来完成,令牌是用于计算参数上的消息身份验证代码的密钥。这可以防止任何人篡改请求。

这里有关于如何在 WCF 中执行此操作的相当长的讨论。请参阅“安全注意事项”部分和“实施身份验证和授权”部分

假设您已经完成了此操作(或者您在每个请求中都发送了用户名和密码——这是一个坏主意,但这只是出于教育目的),并且您有一个 AuthenticateUser 方法,如果用户未经过身份验证,该方法将返回 false。现在,在每个公开的 REST 方法中,您添加此调用(参数可以是用户名和密码,也可以是身份验证令牌)

if (!AuthenticateUser(/* auth params here */))

{

    WebOperationContext.Current.OutgoingResponse.StatusCode =

        HttpStatusCode.Unauthorized;

    return;
}
Run Code Online (Sandbox Code Playgroud)

这会导致请求失败,并且客户端将收到 HTTP 403 Forbiden 响应。

我假设您正在使用 HttpWebRequest 来调用 REST API。

因此,在您的客户端程序中,在准备好请求后,添加您需要的任何参数,然后执行此操作

try
{
    var wResp = (HttpWebResponse)wReq.GetResponse();
    var wRespStatusCode = wResp.StatusCode;
}
catch (WebException we)
{
    var wRespStatusCode = ((HttpWebResponse)we.Response).StatusCode;
    if( wRespStatusCode == HttpStatusCode. Unauthorized)
    {
       // call to your sign in / login logic here
    } else{
        throw we;
    }
}
Run Code Online (Sandbox Code Playgroud)

您需要以某种方式在请求中包含身份验证令牌,无论是作为 get 或 post 参数还是在标头中。Post 或 Get 只是将参数添加到请求数据中。标头有点困难,我相信它在我上面引用的 MSDN 链接中进行了概述。