65.9K
CodeProject 正在变化。 阅读更多。
Home

Web 服务身份验证

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.35/5 (39投票s)

2005年1月19日

CPOL

3分钟阅读

viewsIcon

473275

downloadIcon

8349

一种简单地验证用户是否可以访问 WebService 的机制

Sample Image - WebServiceAuthentication.gif

引言

这是一个简单的机制,用于通过使用时间令牌和 MD5 哈希来加密密码,对 Web 服务进行用户身份验证。

背景

在 CodeProject 中,您可以找到至少两种其他机制来对 Web 服务进行用户身份验证。Dan_P 编写了 用于 Web 服务的身份验证作为使用 SOAP 标头的 Web 服务的简单身份验证。 但是用户名和密码以明文形式发送,并且没有对数据进行加密。HENDRIK R.使用 WSE 进行 Web 服务安全性简介的作者,这确实是一个完整的解决方案,但对于我的目的而言过于复杂。 用户名以明文形式发送,但是可以使用密码摘要来加密密码。 数据使用 XML 加密规范进行加密,以加密 SOAP 消息的各个部分。

我的解决方案介于上述两者之间。 用户名以明文形式发送,但是我使用 MD5 加密密码。 我不需要发送敏感数据,因此 Web 服务返回的数据未加密。

Using the Code

基本思想是使用 MD5 哈希代码作为加密系统,将 UserNamePassword 从客户端发送到 Web 服务。 这样,密码永远不会在网络上以明文形式传输。 Web 服务从数据库或其他任何地方检索用户密码,并使用相同的 MD5 算法来测试密码是否正确。 为了确保如果有人拦截了哈希,则可以用来在以后的时间进行身份验证,我在哈希 Key 字符串之前添加了一个时间戳。 最后,由于我们并不总是在同一台服务器上,并且/或者客户端时钟可能位于不同的时区或者只是未同步,因此我添加了请求包含服务器时间标记的令牌的可能性。

我提供了 ASP.NET C# 中的客户端示例,但是可以使用任何语言:ASP 经典 JScript 或 VBScript,PHP,Python 等。 无论如何,在客户端,我们需要使用 UserNamePassword 和先前从同一 Web 服务获得的哈希时间戳 Token 来构建 Key。 然后,我们可以调用该服务,我们将获得在网页上显示的答案(或身份验证失败警告)。

private void ButtonUseToken_Click(object sender, System.EventArgs e)
{
    string ret;
    string UserName, Password, ServiceName, Token;
    string Key, ToHash;

    UserName=this.TextBoxUserName.Text;
    Password=this.TextBoxPwd.Text;
    ServiceName=this.TextBoxService.Text;
    Token=this.TextBoxToken.Text;
    ToHash=UserName.ToUpper()+"|"+Password+"|"+Token;
    Key=Hash(ToHash)+"|"+UserName;

    ServicePointReference.ServicePoint Authenticate = 
                             new ServicePointReference.ServicePoint();
    ret=Authenticate.UseService(Key, ServiceName);
    this.ServResponse.Text=ret;
}

C# 中的 MD5 Hash 过程非常简单; 这是由 Vasudevan Deepak Kumar保护 Web 帐户中编写的。

private string Hash(string ToHash)
{
    // First we need to convert the string into bytes,
    // which means using a text encoder.
    Encoder enc = System.Text.Encoding.ASCII.GetEncoder();

    // Create a buffer large enough to hold the string
    byte[] data = new byte[ToHash.Length];
    enc.GetBytes(ToHash.ToCharArray(), 0, ToHash.Length, data, 0, true);

    // This is one implementation of the abstract class MD5.
    MD5 md5 = new MD5CryptoServiceProvider();
    byte[] result = md5.ComputeHash(data);

    return BitConverter.ToString(result).Replace("-", "").ToLower();
}

在 Web 服务服务器端,我仅实现了三种 Web 方法

GetToken 用于获取带有时间标记的令牌。 您以这种方式获得的令牌旨在用于基本 Authenticate 方法中,或者用于 UseService 中,后者也可以验证对请求的服务进行身份验证的用户的访问权限。 该系统的核心由 TestHash 实现。 在这里,密码是硬编码的,但是在提供的示例中,您还可以找到从数据库中获取密码的代码

private bool TestHash (string HashStr, 
             string UserName, int minutes, string ServiceName)
{
    string Pwd, ToHash;
    string sResult, sResultT, sResultToken;
    try
    {
        // JUST FOR TEST: the password is hard-coded:
        Pwd="SeCrEt";

        DateTime dt = DateTime.Now;
        System.TimeSpan minute = new System.TimeSpan(0,0,minutes,0,0);
        dt = dt-minute;
        //before hashing we have:
        //USERNAME|PassWord|YYYYMMDD|HHMM
        ToHash=UserName.ToUpper()+"|"+Pwd+"|"+dt.ToString("yyyyMMdd")+
                                             "|"+dt.ToString("HHmm");
        sResult = Hash(ToHash);
        //TokenWeGotBefore
        ToHash=dt.ToString("yyyyMMdd")+"|"+dt.ToString("HHmm");
        sResultToken = Hash(ToHash);
        //USERNAME|PassWord|TokenWeGotBefore
        ToHash=UserName.ToUpper()+"|"+Pwd+"|"+sResultToken;
        sResultT = Hash(ToHash);
    
        if ((sResult==HashStr) || (sResultT==HashStr)) 
            return true;
        else
            if (minutes==0) // allowed max 2 minutes - 1
                            // second to call web service
            return TestHash (HashStr, UserName, 1, ServiceName);
        else
            return false;
    }
    catch
    {
        return false;
    }
}

要向 Web 服务请求哈希的时间戳记令牌,该方法是

[WebMethod]
public string GetToken ()
{
    string ToHash, sResult;
    DateTime dt = DateTime.Now;
    ToHash=dt.ToString("yyyyMMdd")+"|"+dt.ToString("HHmm");
    sResult = Hash(ToHash);
    return sResult;
}

检查用户身份验证的方法也保持非常简单; 在实际应用程序中,您通常需要访问数据库以检查身份验证级别,并且可能需要将一些数据返回给调用方

[WebMethod]
public string UseService (string Key, string ServiceName)
{
    string [] HashArray;
    string UserName, level;

    // Key string: HASH|User|OptionalData
    HashArray=Key.Split('|');
    level = "-1";    //default level

    if (TestHash(HashArray[0], HashArray[1], 0, ServiceName))
    {
        try
        {
            UserName=HashArray[1];
            // JUST FOR TEST: the User authentication level is hard-coded
            // but may/should be retrieved from a DataBase
            switch (UserName)
            {
                case "MyUserName":
                    level="1";
                    break;
                case "OtherUser":
                    level="2";
                    break;
                default:
                    level="-1";
                    break;
            }
            if (level=="1") return "YOU ARE AUTHORIZED";
        }
        catch (Exception exc)
        {
            return "Authentication failure: " + exc.ToString();
        }
    }
    return "Authentication failure";
}

关注点

TestHash 检查 Hash 是否包含时间戳或已哈希的令牌,并且在失败的情况下再次调用自身:如果有人(例如)在 11:34:58 调用该服务,则 Key 从 11:34:00 到 11:35:59 有效,即在两分钟减去一秒的时间内有效。

客户端可以用任何语言实现:ASP 经典、JScript 或 VBScript、PHP、Python 等。 我打算下次也发布此代码……

历史

  • 2005 年 1 月 20 日 - 创建文章
  • 2009 年 11 月 25 日 - 更新了源代码
© . All rights reserved.