How to Design reusable SSO components (Implementation)

Source: Internet
Author: User

I spent a whole night writing this.Code, A little messy. Sorry.

The first is the abstraction of encryption and decryption, because. net implements 3DES very well and does not support string, so we must encapsulate it as a 3DES Encryption Class that can use string as key, IV, input and output. This is very important, encryption and decryption of cookies and encryption and decryption of URLs depend on it.

 

Using system;
Using system. Collections. Generic;
Using system. text;
Using system. Security;
Using system. Security. cryptography;

Namespace Alexander. ssolib. Cryptography
{
Public class decrypter
{
Private icryptotransform decrypter;

Public decrypter (string key, string IV)
{
System. Security. cryptography. tripledes des = system. Security. cryptography. tripledescryptoserviceprovider. Create ();
Des. Key = Common. str2byte (key );
Des. IV = Common. str2byte (IV );
Des. mode = ciphermode. ECB;
Des. Padding = paddingmode. pkcs7;
Decrypter = des. createdecryptor ();
}

Public String decryptstring (string inputstring)
{
Byte [] input = Common. str2byte (inputstring );
Byte [] Output = decrypter. transformfinalblock (input, 0, input. Length );
Return common. normalbyte2str (output );
}
}
}

Here let's take a look at the core here

System. Security. cryptography. tripledes des = system. Security. cryptography. tripledescryptoserviceprovider. Create ();

3DES processing is obtained.Program.

The key and IV must be set here before they can be used. What is extremely depressing is that keys and IV both need byte arrays, so we have to convert the input string once, we can see that the static method of the common class is called. Let's take a look at this common class.

Using system;
Using system. Collections. Generic;
Using system. text;

Namespace Alexander. ssolib. Cryptography
{
Public class common
{
Public static byte [] str2byte (string Str)
{
Return convert. frombase64string (STR );
}
Public static string byte2str (byte [] BYT)
{
Return convert. tobase64string (BYT );
}
Public static byte [] normalstr2byte (string Str)
{
Return encoding. Default. getbytes (STR );
}
Public static string normalbyte2str (byte [] BYT)
{
Return encoding. Default. getstring (BYT );
}
}
}

There are four methods:

Convert. frombase64string (STR );

Convert. tobase64string (BYT );

Encoding. Default. getbytes (STR );

Encoding. Default. getstring (BYT );

Here are the base64 encoded strings and the conversion method between the default Character Set string and byte []. Here we use the str2byte (string Str) method.

What if I use base64 for why? Because we can use base64 to encode the byte array of key and IV into a string like 5di2ljtk/z/giea0xsn66lasdvnhbmuv. If we use other methods, it becomes garbled.

After key and IV are set, the filling mode and other attributes are displayed, which is unnecessary.

Then

Des. createdecryptor (); Create a converter instance defined by the icryptotransform interface.

You can use decrypter. transformfinalblock to convert the data. Note that base64 is required for the input string, and the output string must be created for the current system character set.

The structure of the Encryption Class is similar to that of the decryption class. However, the input uses the current character set to be converted to the byte array, while the output uses the base64 method, which is the opposite of decryption.

Then there is the data structure and operation class in the common

Keymanager manages key and IV

Using system;
Using system. Collections. Generic;
Using system. text;
Using system. IO;

Namespace Alexander. ssolib. Common
{
Public class keymanager
{
Private Static string storebase;

Static keymanager ()
{
Storebase = system. configuration. configurationsettings. receivettings ["storebase"];
}

Public static void getkeybysiteid (string ID, out string key, out string IV)
{< br> string Path = (storebase. endswith ("\\")? Storebase: storebase + "\") + ID + ". key "
If (file. exists (PATH)
{< br> using (streamreader reader = new streamreader (PATH)
{< br> key = reader. readline ();
IV = reader. readline ();
reader. close ();
}< BR >}< br> else
{< br> key = ""
IV = ""
}< BR >}< br>> Public static void updatekey (string siteid)
{< br> string Path = (storebase. endswith ("\\")? Storebase: storebase + "\") + siteid + ". key "
using (streamwriter writer = new streamwriter (path, false, encoding. default)
{< br> Alexander. ssolib. cryptography. keymaker Km = new Alexander. ssolib. cryptography. keymaker ();
writer. writeline (km. key);
writer. writeline (km. IV);
writer. flush ();
writer. close ();
}< BR >}< br> Public static void deletekey (string siteid)
{< br> string Path = (Storebase. endswith ("\\")? Storebase: storebase + "\") + siteid + ". key "
file. delete (PATH);
}< BR >}

Psorequest indicates the request of the PSO.

Using system;
Using system. Collections. Generic;
Using system. text;
Using Alexander. ssolib. cryptography;

Namespace Alexander. ssolib. Common
{
Public class psorequest
{
Private string key = system. configuration. configurationsettings. deleettings ["key"];

Private string IV = system. configuration. configurationsettings. deleettings ["IV"];

Private string siteid;

Public String siteid
{
Get {return siteid ;}
Set {siteid = value ;}
}
Private string createdate;

Public String createdate
{
Get {return createdate ;}
Set {createdate = value ;}
}
Private string returnurl;

Public String returnurl
{
Get {return returnurl ;}
Set {returnurl = value ;}
}

Public psorequest ()
{
Siteid = system. configuration. configurationsettings. receivettings ["siteid"];
Createdate = datetime. Now. tostring ();
Returnurl = system. Web. httpcontext. Current. Request. url. tostring ();
}
Public psorequest (string hashstring)
{
String [] RLS = hashstring. Split ('$ ');
String skey;
String SIV;
Keymanager. getkeybysiteid (RLS [0], out skey, out SIV );
Decrypter de = new decrypter (skey, Siv );
String rs = de. decryptstring (RLS [1]);
String [] sp = Rs. Split ('| ');
Siteid = RLS [0];
Createdate = Sp [0];
Returnurl = Sp [1];
}
Public String createhash ()
{
Encrypter en = new encrypter (Key, IV );
Return siteid + "$" + en. encryptstring (createdate + "|" + returnurl );
}

}
}

Here we can see that the request data includes the site ID, The 3DES encrypted session time, and the returned address.

Public psorequest () is the constructor instantiated by the PSO. It creates an empty request and uses

Createhash () to create an encrypted string.

Public psorequest (string hashstring) is used on the SSO end to parse the format of the PSO request, and then write the value into the variable of the object.

The ssoresponse class contains the class of URL format feedback sent from the SSO end to the PSO.

Using system;
Using system. Collections. Generic;
Using system. text;
Using Alexander. ssolib. cryptography;

Namespace Alexander. ssolib. Common
{
Public class ssoresponse
{

Private string key = system. configuration. configurationsettings. deleettings ["key"];

Private string IV = system. configuration. configurationsettings. deleettings ["IV"];

Private psorequest request;

Private string response;
Public ssoresponse (psorequest request)
{
Request = request;
}
Public ssoresponse (string ssoresponse)
{
Response = ssoresponse;
}
Public String createresponsestring (Ticket)
{
Ticket. createdate = request. createdate;
String data = request. siteid + "|" + ticket. userid + "|" + ticket. username + "|" + ticket. ticketdata + "|" + ticket. createdate;
String skey;
String SIV;
Keymanager. getkeybysiteid (request. siteid, out skey, out SIV );
Encrypter ENC = new encrypter (skey, Siv );
Return enc. encryptstring (data );
}
Public ticket createpsoticket ()
{
Decrypter Dc = new decrypter (Key, IV );
String data = Dc. decryptstring (response. Trim (). Replace ("", "+ "));
String [] ls = data. Split ('| ');
Ticket Tc = new ticket (LS [0], ls [1], ls [2], ls [3]);
Return TC;
}
}
}

Likewise, there are two constructors, one for the PSO and the other for the SSO. The principle is similar to that of psorequest.

The ticket class contains the cookie structure and encryption, decryption, loading, and storage operations.

Using system;
Using system. Collections. Generic;
Using system. text;
Using system. Web;
Using Alexander. ssolib. cryptography;

Namespace Alexander. ssolib. Common
{
Public class ticket
{
Private encrypter ENC;
Private decrypter Dec;
Private string key = system. configuration. configurationsettings. deleettings ["key"];
Private string IV = system. configuration. configurationsettings. deleettings ["IV"];

Private string userid = ""

Public String userid
{
Get {return dec. decryptstring (userid );}
Set {userid = enc. encryptstring (value );}
}
Private string username = ""

Public String Username
{
Get {return dec. decryptstring (username );}
Set {username = enc. encryptstring (value );}
}
Private string ticketdata = ""

Public String ticketdata
{
Get {return dec. decryptstring (ticketdata );}
Set {ticketdata = enc. encryptstring (value );}
}

Private string createdate = ""

Public String createdate
{
Get {return dec. decryptstring (createdate );}
Set {createdate = enc. encryptstring (value );}
}

Public ticket (string userid, string username, string ticketdata, string createdate)
{
ENC = new encrypter (Key, IV );
Dec = new decrypter (Key, IV );
Userid = enc. encryptstring (userid );
Username = enc. encryptstring (username );
Ticketdata = enc. encryptstring (ticketdata );
Createdate = enc. encryptstring (createdate );
}
Public ticket ()
{
ENC = new encrypter (Key, IV );
Dec = new decrypter (Key, IV );
}
Public bool loadticket (string domain)
{
If (system. Web. httpcontext. Current. Request. Cookies [domain]! = NULL)
{
Userid = system. Web. httpcontext. Current. Request. Cookies [domain] ["userid"];
Username = system. Web. httpcontext. Current. Request. Cookies [domain] ["username"];
Ticketdata = system. Web. httpcontext. Current. Request. Cookies [domain] ["ticketdate"];
Return true;
}
Else
{
Return false;
}
}
Public void saveticket (string domain)
{
System. Web. httpcontext. Current. response. Cookies [domain] ["userid"] = userid;
System. Web. httpcontext. Current. response. Cookies [domain] ["username"] = username;
System. Web. httpcontext. Current. response. Cookies [domain] ["ticketdate"] = ticketdata;
}
}
}

After the abstraction of the above layers, let's take a look at psoclient

Using system;
Using system. Collections. Generic;
Using system. text;
Using Alexander. ssolib. Common;

Namespace Alexander. ssolib. PSO
{
Public class psoclient: system. Web. ihttpmodule
{
# Region ihttpmodule Member

System. Web. httpapplication context;

Public void dispose ()
{
// Throw new exception ("the method or operation is not implemented .");
}

Public void Init (system. Web. httpapplication context)
{
Context = context;
Context. beginrequest + = new eventhandler (context_beginrequest );
}

Void context_beginrequest (Object sender, eventargs E)
{
String required tlist = "$" + system. configuration. configurationsettings. Required ettings ["required tlist"];

If (fig. indexof (context. Request. url. tostring () <1)
{
String key = system. configuration. configurationsettings. deleetask[ "ssokey"];
String ssoresponse = context. Request. querystring [Key];
Ssoresponse sr = new ssoresponse (ssoresponse );
If (ssoresponse! = NULL)
{
Ticket Tc = Sr. createpsoticket ();
TC. saveticket (system. configuration. configurationsettings. receivettings ["Domain"]);
}
Else
{
Ticket Tc = new ticket ();
If (! TC. loadticket (system. configuration. configurationsettings. deleettings ["Domain"])
{
Psorequest request = new psorequest ();
String requeststr = request. createhash ();
String keeperurl = system. configuration. configurationsettings. keepettings ["keeperurl"];
Context. response. Redirect (keeperurl + "? "+ Key +" = "+ context. server. urlencode (requeststr ));
}
}
}
}

# Endregion
}
}

A typical httpmodule with clear and simple logic

SSO is similar.

finally look at the complete code: http://files.cnblogs.com/Alexander-Lee/SSO.rar

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.