This article mainly introduces the ASP. NET MVC SSO Single Sign-on design and implementation, with a certain reference value, interested can understand.
Lab Environment Configuration
The host file is configured as follows:
127.0.0.1 app.com
127.0.0.1 sso.com
IIS is configured as follows:
Application pools with the. Net Framework 4.0
Note The IIS-bound domain name, two completely different domain names.
The App.com website is configured as follows:
The Sso.com website is configured as follows:
Memcached cache:
Database configuration:
The database uses EntityFramework 6.0.0, and the first run automatically creates the corresponding database and table structure.
The authorization verification process demonstrates:
Access in the browser address bar: http://app.com, if the user has not logged in the site will be automatically redirected to: Http://sso.com/passport, while the querystring through the parameters of the corresponding Appkey app identity passed over, Run as follows:
URL Address: http://sso.com/passport?appkey=670b14728ad9902aecba32e22fa4f6bd&username=
After entering the correct login account and password, click the Login button system automatically 301 redirect to the app will drop the home page, destroy the success as follows:
Because SSO authorization is logged under different domains, the authorization identity is returned in querystring manner. Cookies can be used under the same domain website. Since the 301 redirect request is sent by the browser, the browser redirection is lost if the authorization ID is placed in handers. After the redirect succeeds, the program automatically writes the authorization ID to the cookie, and when you click on a different page address, the authorization information is no longer visible in the URL address bar. The cookie is set as follows:
Follow-up authorization verification after successful login (access other pages that require authorization access):
Verify Address: http://sso.com/api/passport?sessionkey=xxxxxx&remark=xxxxxx
return Result: True,false
Depending on the actual business situation, the client may choose to prompt the user that the authorization has been lost and need to be re-authorized. The default is automatically redirected to the SSO landing page, which is: http://sso.com/passport?appkey=670b14728ad9902aecba32e22fa4f6bd&username=seo@ljja.cn At the same time landing page email address text box will be customized to complete the user's login account, the user only need to enter the login password can be authorized after the successful session of the duration of the automatic extension of one year.
SSO Database Validation Log:
User Authorization Verification log:
User Licensing Sessions session:
Database user account and application information:
Application Authorization Login Verification page core code:
<summary>//Public key: AppKey//private key: Appsecret///session: SessionKey//</summary> publicly class Passportcontro Ller:controller {private readonly Iappinfoservice _appinfoservice = new Appinfoservice (); Private readonly Iappuserservice _appuserservice = new Appuserservice (); Private readonly Iuserauthsessionservice _authsessionservice = new Userauthsessionservice (); Private readonly Iuserauthoperateservice _userauthoperateservice = new Userauthoperateservice (); Private Const string AppInfo = "AppInfo"; Private Const string SessionKey = "SessionKey"; Private Const string sessionusername = "Sessionusername"; Default login interface public ActionResult Index (String appKey = "", String username = "") {Tempdata[appinfo] = _appinfoserv Ice. Get (AppKey); var ViewModel = new Passportloginrequest {AppKey = AppKey, UserName = UserName}; Return View (ViewModel); }//Authorized login [HttpPost] public actionresult Index (passportlogInrequest model) {//Get application information var appInfo = _appinfoservice.get (model. AppKey); if (AppInfo = = null) {//application does not exist with return View (model); } Tempdata[appinfo] = AppInfo; if (Modelstate.isvalid = = False) {//entity validation failed return View (model); }//filter field Invalid character model. Trim (); Get user information var userInfo = _appuserservice.get (model. UserName); if (UserInfo = = null) {//user does not exist return View (model); } if (userinfo.userpwd! = model. PASSWORD.TOMD5 ()) {//Password incorrect return View (model); }//Get current non-expiring session var currentsession = _authsessionservice.existsbyvalid (Appinfo.appkey, userinfo.username); if (currentsession = = null) {//build session Currentsession = new Userauthsession {A Ppkey = Appinfo.appkey, Createtime = DateTime.Now, Invalidtime = DateTime.Now.AddYears (1), IpAd Dress = request.userhostaddress, SessionKey = Guid.NewGuid (). ToString (). ToMd5 (), UserName = Userinfo.username}; Create session _authsessionservice.create (Currentsession); } else {//extended validity period, default one year _authsessionservice.extendvalid (Currentsession.sessionkey); }//Record user authorization log _userauthoperateservice.create (new Userauthoperate {createtime = DateTime.Now, IpAddress = request.userhostaddress, Remark = string. Format ("{0} login {1} authorization succeeded", Currentsession.username, appinfo.title), SessionKey = Currentsession.sessionkey}); 104 var RedirectURL = string. Format ("{0}"? Sessionkey={1}&sessionusername={2} ", Appinfo.returnurl, Currentsession.sessionkey, UserInfo.Use Rname); Jump default Callback Page return Redirect (RedirectURL); }memcached session ID Validation Core code: public class Passportcontroller:apicontroller {private ReadOnly iuserauthsessionservice _au Thsessionservice = new Userauthsessionservice (); PrivaTe readonly iuserauthoperateservice _userauthoperateservice = new Userauthoperateservice (); public bool Get (string sessionkey = "", String remark = "") {if (_authsessionservice.getcache (SessionKey)) { _userauthoperateservice.create (new Userauthoperate {createtime = DateTime.Now, IpAddress = Request.RequestUri.Host, Remark = string. Format ("Validation succeeded-{0}", remark), SessionKey = SessionKey}); return true; } _userauthoperateservice.create (new userauthoperate {createtime = DateTime.Now, IpAddress = Requ Est. Requesturi.host, Remark = string. Format ("Validation failed-{0}", remark), SessionKey = SessionKey}); return false; } }
Client Authorization Verification Filters Attribute
public class Ssoauthattribute:actionfilterattribute {Public Const string sessionkey = "SessionKey"; Public Const string sessionusername = "Sessionusername"; public override void OnActionExecuting (ActionExecutingContext filtercontext) {var cookiesessionkey = ""; var cookiesessionusername = ""; SessionKey by QueryString if (Filtercontext.httpcontext.request.querystring[sessionkey]! = null) {Cook Iesessionkey = Filtercontext.httpcontext.request.querystring[sessionkey]; FILTERCONTEXT.HTTPCONTEXT.RESPONSE.COOKIES.ADD (New HttpCookie (SessionKey, Cookiesessionkey)); }//sessionusername by QueryString if (filtercontext.httpcontext.request.querystring[sessionusername]! = NULL) {cookiesessionusername = Filtercontext.httpcontext.request.querystring[sessionusername]; FILTERCONTEXT.HTTPCONTEXT.RESPONSE.COOKIES.ADD (New HttpCookie (Sessionusername, cookiesessionusername)); }//read from Cookie SessionKey if (Filtercontext.httpcontext.request.cookies[sessionkey]! = null) {Cookiesessionkey = Filtercontext.httpcon Text. Request.cookies[sessionkey]. Value; }//read from Cookie Sessionusername if (filtercontext.httpcontext.request.cookies[sessionusername]! = null) { Cookiesessionusername = Filtercontext.httpcontext.request.cookies[sessionusername]. Value; } if (string. IsNullOrEmpty (Cookiesessionkey) | | String. IsNullOrEmpty (Cookiesessionusername)) {//Direct login Filtercontext.result = Ssologinresult (cookiesessionusern AME); } else {//verify if (Checklogin (Cookiesessionkey, filterContext.HttpContext.Request.RawUrl) = = False) {//session lost, jump to login page Filtercontext.result = Ssologinresult (cookiesessionusername); }} base. OnActionExecuting (Filtercontext); } public static bool Checklogin (string sessionkey, string remark = "") {var httpClient = new HttpClient {baseaddress = new Uri (configurationmanager.appsettings["Ssopassport"])}; var RequestUri = string. Format ("Api/passport?sessionkey={0}&remark={1}", SessionKey, remark); try {var resp = Httpclient.getasync (RequestUri). Result; Resp. Ensuresuccessstatuscode (); Return resp. Content.readasasync<bool> (). Result; } catch (Exception ex) {throw ex; }} private static ActionResult Ssologinresult (string username) {return new Redirectresult (string. Format ("{0}/passport?appkey={1}&username={2}", configurationmanager.appsettings["Ssopassport"], Conf igurationmanager.appsettings["Ssoappkey"], username)); } }
Sample SSO Validation Attribute usage:
[Ssoauth] public class Homecontroller:controller {public actionresult Index () { return View (); } Public ActionResult About () { viewbag.message = "Your Application description page."; return View (); } Public ActionResult Contact () { viewbag.message = ' Your contact page. '; return View (); } }
Summarize:
From the draft sample code, you can see that there are many optimizations in code performance, as well as a series of informational messages such as a user account that does not exist for the SSO Application authorization landing page, a password error, and so on. After the business code is running basically correctly, you can consider optimizing for more security aspects, such as enabling Appsecret private key signature verification, IP range authentication, fixed session request attack, authentication code for the SSO authorization login interface, session cache auto-rebuild, SSO server, level expansion of the cache, etc.