单点登录 (SSO) 基础






4.65/5 (21投票s)
SSO:单点登录 (SSO) 是一种会话/用户身份验证过程,允许用户输入一个用户名和密码即可访问多个应用程序。
SSO: 单点登录 (SSO) 是一种会话/用户身份验证过程,允许用户输入一个用户名和密码即可访问多个应用程序。该过程会验证用户对他们有权访问的所有应用程序的身份,并消除切换应用程序时的进一步提示。
当我研究 SSO 基础设施时,我理解了一件事,那就是“它在执行方面非常安全,但初看之下却非常复杂”。因此,凭借我从各种来源获得的知识,我决定创建一个小型 POC 来演示 SSO 的内容及其工作原理,从最基础的开始。
通常,当用户在不同的应用程序域之间切换时,SSO 就会发挥作用。
示例: 我们在 IT 公司工作,而 IT 公司通常不会承担薪资和保险流程的负担。因此,他们会聘请一个供应商来处理这些活动。在这种情况下,您的公司网站上会有指向这些站点的链接。如果您观察 URL,它们属于不同的域(http://xxxxx.com)。但是,当您从公司网站导航到供应商网站时,它不会要求输入用户 ID 和密码。
这是否意味着两个站点的用户 ID 和密码相同?
不,在大多数实际场景中,它们是不同的。现在我们有一个问题:如何处理这种身份验证?答案:SSO 是其中一个答案。
让我们一步一步地演示并验证其功能。
步骤 1: 创建一个名为“主应用程序”的表单身份验证应用程序,代表我们的公司网站。
为什么选择表单身份验证,而不是 Windows 身份验证?
您可以使用任何一种。但最终 SSO 的工作基于声明身份验证,而与您在应用程序中使用哪种身份验证无关。我的 MainApp (SSOBase1) 有一个 MainLogin 页面和一个 MainApppage。步骤 2: 创建第二个表单应用程序,可以代表供应商或客户应用程序。
步骤 3: 为了支持表单身份验证,我们需要一个数据库来维护凭据。因此,为您的应用程序创建支持数据库的基础结构。在实际场景中,您将为两个应用程序使用不同的数据库。但为了简单起见,我创建了一个单一数据库,但在应用程序中保持了隔离。我们来看看如何实现。
数据库
主应用程序 DBML
客户应用程序 DBML
现在仔细观察 dbml 文件。主应用程序不处理 Client1 应用程序的数据库表,反之亦然。因此,我们保持了主应用程序和客户应用程序数据库基础结构之间的完全隔离。
主应用程序 / STS 数据库将包含以下信息
- 主应用程序的用户凭据。
- 为我们的主应用程序提供服务的客户/供应商应用程序列表。
- 用户及其对客户/供应商应用程序的可访问性。
客户应用程序数据库将包含以下信息
- 客户应用程序凭据。
- STS 安全令牌或声明与客户应用程序凭据之间的映射。
步骤 4: 现在实现各自的表单身份验证,以确保未经身份验证的用户无法进入各个应用程序。
主应用程序
客户应用程序 1
仔细查看 URL。您可以看到应用程序正在将未经身份验证的用户重定向到各自的登录页面进行身份验证。
步骤 5: 现在来看用户在跨域应用程序之间的切换。SSO 使用一个通常称为 STS(安全令牌系统)的身份验证提供程序。
STS: 这是身份验证用户的系统,其身份验证将受到所有方(主应用程序和供应商/客户应用程序)的信任。一旦用户通过 STS 获得身份验证,由该 STS 提供的所有客户应用程序都将信任该用户,并且不再提示进一步的身份验证。
STS 可以是主应用程序的一部分,也可以是独立的服务器或应用程序。
现在让我们看看我们在应用程序中是如何处理的。如果您仔细观察主应用程序的快照,我们会有一个名为“Authenticator.asmx”的 Web 服务。
它充当 STS 系统,通常承担以下职责
- 身份验证用户
- 验证用户有权访问哪些应用程序。
- 提供特定的安全令牌或声明(客户应用程序会将其视为身份验证的信息片段)。
看看不同的方法
Authenticator()
:接受已登录用户 ID 的构造函数。AuthenticateUser()
:验证主应用程序用户身份的方法。GetAppAccessDetails()
:此方法将从 STS / 主应用程序的数据库收集有关当前用户已获得身份验证的所有客户或供应商站点的相关信息。AuthenticateClientUrl()
:此方法将提供供应商/客户应用程序所需的安全令牌或声明。
步骤 6: 供应商系统唯一需要做的更改是,在登录时,它需要检查用户是否已通过 STS 获得身份验证。如果是,则允许他访问客户主页。如果否,则将其重定向到客户应用程序的登录页面。为了实现这一点,我在重定向到客户应用程序时添加了一个名为“IsAuthenticatedBySSO”的查询字符串。此操作由 STS 的 AuthenticateClientUrl()
方法处理。只需按照以下来自 STS 和客户主页的代码片段。
STS
namespace SSOBase1 { [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] public class Authenticator : System.Web.Services.WebService { string _userId; public Authenticator(string UserId) { _userId = UserId; } [WebMethod] public bool AuthenticateUser(string password) { SSODBDataContext db = new SSODBDataContext(); var psw = (from c in db.MainAppCredentials where c.Userid == _userId select c.Password).ToList(); string strPsw = psw[0].ToString(); return strPsw == password ? true : false; } [WebMethod] public DataTable GetAppAccessDetails() { SSODBDataContext db = new SSODBDataContext(); var results=(from a in db.Apps join ua in db.UserAppMaps on a.AppId equals ua.AppId where ua.MUserId == _userId select a).ToList(); DataTable dtRes=new DataTable(); dtRes.Columns.Add("ApplicationName"); dtRes.Columns.Add("ApplicationURL"); foreach(var c in results) { DataRow dr=dtRes.NewRow(); dr[0]=c.AppName; dr[1]=c.AppUrl; dtRes.Rows.Add(dr); } return dtRes; } [WebMethod] public string AuthenticateClientUrl(string clientUrl) { return clientUrl + "?UserId=" + _userId + "&IsAuthenticatedBySSO=1"; } } }
客户主页
namespace SSOTestClientApp1 { public partial class Client1MainPage : System.Web.UI.Page { string struser; protected void Page_Load(object sender, EventArgs e) { if (Request.QueryString["UserId"] == null && Session["FromClient1Login"] == null) FormsAuthentication.RedirectToLoginPage(); else { if (Session["FromClient1Login"] != null) { struser = Session["Userid"].ToString(); lblMessage.Text = "Welcome " + struser + System.Environment.NewLine + " This Login is from Client 1 login"; } else if (Request.QueryString["IsAuthenticatedBySSO"].ToString()== "1") { struser = Request.QueryString["UserId"].ToString(); SSOClient1DBDataContext db = new SSOClient1DBDataContext(); var _appSpecificUserName = (from u in db.Client1Maps where u.MainAppUserId == struser select u.C1AppUserId).ToList(); struser = _appSpecificUserName[0].ToString(); lblMessage.Text = "Welcome " + struser + System.Environment.NewLine + " This Login is from SSO"; } } } } }
在客户主页中
- 第一部分突出显示的代码将验证用户是否通过 Client1 登录页面或 STS 获得身份验证。如果是,它会将用户重定向到 Client1 应用程序的登录页面。
- 第二部分突出显示的代码将在用户直接通过供应商或客户登录页面获得身份验证时显示消息。
- 第三部分将在用户通过 STS 获得身份验证时显示消息。
步骤 7: 现在,让我们访问供应商主页。
让我们使用供应商凭据登录。
现在访问主应用程序。
进入主应用程序。
现在主应用程序主页上的这些超链接是如何显示的?看看下面的代码
namespace SSOBase1 { public partial class MainAppPage : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (Session["UserId"] == null) FormsAuthentication.RedirectToLoginPage(); else { string _userId = Session["UserId"].ToString(); lblMessage.Text = "Welcome " + _userId + " to Main Application."; Authenticator svc = new Authenticator(_userId); DataTable dtResult = svc.GetAppAccessDetails(); foreach (DataRow dr in dtResult.Rows) { HyperLink _hyperlink = new HyperLink(); _hyperlink.Text = dr["ApplicationName"].ToString(); _hyperlink.NavigateUrl = svc.AuthenticateClientUrl(dr["ApplicationURL"].ToString()); divLinks.Controls.Add(_hyperlink); divLinks.Controls.Add(new LiteralControl("<BR>")); } } } } }
现在点击 Client1 链接
看看消息,并与客户主页代码进行核对,您将看到 LoginUser 映射是如何完成的,以及客户应用程序是如何接受 STS 提供的令牌的。
但我使用了查询字符串作为安全令牌,有人可能会使用其他方法。因此,SSO 通过采用 SAML(安全断言标记语言)标准化了令牌方法。我们将在下一篇文章中讨论所有标准和实际实现。
现在我们清楚地理解了 SSO 的基本框架。
代码:点击此处下载 SSOBase1
对您有帮助吗?请告诉我您的评论/问题。