A few days ago a friend asked me to help do a single point of entry, in fact, this concept has long been familiar with, but the actual application of very few, rare recently patronagejob, so decided to detail an SSO solution through this article, I hope to help. There are many solutions for SSO, but the results are disappointing, most of them are reproduced and are described in a cursory way.
Gossip less, to get to the point, my idea is to use centralized authentication, multiple sites to centralize passport validation. As shown in the following illustration:
To facilitate a clear description, first define a few nouns, which appear in this article are the following meaning.
Main station: Passport centralized authentication server http://www.passport.com/.
Sub-stations: http://www.a.com/, http://www.b.com/, http://www.c.com/
Voucher: User login to generate data identification, for the identification of authorized users, can be a variety of ways, demo in the main station I use the cache, the station use session.
Token: A unique identification issued by a passport that can circulate in each branch.
OK, now describe the single sign-on process:
Scenario One, anonymous User: Anonymous user access to an authorization page on the sub-station a first jump to the main station to allow users to enter the account number, password for login, verify the main station voucher after the pass, and create a token, jump back to the substation A, at this point a detection of the user has a token, then use the token to the main station again to obtain user credentials, Allows the user to access the authorization page after successful acquisition. It also produces local credentials for station A, and checks local credentials to reduce network interaction when the user needs to authenticate again.
Situation two, at the substation a login user access sub-station B: Because the user in the substation a login, has held a token, so the station B will use the token to the main station to obtain user credentials, the success of the user to allow access to the authorization page. The local voucher for the station B is also generated.
After the design is complete, the following are some key points for the implementation of the solution:
Token: The token is issued by the master station, the master station issuing token generates the user credentials at the same time, and records the corresponding relationship between the token and the user's voucher to respond to the corresponding voucher according to the user's token; The token is to be circulated across the cross-domain stations, so the token in the demo I use the Master station cookie, and specify cookie.domain= "passport.com". How do the stations share cookies from the master station? From the redirect to the main station page, and then the page read the cookie and return it as a URL parameter, you can see the detailed implementation in the demo code, of course, if there is a better way to achieve the token to share.
Copy Code code as follows:
Generate 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 voucher: Master voucher is a relational table that contains three fields: token, voucher data, expiration time. There are a variety of implementation options, the need for reliable language database, requirements for performance in Cache,demo I use the cache in the DataTable. As shown in the following code:
Copy Code code as follows:
<summary>
Initializing data structures
</summary>
<remarks>
/// ----------------------------------------------------
/// | Token (token) | Info (user voucher) | Timeout (expired time) |
/// |--------------------------------------------------|
</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.appsettings["timeout"));
Datacolumn[] keys = new datacolumn[1];
Keys[0] = dt. columns["token"];
Dt. PrimaryKey = keys;
Cache expiration time is token expiration *2
HttpContext.Current.Cache.Insert ("CERT", DT, NULL, DateTime.MaxValue, Timespan.fromminutes (double). Parse (system.configuration.configurationmanager.appsettings["Timeout"]));
}
}
Sub-station voucher: The station voucher is mainly used to reduce the interaction of the network during the repetitive verification, for example, the user has logged on to the station A, when he accesses the station a again, he does not have to use the token to verify the main station, because the user's credentials are already in the station a The station voucher is relatively simple, use session, cookie all can.
Sub-station SSO page base class: Sub-stations using the SSO page will do a series of logical judgments, such as the flowchart at the beginning of the article. If there is more than one page, it is not possible to write one such logic for each page, OK, then encapsulate this logic into a base class, which will inherit the base class from the page that uses SSO. As shown in the following code:
Copy Code code 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)
{
Station voucher exists
Response.Write ("Congratulations, the station voucher exists, you are authorized to access the page!") ");
}
Else
{
Token verification Results
if (request.querystring["Token"]!= null)
{
if (request.querystring["Token"]!= "$Token $")
{
Hold a token
String tokenvalue = request.querystring["Token"];
Call WebService to obtain master station credentials
Sso. SiteA.RefPassport.TokenService tokenservice = new SSO. SiteA.RefPassport.TokenService ();
Object o = tokenservice.tokengetcredence (tokenvalue);
if (o!= null)
{
Token is correct
session["Token"] = O;
Response.Write ("Congratulations, token exists, you are authorized to access the page!") ");
}
Else
{
Token error
Response.Redirect (This.replacetoken ());
}
}
Else
{
Not holding token
Response.Redirect (This.replacetoken ());
}
}
No token verification, go to master station validation
Else
{
Response.Redirect (This.gettokenurl ());
}
}
Base. OnLoad (e);
}
<summary>
To obtain a URL with a token request
Attach token request parameter in 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>
Get rid of tokens in URLs
To remove token parameters 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 exits: Clears the master station voucher and the current station voucher separately when the user exits. If you require a site to exit, B, C site also exits, you can expand the interface to clear each of the station voucher.
Main station expired voucher/token purge: Scheduled purge (DataTable) cache[Records in the timeout field that exceed the current time in the "CERT".