Maison > Article > développement back-end > Solution de mise en œuvre d'authentification unique (SSO) basée sur .Net
Il y a quelques jours, un ami m'a demandé de l'aider avec l'authentification unique. En fait, ce concept est familier depuis longtemps, mais il y a peu d'applications pratiques que j'ai récemment, j'ai donc décidé de décrire un. Solution SSO en détail à travers cet article, j'espère qu'elle sera utile à tout le monde. Il existe de nombreuses solutions SSO, mais les résultats de recherche sont décevants. La plupart d'entre elles sont republiées les unes des autres et les descriptions sont superficielles.
Sans plus tarder, entrons dans le vif du sujet. Mon idée est d'utiliser la vérification centralisée et la vérification centralisée des passeports sur plusieurs sites. Comme le montre la figure ci-dessous :
Afin de faciliter une description claire, nous définissons d'abord quelques noms. Les significations qui apparaissent dans cet article sont les suivantes.
Site principal : Serveur de vérification centralisé des passeports http://www.passport.com/.
Sites des succursales : http://www.a.com/, http://www.b.com/, http://www.c.com/
Credential : identification des données générées après la connexion de l'utilisateur, utilisé pour identifier les utilisateurs autorisés, peut être utilisé de différentes manières Dans la DÉMO, j'utilise Cache pour le site principal et Session pour les sous-sites.
Jeton : Un identifiant unique délivré par Passeport pouvant circuler dans chaque agence.
OK, décrivez maintenant le processus d'authentification unique :
Scénario 1. Utilisateur anonyme : un utilisateur anonyme accède à une page d'autorisation sur le sous-site a. Tout d'abord, il accède au site principal et permet à l'utilisateur d'entrer. leur numéro de compte et leur mot de passe pour se connecter. Après avoir réussi la vérification, les informations d'identification du site principal sont générées, et le jeton est généré en même temps, et revient au sous-site a. À ce moment, le sous-site a détecte cela. l'utilisateur détient déjà le jeton, il utilise donc le jeton pour accéder à nouveau au site principal afin d'obtenir les informations d'identification de l'utilisateur. Une fois l'acquisition réussie, l'utilisateur est autorisé à accéder à la page d'autorisation. Dans le même temps, les informations d'identification locales de la branche a sont générées. Lorsque l'utilisateur doit être à nouveau authentifié, les informations d'identification locales seront d'abord vérifiées pour réduire l'interaction réseau.
Scénario 2 : Un utilisateur connecté au sous-site a visite le sous-site b : Parce que l'utilisateur s'est connecté au sous-site a et détient déjà un jeton, le sous-site b utilisera le jeton pour accéder au site principal pour obtenir les informations d'identification de l'utilisateur, et l'acquisition est réussie. L'utilisateur est alors autorisé à accéder à la page d'autorisation. Dans le même temps, les informations d'identification locales de la sous-station b sont générées.
Une fois la conception terminée, voici quelques points clés de la mise en œuvre de la solution :
Jeton : Le jeton est émis par la gare principale, et la gare principale émet le jeton et génère les informations d'identification des utilisateurs en même temps, et enregistre la correspondance entre les jetons et les informations d'identification de l'utilisateur pour répondre aux informations d'identification correspondantes en fonction du jeton fourni par l'utilisateur. Les jetons doivent circuler dans diverses sous-stations inter-domaines, de sorte que dans la DÉMO, j'utilise le jeton Cookie de la station principale et je précise Cookie.Domain="passport.com". Comment chaque site filiale partage-t-il les cookies du site principal ? Redirigez du sous-site vers la page principale du site, puis la page lit le cookie et le renvoie sous la forme de paramètres d'URL. Vous pouvez vérifier l'implémentation détaillée dans le code DEMO. Bien sûr, si quelqu'un a un meilleur jeton. mise en œuvre, veuillez la partager.
//产生令牌 string tokenValue = Guid.NewGuid().ToString().ToUpper(); HttpCookie tokenCookie = new HttpCookie("Token"); tokenCookie.Values.Add("Value", tokenValue); tokenCookie.Domain = "passport.com"; Response.AppendCookie(tokenCookie);
Identifiant du site principal : l'identifiant du site principal est une table relationnelle qui contient trois champs : le jeton, les données d'identification et l'heure d'expiration. Il existe de nombreuses méthodes d'implémentation parmi lesquelles choisir. Si la fiabilité est requise, utilisez la base de données. Si les performances sont requises, utilisez Cache. Dans la DÉMO, j'ai utilisé le DataTable dans le cache. Comme indiqué dans le code suivant :
/// <summary> /// 初始化数据结构 /// </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.AppSettings["timeout"])); DataColumn[] keys = new DataColumn[1]; keys[0] = dt.Columns["token"]; dt.PrimaryKey = keys; //Cache的过期时间为 令牌过期时间*2 HttpContext.Current.Cache.Insert("CERT", dt, null, DateTime.MaxValue, TimeSpan.FromMinutes(double.Parse(System.Configuration.ConfigurationManager.AppSettings["timeout"]) * 2)); } }
Identifiants de sous-site : les informations d'identification de sous-site sont principalement utilisées pour réduire l'interaction réseau lors de vérifications répétées. Par exemple, l'utilisateur s'est connecté au sous-site a, et lorsqu'il visite à nouveau le sous-site a When , il n'est pas nécessaire d'utiliser le jeton pour accéder au site principal pour vérification, car la branche a possède déjà les informations d'identification de l'utilisateur. Les informations d'identification du sous-site sont relativement simples et peuvent utiliser une session ou un cookie.
Classe de base de la page SSO du sous-site : la page du sous-site utilisant SSO effectuera une série de traitements de jugement logique, comme l'organigramme au début de l'article. S'il y a plusieurs pages, il est impossible d'écrire une telle logique pour chaque page. OK, puis encapsulez cet ensemble de logique dans une classe de base, et toutes les pages qui souhaitent utiliser SSO peuvent hériter de cette classe de base. Comme indiqué dans le code suivant :
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> /// 授权页面基类 /// </summary> public class AuthBase : System.Web.UI.Page { protected override void OnLoad(EventArgs e) { if (Session["Token"] != null) { //分站凭证存在 Response.Write("恭喜,分站凭证存在,您被授权访问该页面!"); } else { //令牌验证结果 if (Request.QueryString["Token"] != null) { if (Request.QueryString["Token"] != "$Token$") { //持有令牌 string tokenValue = Request.QueryString["Token"]; //调用WebService获取主站凭证 SSO.SiteA.RefPassport.TokenService tokenService = new SSO.SiteA.RefPassport.TokenService(); object o = tokenService.TokenGetCredence(tokenValue); if (o != null) { //令牌正确 Session["Token"] = o; Response.Write("恭喜,令牌存在,您被授权访问该页面!"); } else { //令牌错误 Response.Redirect(this.replaceToken()); } } else { //未持有令牌 Response.Redirect(this.replaceToken()); } } //未进行令牌验证,去主站验证 else { Response.Redirect(this.getTokenURL()); } } base.OnLoad(e); } /// <summary> /// 获取带令牌请求的URL /// 在当前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> /// 去掉URL中的令牌 /// 在当前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 }
Sortie utilisateur : lorsque l'utilisateur quitte, les informations d'identification du site principal et les informations d'identification actuelles du sous-site sont respectivement effacées. Si le site A doit quitter et que les sites B et C quittent également, vous pouvez développer l'interface pour effacer les informations d'identification de chaque sous-station.
Effacer les informations d'identification/jetons expirés du site principal : effacez régulièrement les enregistrements dont le champ de délai d'attente dans le cache (DataTable) ["CERT"] dépasse l'heure actuelle.
Pour plus d'articles liés aux solutions d'implémentation d'authentification unique (SSO) basées sur .Net, veuillez prêter attention au site Web PHP chinois !