6

組み込みのWCF/c#コンポーネントを使用せずに、

  1. クライアントをRESTfulサービスに認証する
  2. クライアントでのAPI呼び出しでの認証の失敗を処理する

これは教育的な演習です。認証には組み込みのメソッドがあることに気付きました。これを最初から実行して、すべてがどのように機能するかを理解したいと思います。

パスワードのハッシュとチェックのロジックと、パスワードを検証する公開されたREST呼び出しがありますが、そこから続行する方法がわかりません。

バックグラウンド

Restサービスの認証方法の作成に苦労しています。

これまでのところ、パスワードのハッシュを作成し、ソルトを作成して保存し、ユーザーを認証することができました。ただし、すべての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;
        }
    }

したがって、メインアプリがこれらの残りのリクエストにアクセスするために、上記のいずれかがあれば、メインアプリケーションに他に何を実行する必要があるかわかりません。

4

4 に答える 4

0

RESTサービスにOAuthまたはOpenIDを使用しないのはなぜですか?!OAuth2.0以前のバージョンがあります。クライアントとサーバーの実装もあります。RESTサービスに適したOAuthパス

独自のメカニズムを作成する必要はありません。

OAuthのメインサイト-http ://oauth.net/code/OAuth の動作、フローなどの説明があります。また、DotnetOpenAuthなどの実装へのリンクもあります。

最新の仕様-https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2。

DotNetOAuthのOAuth実装のサンプルは、Githubリポジトリ https://github.com/AArnott/dotnetopenid/tree/master/samplesに多数あります。

于 2012-05-02T14:14:15.213 に答える
0

したがって、これが通常行われる方法は次のとおりです。

  1. クライアントは、認証サービス呼び出しを介していくつかの資格情報を提供します
  2. サービスはそれらの資格情報を検証し、いくつかの認証トークンを返します。
  3. 後続の呼び出しでは、そのトークンを使用して認証します。

    これは、トークンを送信するか(httpダイジェスト認証など)、より安全に送信することで実行されます。トークンは、パラメータのメッセージ認証コードを計算するために使用されるキーです。これにより、誰もがリクエストを改ざんするのを防ぎます。

ここでは、WCFでこれを行う方法について長い議論があります。「セキュリティに関する考慮事項」のセクションと「認証と承認の実装」のセクションを参照してください。

つまり、これを実行し(または、リクエストごとにユーザー名とパスワードを送信する-悪い考えですが、これは教育目的のためだけです)、ユーザーが認証されていない場合にfalseを返すAuthenticateUserメソッドがあるとします。これで、公開されたすべてのRESTメソッドで、この呼び出しを追加します(パラメーターは、ユーザー名とパスワード、または認証トークンのいずれかです)。

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

{

    WebOperationContext.Current.OutgoingResponse.StatusCode =

        HttpStatusCode.Unauthorized;

    return;
}

これにより、リクエストは失敗し、クライアントはHTTP403Forbidenレスポンスを受け取ります。

HttpWebRequestを使用してRESTAPIを呼び出していると仮定します。

したがって、クライアントプログラムで、リクエストを準備した後、必要なパラメータを追加して、これを実行します

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;
    }
}

getまたはpostパラメータとして、またはヘッダーに、何らかの方法で認証トークンをリクエストに含める必要があります。PostまたはGetは、リクエストデータにパラメータを追加するだけです。ヘッダーはもう少し難しいです。上記で参照したMSDNリンクで概説されていると思います。

于 2012-05-02T07:06:07.053 に答える
-1

@jbtuleと@Damien_The_Unbelieverは、ハッシュ化されたパスワードを使用してソルトを保存することについて優れた点を示しています。

実装方法については、別のサービスメソッドとしてではなく、メソッド呼び出し自体の認証部分を作成します。その後、サービス呼び出しでクレデンシャルを渡すのはクライアント次第です。

このリンクでは、それを実現する方法、サーバーとクライアントからどのように見えるかなどについて詳しく説明しています。

編集:上記のリンクのようにメッセージクレデンシャルでユーザー名とパスワードを渡す代わりに、ログイントークンを渡して、リクエストを実行する前にWebサービスで有効であることを確認することができます。

于 2012-04-30T22:42:07.073 に答える
-2

私が最近(ここ数週間)行った方法は、IDispatchMessageInspectorを介したものです。メッセージインスペクタークラスでは、securityContext.AuthorizationContext.ClaimSetsを使用してクライアント(呼び出し元)の証明書を確認しましたが、カスタムヘッダー(User、Password)を使用して、OperationContext.Current.IncomingMessageHeadersを確認できます。また、AfterReceiveRequest()では、ユーザーが有効なユーザーでない場合はフォールトをスローするか、成功を示すために単にnullを返します。

次に、インスペクター(MessageInspector)をサービスクラスに追加する属性を作成しました。

[AttributeUsage(AttributeTargets.Class)]
public class AuthorizeAttribute : Attribute, IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase dispatcher in serviceHostBase.ChannelDispatchers)
        {
            var channelDispatcher = dispatcher as ChannelDispatcher;
            if (channelDispatcher != null)
            {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    var inspector = new MessageInspector();
                    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
                }
            }
        }

        //var config = new ServiceLayerConfiguration();
        //config.RequestProcessorImplementation = typeof(PassThruRequestProcessor);
        //config.Initialize();

    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

そして最後に、サービスクラスに属性を追加するだけです。

[AuthorizeAttribute]
public class OperaService : IMyService

必要に応じて詳細をお知らせします。私はまだ私の箱にクライアント/サービスアプリを持っています。:)

于 2012-04-30T23:11:59.973 に答える