Memakai UserName Authentication Di WSHttpBinding Pada WCF


Pada artikel ini, saya akan mencoba memakai fasilitas authentication dari WCF untuk membatasi agar hanya pengguna tertentu saja yang boleh mengakses web service. Untuk itu, saya mulai dengan membuat sebuah console application C# yang berfungsi sebagai web service provider. Saya tidak lupa menambahkan referensi ke System.ServiceModel dengan men-klik kanan proyek dan memilih menu Add Reference…. Saya kemudian membuat kode program sederhana seperti berikut ini:

using System.ServiceModel;
using System;
using System.ServiceModel.Description;

namespace LatihanWCFAuthentication
{
    [ServiceContract]
    public class Hitung
    {
        [OperationContract]
        public int tambah(int angka1, int angka2)
        {
            return angka1 + angka2;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Uri baseAddress = new Uri("http://localhost:80/Latihan");
            ServiceHost selfHost = new ServiceHost(typeof(Hitung), baseAddress);
            try
            {
                WSHttpBinding binding = new WSHttpBinding();                
                selfHost.AddServiceEndpoint(typeof(Hitung), binding, "LatihanService");
                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                selfHost.Description.Behaviors.Add(smb);                
                selfHost.Open();
                Console.WriteLine("Web services sudah aktif dan siap melayani request.");
                while (true)
                {
                    System.Threading.Thread.Sleep(50);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Telah terjadi kesalahan: {0}", ex.Message);
                selfHost.Abort();
            }
            Console.ReadLine();
        }
    }
}

Berikutnya, saya membuat sebuah proyek Console Application baru yang akan berperan sebagai web service consumer. Untuk menambahkan client proxy ke proyek baru ini, saya perlu menjalankan proyek yang berperan sebagai web service provider terlebih dahulu dengan memilih menu Debug, Start Without Debugging…. Setelah itu, saya men-klik kanan pada proyek web service consumer dan memilih menu Add Service Reference…. Saya memasukkan nilai http://localhost:80/Latihan pada Address dan WebServiceLatihan pada Namespace. Setelah itu, saya men-klik tombol OK. Saya kemudian mengubah isi Program.cs menjadi seperti berikut ini:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Consumer.WebServiceLatihan;

namespace Consumer
{
    class Program
    {
        static void Main(string[] args)
        {
            HitungClient client = new HitungClient();
            int angka1 = 10, angka2 = 20;
            int hasil = client.tambah(angka1, angka2);
            Console.WriteLine("Hasil = {0}", hasil);
        }
    }
}

Bila saya menjalankan program (dengan menekan tombol Ctrl+F5), saya akan memperoleh hasil seperti:

Hasil = 30

Terlihat bahwa web services dapat dipanggil oleh siapa saja secara bebas. Ada kalanya dibutuhkan pembatasan agar web service hanya boleh dipanggil oleh pihak berwenang. Salah satu solusi yang bisa dipakai adalah fasilitas authentication bawaan dari WCF. Fasilitas security yang ditawarkan oleh WCF berupa keamanan pada transport layer (HTTPS), message, atau keduanya. Fasilitas tersebut masing-masing diwakili oleh nilai System.ServiceModel.SecurityMode seperti None, Transport, Message dan TransportWithMessageCredential. Nilai SecurityMode.Transport menawarkan keamanan yang paling transparan karena memakai SSL. Selain SecurityMode.Transport, juga ada SecurityMode.Message yang menambahkan security data pada setiap service yang dipanggil (bekerja pada application layer bukan transport layer).

Karena ingin authentication berdasarkan username, saya perlu memakai SecurityMode.Message dengan menambahkan baris berikut ini pada web service provider:

binding.Security.Mode = SecurityMode.Message;

Berikutnya, saya perlu menentukan jenis authentication yang perlu dilakukan. Karena saya ingin authentication berdasarkan nama user di sistem operasi Windows, saya bisa menambahkan baris berikut ini:

binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

Salah satu hal yang cukup merepotkan adalah fasilitas keamanan SecurityMode.Message yang membutuhkan sertifikat digital X.509. Sertifikat ini dibutuhkan untuk melakukan enkripsi data. Hal ini memang lebih aman karena data yang dikirim tidak dapat dilihat dengan mudah oleh pihak ketiga (misalnya melalui Wireshark), tapi juga berarti saya harus melakukan setup yang lebih rumit. Untuk membuat sebuah sertifikat sementara yang dipakai untuk keperluan percobaan, saya akan membuka Visual Studio 2010 Command Prompt dan memberikan perintah berikut ini:

MakeCert -n "CN=TestingCA" -r -sv TestingCA.pvk TestingCA.cer

Saya perlu membuat sebuah password baru pada dialog yang muncul. Setelah perintah ini selesai dikerjakan, saya akan menemukan private key di file TestingCA.pvk dan sebuah public key di TestingCA.cer. Digital certificate ini akan berperan sebagai CA Root Certificate. Bila saya men-double click file TestingCA.cer, saya akan memperoleh tampilan seperti pada gambar berikut ini:

Self-signed root certificate digital

Self-signed root certificate digital

Windows memberikan pesan peringatan karena Testing CA bukanlah penyedia root certificate yang terpercaya. Pada lingkungan produksi, saya perlu membeli sertifikat digital dari penyedia terpercaya seperti VeriSign, GoDaddy, dan sebagainya. Karena ini hanya untuk latihan, saya tetap men-klik tombol Install Certificate…. Pada wizard yang muncul, saya men-klik tombol Next hingga Finish tanpa melakukan perubahan. Setelah selesai, root certificate akan ter-install pada komputer server. Bila komputer client adalah komputer yang terpisah, saya juga perlu men-install root certificate ini pada komputer client.

Setelah itu, saya perlu membuat sertifikat digital lain yang di-sign oleh private key milik root certificate yang dibuat sebelumnya. Untuk itu, saya memberikan perintah berikut ini:

MakeCert -sk SolidSnake -iv TestingCA.pvk -n "CN=SolidSnake" -ic TestingCA.cer -ss My -sky exchange -pe

Saya akan diminta untuk memasukkan password yang isinya harus sama dengan password yang saya berikan pada root certificate.

Penggunaan -sky exchange menunjukkan bahwa sertifikat digital ini dipakai untuk enkripsi data bukan sebagai digital signature (tanda tangan digital). Sertifikat digital ini akan langsung di-install pada lokasi Personal (terlihat dari penggunaan -ss My). Penggunaan -pe akan membuat private key disertakan di dalam sertifikat digital.

Untuk memakai sertifikat digital tersebut sebagai service certificate, saya dapat menambahkan baris program berikut ini:

selfHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.CurrentUser,
   StoreName.My, X509FindType.FindBySubjectName, "SolidSnake");                

Kode program web service provider saya secara lengkap kini akan terlihat seperti pada contoh berikut ini:

using System.ServiceModel;
using System;
using System.ServiceModel.Description;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates;

namespace LatihanWCFAuthentication
{
    [ServiceContract]
    public class Hitung
    {
        [OperationContract]
        public int tambah(int angka1, int angka2)
        {
            return angka1 + angka2;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Uri baseAddress = new Uri("http://localhost:80/Latihan");
            ServiceHost selfHost = new ServiceHost(typeof(Hitung), baseAddress);
            try
            {
                WSHttpBinding binding = new WSHttpBinding();
                binding.Security.Mode = SecurityMode.Message;
                binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
                selfHost.AddServiceEndpoint(typeof(Hitung), binding, "LatihanService");
                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                selfHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.CurrentUser,
                    StoreName.My, X509FindType.FindBySubjectName, "SolidSnake");                
                selfHost.Description.Behaviors.Add(smb);                
                selfHost.Open();
                Console.WriteLine("Web services sudah aktif dan siap melayani request.");
                while (true)
                {
                    System.Threading.Thread.Sleep(50);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Telah terjadi kesalahan: {0}", ex);                
                selfHost.Abort();
            }
            Console.ReadLine();
        }
    }
}

Saya kemudian menjalankan kode program web service provider dan memastikan bahwa tidak ada pesan kesalahan yang muncul.

Yang berbeda kali ini adalah bila saya menjalankan kode program web service consumer, saya akan memperoleh pesan kesalahan. Saya tidak bisa lagi mengerjakan service begitu saja. Web service consumer kini perlu melakukan authentication berdasarkan nama user dan password yang dapat dipakai untuk login ke sistem operasi Windows di web service provider. Tapi sebelumnya, saya perlu memperbaharui client proxy dengan men-klik kanan nama web service di proyek web service consumer dan memilih menu Update Service Reference. Setelah itu, saya mengubah kode program web service consumer menjadi seperti berikut ini:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Consumer.WebServiceLatihan;
using System.ServiceModel;
using System.ServiceModel.Security;

namespace Consumer
{
    class Program
    {
        static void Main(string[] args)
        {
            HitungClient client = new HitungClient();
            WSHttpBinding binding = (WSHttpBinding) client.Endpoint.Binding;
            binding.Security.Mode = SecurityMode.Message;
            binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
            client.ClientCredentials.UserName.UserName = "nama_user_di_komputer_provider";
            client.ClientCredentials.UserName.Password = "password_untuk_user_tersebut";
            client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = 
                X509CertificateValidationMode.None;

            int angka1 = 10, angka2 = 20;
            int hasil = client.tambah(angka1, angka2);
            Console.WriteLine("Hasil = {0}", hasil);
        }
    }
}

Pada kode program di atas, saya menyertakan nama dan password untuk sebuah user yang dapat login di sistem operasi Windows di web service provider. Selain itu, karena saya memakai root certificate yang saya buat sendiri (self-signed), maka saya mematikan fasilitas validasi sertifikat dengan mengubah CertificateValidationMode menjadi X509CertifateValidationMode.NONE. Bila saya menjalankan program, maka web service kembali dapat dipanggil seperti biasa. Akan tetapi, bila memasukkan nama atau password yang salah, saya akan memperoleh pesan kesalahan seperti berikut ini:

Unhandled Exception: System.ServiceModel.Security.MessageSecurityException: An unsecured or incorrectly secured fault was received from the other party. See the
 inner FaultException for the fault code and detail. ---> System.ServiceModel.FaultException: At least one security token in the message could not be validated.

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: