Configure the HOST file in the experiment environment as follows:
127.0.0.1 app.com
127.0.0.1 sso.com
The IIS configuration is as follows:
The application pool uses. Net Framework 4.0
Note that the domain name bound to IIS is two domain names that are completely different from each other.
The website configuration for app.com is as follows:
The sso.com website configuration is as follows: memcached cache:
Database Configuration:
The database uses EntityFramework 6.0.0. When it is run for the first time, the corresponding database and table structure will be automatically created.
Authorization verification process Demonstration:
Access the: http://app.com in the browser's address bar and the website will be automatically redirected to: login if the user has not logged in:
URL: http://sso.com/passport? Appkey = 670b14728ad9902aecba32e22fa4f6bd & username =
After you enter the correct Logon account and password, click the login button and the system will automatically redirect 301 to the application's homepage. After the successful destruction, the system will see the following:
Because SSO authorization login is performed in different domains, the QueryString method is used to return the authorization ID. The Cookie method can be used for the same network site. Because the 301 redirection request is sent by the browser, if the authorization ID is put into Handers, the browser will lose the redirection. After successful redirection, the program automatically writes the authorization identifier to the Cookie. When you click another page address, the authorization identifier information will no longer be displayed in the URL address bar. Cookie settings are as follows:
Subsequent authorization verification after successful login (access to other pages that require authorized access ):
Check address: http://sso.com/api/passport? Sessionkey = xxxxxx & remark = xxxxxx
Returned results: true, false
The client prompts that the user's authorization has been lost and needs to be re-authorized based on the actual business situation. The default automatic redirect to SSO login page, that is: http://sso.com/passport? Appkey = 670b14728ad9902aecba32e22fa4f6bd & username = seo@ljja.cn at the same time login page mailbox address text box will be customized to complete the user's login account, the user just enter the login password, after successful authorization session validity period automatically extended for one year.
SSO database verification log:
User authorization verification log:
User-authorized Session:
Database User Account and application information:
Core code of the application authorization login verification page:
1 // <summary> 2 // Public Key: AppKey 3 // Private Key: AppSecret 4 // session: sessionKey 5 // </summary> 6 public class PassportController: Controller 7 {8 private readonly IAppInfoService _ appInfoService = new AppInfoService (); 9 private readonly IAppUserService _ appUserService = new AppUserService (); 10 private readonly IUserAuthSessionService _ authSessionService = new UserAuthSessionService (); 11 private reado Nly IUserAuthOperateService _ userAuthOperateService = new UserAuthOperateService (); 12 13 private const string AppInfo = "AppInfo"; 14 private const string SessionKey = "SessionKey "; 15 private const string SessionUserName = "SessionUserName"; 16 17 // default logon page 18 public ActionResult Index (string appKey = "", string username = "") 19 {20 TempData [AppInfo] = _ appInfoService. get (appKey); 21 22 var vie WModel = new PassportLoginRequest 23 {24 AppKey = appKey, 25 UserName = username 26}; 27 28 return View (viewModel ); 29} 30 31 // authorized logon 32 [HttpPost] 33 public ActionResult Index (PassportLoginRequest model) 34 {35 // obtain application information 36 var appInfo = _ appInfoService. get (model. appKey); 37 if (appInfo = null) 38 {39 // The application does not exist 40 return View (model); 41} 42 TempData [AppInfo] = appInfo; 44 45 if (ModelState. IsValid = false) 46 {47 // entity Verification Failed 48 return View (model); 49} 50 51 // invalid Filter field character 52 model. trim (); 53 54 // obtain user information 55 var userInfo = _ appUserService. get (model. userName); 56 if (userInfo = null) 57 {58 // the user does not exist 59 return View (model); 60} 61 62 if (userInfo. userPwd! = Model. password. toMd5 () 63 {64 // incorrect password 65 return View (model); 66} 67 68 // get the currently expired Session 69 var currentSession = _ authSessionService. existsByValid (appInfo. appKey, userInfo. userName); 70 if (currentSession = null) 71 {72 // construct Session 73 currentSession = new UserAuthSession 74 {75 AppKey = appInfo. appKey, 76 CreateTime = DateTime. now, 77 InvalidTime = DateTime. now. addYears (1), 78 IpAddress = R Equest. userHostAddress, 79 SessionKey = Guid. newGuid (). toString (). toMd5 (), 80 UserName = userInfo. userName 81}; 82 83 // create Session 84 _ authSessionService. create (currentSession); 85} 86 else 87 {88 // extend the validity period. The default value is 89 _ authSessionService per year. extendValid (currentSession. sessionKey); 90} 91 92 // record user authorization log 93 _ userAuthOperateService. create (new UserAuthOperate 94 {95 CreateTime = DateTime. now, 96 IpAddress = Request. userHostAddress, 97 Remark = string. format ("{0} login {1} authorization successful", currentSession. userName, appInfo. title), 98 SessionKey = currentSession. sessionKey 99}); 104 105 var redirectUrl = string. format ("{0 }? SessionKey = {1} & SessionUserName = {2} ", 106 appInfo. returnUrl, 107 currentSession. sessionKey, 108 userInfo. userName); 109 110 // jump to the default callback page 111 return Redirect (redirectUrl); 112} 113}
Core code for Memcached session identity verification:
Public class PassportController: ApiController {private readonly IUserAuthSessionService _ authSessionService = new UserAuthSessionService (); private readonly register _ Role = 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 ("verified-{0}", remark), SessionKey = sessionKey}); return true;} _ userAuthOperateService. create (new UserAuthOperate {CreateTime = DateTime. now, IpAddress = Request. requestUri. host, Remark = string. format ("Verification 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 Limit = ""; var cookieSessionUserName = ""; // SessionKey by QueryString if (filterContext. httpContext. request. queryString [SessionKey]! = Null) {cookieSessionkey = 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 SessionKey if (filterContext. httpContext. request. cookies [SessionKey]! = Null) {cookieSessionkey = filterContext. HttpContext. Request. Cookies [SessionKey]. Value;} // read SessionUserName if (filterContext. HttpContext. Request. Cookies [SessionUserName] From the Cookie. = Null) {cookieSessionUserName = filterContext. httpContext. request. cookies [SessionUserName]. value;} if (string. isNullOrEmpty (cookieSessionkey) | string. isNullOrEmpty (cookieSessionUserName) {// directly log on to filterContext. result = SsoLoginResult (cookieSessionUserName);} else {// verify if (CheckLogin (cookieSessionkey, filterContext. httpContext. request. rawUrl) = false) {// session loss, jump to the 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. configurettings [" SSOPassport "], ConfigurationManager. configurettings [" SSOAppKey "], username ));}}
Sample SSO verification feature 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(); } }
Summary:
From the sample code of the draft, we can see that there are many optimizations in the code performance, as well as a series of prompts such as the absence of user accounts on the Authorization login page of the SSO application, and incorrect passwords. After the Business Code runs properly, you can consider optimizing it to more security levels, such as enabling AppSecret Private Key signature verification and IP Range verification, fixed Session Request attacks, verification code on the SSO authorized login interface, automatic rebuilding of session cache, SSo server, horizontal scaling of cache, etc.
Source Code address: https://github.com/smartbooks/SmartSSO