A friend asked me to help with single-point logon some days ago. In fact, this concept has long been familiar, but it has very few practical applications. It is rare to be idle recently. So I decided to use this article to describe an SSO solution in detail, hope to help you. There are a lot of SSO solutions, but the search results are disappointing. Most of them are reprinted and described.
When I enter the topic, I want to use the centralized authentication method, and multiple sites will focus on Passport verification. As shown in:
To facilitate a clear description, we first define several terms, which are described as follows.
Main Site: Passport centralized validation server http://www.passport.com /.
Substation: http://www.a.com/?http://www. B .com/?http://www.c.com/
Credential: The data ID generated after a user logs on to the console. It is used to identify authorized users. It can be used in many ways. In the DEMO, the main site uses the Cache and the sub-site uses the Session.
Token: a unique identifier issued by Passport that can be circulated in each substation.
OK. Now describe the Single Sign-On process:
Case 1: Anonymous Users: anonymous users access an authorization page on Substation a. First, they jump to the master station to allow users to enter their accounts and passwords for Logon. After the verification is passed, the master station creden, are generated, generate a token at the same time and jump back to substation a. At this time, substation a detects that the user has a token. Then, use the token to go to the main station again to obtain the User Token. After obtaining the token, the user is allowed to access the authorization page. At the same time, the local credential of substation a is generated. When the user needs to verify again, the local credential is checked first to reduce network interaction.
Scenario 2: users logging on to substation a access substation B: Because the user has logged on to substation a and has held a token, substation B will use the token to obtain the user credential from the main station, after successful retrieval, the user is allowed to access the authorization page. Generate local creden。 for substation B at the same time.
After the design is complete, the following are some key points of solution implementation:
Token: the token is issued by the primary site. The primary site issues a token to generate a user credential at the same time, and records the correspondence between the token and the user credential to respond to the corresponding Credential Based on the token provided by the user; the token must be circulated in different cross-origin substations. Therefore, in the DEMO, I use the Cookie of the main site and specify the Cookie. domain = "passport.com ". How do substations share the cookies of the main station? From the substation Redirect to the main station page, then the page reads the Cookie and returns it as a URL parameter. You can view the detailed implementation in the DEMO code, of course, if anyone has a better token implementation method, share it.
Copy codeThe Code is as follows:
// Generate a token
String tokenValue = Guid. NewGuid (). ToString (). ToUpper ();
HttpCookie tokenCookie = new HttpCookie ("Token ");
TokenCookie. Values. Add ("Value", tokenValue );
TokenCookie. Domain = "passport.com ";
Response. AppendCookie (tokenCookie );
Master site creden: the master site creden are a relational table that contains three fields: Token, credential data, and expiration time. There are multiple implementation methods to choose from. If you require reliability, you can use the database. If you require performance, you can use the Cache. In the DEMO, I use the DataTable in the Cache. The following code is used:
Copy codeThe Code is as follows:
/// <Summary>
/// Initialize the Data Structure
/// </Summary>
/// <Remarks>
///----------------------------------------------------
/// | Token | info | timeout |
/// | ---------------------------------------------------- |
/// </Remarks>
Private static void cacheInit ()
{
If (HttpContext. Current. Cache ["CERT"] = null)
{
DataTable dt = new DataTable ();
Dt. Columns. Add ("token", Type. GetType ("System. String "));
Dt. Columns ["token"]. Unique = true;
Dt. Columns. Add ("info", Type. GetType ("System. Object "));
Dt. Columns ["info"]. DefaultValue = null;
Dt. Columns. Add ("timeout", Type. GetType ("System. DateTime "));
Dt. Columns ["timeout"]. DefaultValue = DateTime. Now. AddMinutes (double. Parse (System. Configuration. ConfigurationManager. etettings ["timeout"]);
DataColumn [] keys = new DataColumn [1];
Keys [0] = dt. Columns ["token"];
Dt. PrimaryKey = keys;
// The Cache expiration time is the token expiration time * 2
HttpContext. current. cache. insert ("CERT", dt, null, DateTime. maxValue, TimeSpan. fromMinutes (double. parse (System. configuration. configurationManager. appSettings ["timeout"]) * 2 ));
}
}
Substation credential: the substation credential is used to reduce network interaction during repeated verification. For example, if a user has logged on to substation a and accessed substation a again, you do not need to use the token to go to the master site for verification, because substation a already has the user's credential. The substation creden。 are relatively simple and can be used with Session and Cookie.
Sub-station SSO Page Base Class: the sub-station uses SSO pages to perform a series of logic judgment processes, such as the flowchart at the beginning of the article. If there are multiple pages, it is impossible to write such logic for each page. OK, then this logic is encapsulated into a base class. Any page that uses SSO can inherit this base class. The following code is used:
Copy codeThe Code is as follows:
Using System;
Using System. Data;
Using System. Configuration;
Using System. Web;
Using System. Web. Security;
Using System. Web. UI;
Using System. Web. UI. WebControls;
Using System. Web. UI. WebControls. WebParts;
Using System. Web. UI. HtmlControls;
Using System. Text. RegularExpressions;
Namespace SSO. SiteA. Class
{
/// <Summary>
/// Authorization Page Base Class
/// </Summary>
Public class AuthBase: System. Web. UI. Page
{
Protected override void OnLoad (EventArgs e)
{
If (Session ["Token"]! = Null)
{
// The substation credential exists.
Response. Write ("congratulations, the substation credential exists. You are authorized to access this page! ");
}
Else
{
// Token Verification Result
If (Request. QueryString ["Token"]! = Null)
{
If (Request. QueryString ["Token"]! = "$ Token $ ")
{
// Hold the token
String tokenValue = Request. QueryString ["Token"];
// Call WebService to obtain the master site credential
SSO. SiteA. RefPassport. TokenService tokenService = new SSO. SiteA. RefPassport. TokenService ();
Object o = tokenService. TokenGetCredence (tokenValue );
If (o! = Null)
{
// The token is correct.
Session ["Token"] = o;
Response. Write ("congratulations, the token exists. You are authorized to access this page! ");
}
Else
{
// Token Error
Response. Redirect (this. replaceToken ());
}
}
Else
{
// Token not held
Response. Redirect (this. replaceToken ());
}
}
// Token verification is not performed, go to the master site for verification
Else
{
Response. Redirect (this. getTokenURL ());
}
}
Base. OnLoad (e );
}
/// <Summary>
/// Obtain the URL with the token request
/// Append the token request parameter to the current URL
/// </Summary>
/// <Returns> </returns>
Private string getTokenURL ()
{
String url = Request. Url. AbsoluteUri;
Regex reg = new Regex (@ "^ .*\?. + =. + $ ");
If (reg. IsMatch (url ))
Url + = "& Token = $ Token $ ";
Else
Url + = "? Token = $ Token $ ";
Return "http://www.passport.com/gettoken.aspx? BackURL = "+ Server. UrlEncode (url );
}
/// <Summary>
/// Remove the token from the URL
/// Remove the token parameter from the current URL
/// </Summary>
/// <Returns> </returns>
Private string replaceToken ()
{
String url = Request. Url. AbsoluteUri;
Url = Regex. Replace (url ,@"(\? | &) Token =. * "," ", RegexOptions. IgnoreCase );
Return "http://www.passport.com/userlogin.aspx? BackURL = "+ Server. UrlEncode (url );
}
} // End class
}
User Exit: when the user exits, the master site creden。 and the current substation creden。 are cleared. If you want Site A to exit and site B and Site C to exit, you can expand the interface to clear the creden。 of each substation.
Master site expiration credential/token clearing: records whose timeout field exceeds the current time in the DataTable Cache ["CERT.