我们接下来的demo将包括以下的工程:

  1. SiteA —— 基于.net framework 4.5的MVC 4程序,使用WIF 4.5的SDK,第一个RP
  2. SiteB —— 基于.net framework 4.5的MVC 4程序,使用WIF 3.5的SDK,第二个RP
  3. SiteC —— 基于.net framework 4.0的MVC 4程序,使用WIF 3.5的SDK,第三个RP
  4. SiteD —— 基于.net framework 4.0 的WebApplication程序,使用WIF 3.5的SDK,第四个RP
  5. STS —— 基于.net framework 4.5 的MVC 4程序,作为IP

一、创建第一个RP

以管理员身份打开vs2012,在起始页上点击“新建项目”,在左边的“模板”树下,展开“其它项目类型”,然后选择“Visual Studio解决方案”,“名称”输入框里输入WIFSSO,然后选择解决方案的路径后点击”确定“,如图:

在”解决方案资源管理器“中,在新建好的解决方案上点右键,选择”添加“->”新建项目“。在弹出的对话框中选择”ASP.NET MVC 4 Web应用程序“,记得.Net Framework版本选4.5,名称起名为”SiteA“,然后点确定,如图:

在弹出的“新ASP.NET MVC 4项目”对话框中直接点“确定”,第一个RP项目新建完成后,添加以下两个引用:System.IdentityModel和System.IdentityModel.Services。这次的教程不使用Identity and Access Tool,而是直接修改web.config文件,这样能使大家对WIF的配置有更深入的了解。

打开web.config文件,将configSections节里的entityFramework配置节点删掉,因为我们不需要用到Entity Framework。最好把web.config中关于Entity Framework相关的配置全都删掉,因为我们都用不上。然后加上以下这两个节点:

  1. <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  2. <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />

将authentication节的mode属性设为None,并把里面的form节点删掉,因为我们采用的是WIF的身份验证方式,而不是传统的Forms身份验证。然后增加authorization节点,不允许匿名用户访问站点:

  1. <authorization>
  2. <deny users="?"/>
  3. </authorization>

在system.webServer节点下增加2个HttpModule的配置节点:

  1. <modules>
  2. <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
  3. <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
  4. </modules>

最后,增加WIF的配置节点:

  1. <system.identityModel>
  2. <identityConfiguration>
  3. <audienceUris mode="Always">
  4. <add value="http://www.sitea.com" />
  5. </audienceUris>
  6. <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  7. <trustedIssuers>
  8. <add name="http://www.sts.com" thumbprint="FD1425A2F30937786F46E52E43B01AFD54E5D64D"/>
  9. </trustedIssuers>
  10. </issuerNameRegistry>
  11. </identityConfiguration>
  12. </system.identityModel>
  13. <system.identityModel.services>
  14. <federationConfiguration>
  15. <cookieHandler requireSsl="false" />
  16. <wsFederation passiveRedirectEnabled="true" issuer="http://www.sts.com" realm="http://www.sitea.com" reply="http://www.sitea.com" requireHttps="false"/>
  17. </federationConfiguration>
  18. </system.identityModel.services>

我来详细解释一下这些节点的意义。audienceUris指定了一组可以被RP接受的身份标识URI,只有这些配置中的URI范围内的令牌才可以被接受。这里,我把siteA配置在这里。trustedIssuers就是受信任的发行者,由于我们这个demo没有用到SSL,所以这里我指定的thumbprint是IIS Express的指纹,这个指纹在哪里可以获得呢?打开IIS管理器,在左侧树点击根节点,然后在“功能视图”里双击“服务器证书",如下图:

在打开的证书列表里,找到IIS Express Development Certificate,双击,在弹出的”证书“对话框中点击“详细信息”页签,找到“指纹”然后点击,把框里的指纹拷下来,全都改成大写后粘贴到thumbnail的值里去:

接下来配置federationConfiguration节点,它表示配置WSFederationAuthenticationModule (WSFAM) 和SessionAuthenticationModule (SAM) 时使用联合身份验证通过的 WS 联合身份验证协议。这里我们使用WS 联合身份验证的身份验证模块 (WSFAM),关于该节点的详细配置信息,请参考:http://msdn.microsoft.com/zh-cn/library/office/apps/hh568665.aspx

好,这样一来,SiteA的配置就已经完成了,然后我们来加点代码。

打开/Views/Home/Index.cshtml,将原有的代码删掉,改为如下代码:

  1. @using System.Security.Claims
  2. @{
  3. ViewBag.Title = "SiteA主页";
  4. ClaimsIdentity ci = User.Identity as ClaimsIdentity;
  5. if(ci!=null)
  6. {
  7. <h2>@ci.FindFirst(ClaimTypes.Name).Value</h2>
  8. <h2>@ci.FindFirst(ClaimTypes.Email).Value</h2>
  9. }
  10. }
  11. <a href="http://www.sts.com/Account/LogOff">退出</a>

代码很简单,只要当前用户处于已登录状态,就把用户的名称和Email显示在页面上。

至此,SiteA就已经完成了。你是不是迫不及待的想要运行了呢?别急,虽然有SiteA了,但还没有STS呢,现在启动SiteA,由于没登录,所以它会跳转到STS,但STS还不存在,所以会出错的。

二、创建STS

接下来我们来创建STS,在解决方案上新建项目,新建一个名为STS的MVC 4应用程序,.Net Framework选择4.5,项目模板选择“Internet应用程序",确定。

添加System.IdentityModel和System.IdentityModel.Services这两个引用,打开web.config,为forms节点添加两个属性:

  1. <forms loginUrl="~/Account/Login" timeout="2880" slidingExpiration="true" name=".STSASPAUTH" />

在AppSettings里增加如下三个节点:

  1. <add key="IssuerName" value="PassiveSigninSTS" />
  2. <add key="SigningCertificateName" value="CN=localhost" />
  3. <add key="EncryptingCertificateName" value="" />

同样禁止匿名用户访问:

  1. <authorization>
  2. <deny users="?"/>
  3. </authorization>

在应用程序下新建一个名为Services的文件夹,在里面新建一个类文件,名为:CertificateUtil,用于获取证书,具体代码如下:

  1. public class CertificateUtil
  2. {
  3. public static X509Certificate2 GetCertificate(StoreName name, StoreLocation location, string subjectName)
  4. {
  5. X509Store store = new X509Store(name, location);
  6. X509Certificate2Collection certificates = null;
  7. store.Open(OpenFlags.ReadOnly);
  8. try
  9. {
  10. X509Certificate2 result = null;
  11. certificates = store.Certificates;
  12. for (int i = 0; i < certificates.Count; i++)
  13. {
  14. X509Certificate2 cert = certificates[i];
  15. if (cert.SubjectName.Name.ToLower() == subjectName.ToLower())
  16. {
  17. if (result != null)
  18. throw new ApplicationException(string.Format("subject Name {0}存在多个证书", subjectName));
  19. result = new X509Certificate2(cert);
  20. }
  21. }
  22. if (result == null)
  23. {
  24. throw new ApplicationException(string.Format("没有找到用于 subject Name {0} 的证书", subjectName));
  25. }
  26. return result;
  27. }
  28. finally
  29. {
  30. if (certificates != null)
  31. {
  32. for (int i = 0; i < certificates.Count; i++)
  33. {
  34. certificates[i].Reset();
  35. }
  36. }
  37. store.Close();
  38. }
  39. }
  40. }

创建新类,名为Common,存放几个常量:

  1. public class Common
  2. {
  3. public const string IssuerName = "IssuerName";
  4. public const string SigningCertificateName = "SigningCertificateName";
  5. public const string EncryptingCertificateName = "EncryptingCertificateName";
  6. }

创建新类,名为SingleSignOnManager,用于注册RP以及获取RP列表:

  1. public class SingleSignOnManager
  2. {
  3. const string SITECOOKIENAME = "StsSiteCookie";
  4. const string SITENAME = "StsSite";
  5. /// <summary>
  6. /// Returns a list of sites the user is logged in via the STS
  7. /// </summary>
  8. /// <returns></returns>
  9. public static string[] SignOut()
  10. {
  11. if (HttpContext.Current != null &&
  12. HttpContext.Current.Request != null &&
  13. HttpContext.Current.Request.Cookies != null
  14. )
  15. {
  16. HttpCookie siteCookie =
  17. HttpContext.Current.Request.Cookies[SITECOOKIENAME];
  18. if (siteCookie != null)
  19. return siteCookie.Values.GetValues(SITENAME);
  20. }
  21. return new string[0];
  22. }
  23. public static void RegisterRP(string SiteUrl)
  24. {
  25. if (HttpContext.Current != null &&
  26. HttpContext.Current.Request != null &&
  27. HttpContext.Current.Request.Cookies != null
  28. )
  29. {
  30. // get an existing cookie or create a new one
  31. HttpCookie siteCookie =
  32. HttpContext.Current.Request.Cookies[SITECOOKIENAME];
  33. if (siteCookie == null)
  34. siteCookie = new HttpCookie(SITECOOKIENAME);
  35. siteCookie.Values.Add(SITENAME, SiteUrl);
  36. HttpContext.Current.Response.AppendCookie(siteCookie);
  37. }
  38. }
  39. }

创建新类,CustomSecurityTokenService,自定义令牌服务,继承SecurityTokenService,用于返回需要的声明令牌:

  1. public class CustomSecurityTokenService : SecurityTokenService
  2. {
  3. private readonly SigningCredentials signingCreds;
  4. private readonly EncryptingCredentials encryptingCreds;
  5. public CustomSecurityTokenService(SecurityTokenServiceConfiguration config)
  6. : base(config)
  7. {
  8. this.signingCreds = new X509SigningCredentials(
  9. CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, WebConfigurationManager.AppSettings[Common.SigningCertificateName]));
  10. if (!string.IsNullOrWhiteSpace(WebConfigurationManager.AppSettings[Common.EncryptingCertificateName]))
  11. {
  12. this.encryptingCreds = new X509EncryptingCredentials(
  13. CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, WebConfigurationManager.AppSettings[Common.EncryptingCertificateName]));
  14. }
  15. }
  16. /// <summary>
  17. /// 此方法返回要发布的令牌内容。内容由一组ClaimsIdentity实例来表示,每一个实例对应了一个要发布的令牌。当前Windows Identity Foundation只支持单个令牌发布,因此返回的集合必须总是只包含单个实例。
  18. /// </summary>
  19. /// <param name="principal">调用方的principal</param>
  20. /// <param name="request">进入的 RST,我们这里不用它</param>
  21. /// <param name="scope">由之前通过GetScope方法返回的范围</param>
  22. /// <returns></returns>
  23. protected override ClaimsIdentity GetOutputClaimsIdentity(ClaimsPrincipal principal, RequestSecurityToken request, Scope scope)
  24. {
  25. //返回一个默认声明集,里面了包含自己想要的声明
  26. //这里你可以通过ClaimsPrincipal来验证用户,并通过它来返回正确的声明。
  27. string identityName = principal.Identity.Name;
  28. string[] temp = identityName.Split('|');
  29. ClaimsIdentity outgoingIdentity = new ClaimsIdentity();
  30. outgoingIdentity.AddClaim(new Claim(ClaimTypes.Email, temp[0]));
  31. outgoingIdentity.AddClaim(new Claim(ClaimTypes.DateOfBirth, temp[1]));
  32. outgoingIdentity.AddClaim(new Claim(ClaimTypes.Name, temp[2]));
  33. SingleSignOnManager.RegisterRP(scope.AppliesToAddress);
  34. return outgoingIdentity;
  35. }
  36. /// <summary>
  37. /// 此方法返回用于令牌发布请求的配置。配置由Scope类表示。在这里,我们只发布令牌到一个由encryptingCreds字段表示的RP标识        /// </summary>
  38. /// <param name="principal"></param>
  39. /// <param name="request"></param>
  40. /// <returns></returns>
  41. protected override Scope GetScope(ClaimsPrincipal principal, RequestSecurityToken request)
  42. {
  43. // 使用request的AppliesTo属性和RP标识来创建Scope
  44. Scope scope = new Scope(request.AppliesTo.Uri.AbsoluteUri, this.signingCreds);
  45. if (Uri.IsWellFormedUriString(request.ReplyTo, UriKind.Absolute))
  46. {
  47. if (request.AppliesTo.Uri.Host != new Uri(request.ReplyTo).Host)
  48. scope.ReplyToAddress = request.AppliesTo.Uri.AbsoluteUri;
  49. else
  50. scope.ReplyToAddress = request.ReplyTo;
  51. }
  52. else
  53. {
  54. Uri resultUri = null;
  55. if (Uri.TryCreate(request.AppliesTo.Uri, request.ReplyTo, out resultUri))
  56. scope.ReplyToAddress = resultUri.AbsoluteUri;
  57. else
  58. scope.ReplyToAddress = request.AppliesTo.Uri.ToString();
  59. }
  60. if (this.encryptingCreds != null)
  61. {
  62. // 如果STS对应多个RP,要选择证书指定到请求令牌的RP,然后再用 encryptingCreds
  63. scope.EncryptingCredentials = this.encryptingCreds;
  64. }
  65. else
  66. scope.TokenEncryptionRequired = false;
  67. return scope;
  68. }
  69. }

最后添加新类CustomSecurityTokenServiceConfiguration,继承SecurityTokenServiceConfiguration:

  1. public class CustomSecurityTokenServiceConfiguration : SecurityTokenServiceConfiguration
  2. {
  3. private static readonly object syncRoot = new object();
  4. private const string CustomSecurityTokenServiceConfigurationKey = "CustomSecurityTokenServiceConfigurationKey";
  5. public CustomSecurityTokenServiceConfiguration()
  6. : base(WebConfigurationManager.AppSettings[Common.IssuerName])
  7. {
  8. this.SecurityTokenService = typeof(CustomSecurityTokenService);
  9. }
  10. public static CustomSecurityTokenServiceConfiguration Current
  11. {
  12. get
  13. {
  14. HttpApplicationState app = HttpContext.Current.Application;
  15. CustomSecurityTokenServiceConfiguration config = app.Get(CustomSecurityTokenServiceConfigurationKey) as CustomSecurityTokenServiceConfiguration;
  16. if (config != null)
  17. return config;
  18. lock (syncRoot)
  19. {
  20. config = app.Get(CustomSecurityTokenServiceConfigurationKey) as CustomSecurityTokenServiceConfiguration;
  21. if (config == null)
  22. {
  23. config = new CustomSecurityTokenServiceConfiguration();
  24. app.Add(CustomSecurityTokenServiceConfigurationKey, config);
  25. }
  26. return config;
  27. }
  28. }
  29. }
  30. }

打开/Controllers/HomeController.cs,将Index()方法修改如下:

  1. public ActionResult Index()
  2. {
  3. FederatedPassiveSecurityTokenServiceOperations.ProcessRequest(
  4. System.Web.HttpContext.Current.Request,
  5. User as ClaimsPrincipal,
  6. CustomSecurityTokenServiceConfiguration.Current.CreateSecurityTokenService(),
  7. System.Web.HttpContext.Current.Response);
  8. return View();
  9. }

打开/Controllers/AccountController.cs,将Login(LoginModel model, string returnUrl)方法修改如下:

  1. [HttpPost]
  2. [AllowAnonymous]
  3. [ValidateAntiForgeryToken]
  4. public ActionResult Login(LoginModel model, string returnUrl)
  5. {
  6. var query = HttpUtility.ParseQueryString(Request.UrlReferrer.Query);
  7. if (model.UserName == "ojlovecd@csdn.net" && model.Password == "123456")
  8. {
  9. FormsAuthentication.SetAuthCookie("ojlovecd@csdn.net|1983-10-22|oujian", false);
  10. if (!string.IsNullOrEmpty(returnUrl))
  11. return Redirect(returnUrl);
  12. return RedirectToAction("Index", "Home");
  13. }
  14. return View(model);
  15. }

LogOff方法修改如下:

  1. public ActionResult LogOff()
  2. {
  3. FormsAuthentication.SignOut();
  4. ViewData["AddressesExpected"] = SingleSignOnManager.SignOut().Distinct().ToArray();
  5. return View("Login");
  6. }

打开/Views/Account/Login.cshtml,添加以下代码:

  1. @{
  2. ViewBag.Title = "登录";
  3. var addressesExpected = ViewData["AddressesExpected"] as string[];
  4. if (addressesExpected != null)
  5. {
  6. foreach (var address in addressesExpected)
  7. {
  8. <img src="@(address)?wa=wsignoutcleanup1.0" style="display:none;" />
  9. }
  10. }
  11. }

OK,至此STS也已经完成了。把SiteA和STS都部署到IIS上,然后打开C:\Windows\System32\Drivers\etc\hosts文件,添加几个站点:

  1. 127.0.0.1   www.sitea.com
  2. 127.0.0.1   www.siteb.com
  3. 127.0.0.1   www.sitec.com
  4. 127.0.0.1   www.sited.com
  5. 127.0.0.1   www.sts.com

好了,在浏览器输入www.sitea.com,看看如何,它马上跳转到了www.sts.com的登录页面,输入ojlovecd@csdn.net,密码123456,确定,登录成功,跳回到了www.sitea.com,并显示出了用户名和Email:

点击退出,将注销当前用户,并跳转到登录页。

三、创建其它RP

OK,站点A搞定了,那其它站点如何呢?现在只是最简单的登录退出功能而已,说好的单点登录呢?
别急,接下来就一一实现。
新建基于.NET Framework4.5的MVC4程序,添加Microsoft.IdentityModel引用。修改web.config,configSections里添加如下节点:

  1. <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

Compilation里增加Microsoft.IdentityModel的程序集:

  1. <compilation debug="true" targetFramework="4.5" >
  2. <assemblies>
  3. <add assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  4. </assemblies>
  5. </compilation>

身份验证改为None,添加authorization节点,禁止匿名用户访问:

  1. <authentication mode="None">
  2. </authentication>
  3. <authorization>
  4. <deny users="?" />
  5. </authorization>

添加三个httpModules:

  1. <httpModules>
  2. <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  3. <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  4. <add name="ClaimsAuthorizationModule" type="Microsoft.IdentityModel.Web.ClaimsAuthorizationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  5. </httpModules>
  6. system.webServer里添加以下三个modules:
  7. <modules >
  8. <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />
  9. <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />
  10. <add name="ClaimsAuthorizationModule" type="Microsoft.IdentityModel.Web.ClaimsAuthorizationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />
  11. </modules>

最后增加microsoft.identityModel节点:

  1. <microsoft.identityModel>
  2. <service>
  3. <audienceUris mode="Always">
  4. <add value="http://www.siteb.com" />
  5. </audienceUris>
  6. <federatedAuthentication>
  7. <wsFederation passiveRedirectEnabled="true" issuer="http://www.sts.com" realm="http://www.siteb.com" reply="http://www.siteb.com"  requireHttps="false" />
  8. <cookieHandler requireSsl="false" />
  9. </federatedAuthentication>
  10. <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
  11. <trustedIssuers>
  12. <add thumbprint="FD1425A2F30937786F46E52E43B01AFD54E5D64D" name="http://www.sts.com" />
  13. </trustedIssuers>
  14. </issuerNameRegistry>
  15. </service>
  16. </microsoft.identityModel>

以上配置跟SIteA差不多,只是WIF3.5和4.5的区别而已,在这里就不赘述了,要获取详细信息,请参考微软官方网站。
打开/Views/Home/Index.cshtml,将代码修改如下,在SiteB里我们显示Email和生日:

  1. @using Microsoft.IdentityModel.Claims
  2. @{
  3. ViewBag.Title = "SiteB主页";
  4. ClaimsIdentity ci = User.Identity as ClaimsIdentity;
  5. if(ci!=null)
  6. {
  7. <h2>@ci.Claims.SingleOrDefault(c=>c.ClaimType == ClaimTypes.Email).Value</h2>
  8. <h2>@ci.Claims.SingleOrDefault(c=>c.ClaimType == ClaimTypes.DateOfBirth).Value</h2>
  9. }
  10. }
  11. <a href="http://www.sts.com/Account/LogOff">退出</a>

OK,部署到IIS上,然后运行,页面跳转到了sts的登录页面,输入用户名和密码,跳转,哎哟我去,怎么报错了:

原因是从sts返回来的数据里有<>这种标签,于是asp.net认为那是有危险的,于是抛出了异常,这个异常大家估计以前也碰到过,最简单粗暴的方法就是把验证请求的配置改为false,但这里我不建议这么干, 为此,我们专门用一个类来处理这种情况。
在SiteB目录下新建一个文件夹名为Services,然后添加一个类,名为SampleRequestValidator:

  1. /// <summary>
  2. /// This SampleRequestValidator validates the wresult parameter of the
  3. /// WS-Federation passive protocol by checking for a SignInResponse message
  4. /// in the form post. The SignInResponse message contents are verified later by
  5. /// the WSFederationPassiveAuthenticationModule or the WIF signin controls.
  6. /// </summary>
  7. public class SampleRequestValidator : RequestValidator
  8. {
  9. protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
  10. {
  11. validationFailureIndex = 0;
  12. if (requestValidationSource == RequestValidationSource.Form && collectionKey.Equals(WSFederationConstants.Parameters.Result, StringComparison.Ordinal))
  13. {
  14. return true;
  15. }
  16. return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
  17. }
  18. }

然后在web.config里加入这个类的配置:

  1. <httpRuntime targetFramework="4.5" requestValidationType="SiteC.Services.SampleRequestValidator" />

使用WIF实现单点登录Part III —— 正式实战的更多相关文章

  1. 使用WIF实现单点登录Part III —— 正式实战 -摘自网络

    经过前两篇文章,估计大家对WIF已经有比较充分的认识了,估计大家在经过了枯燥的理论以后,肯定是摩拳擦掌赶紧付诸于行动了.没办法,咱们程序员就是这个毛病.那好吧,我也不那么多废话了,直接进入正题吧. 我 ...

  2. ABP集成WIF实现单点登录

    ABP集成WIF实现单点登录 参考 ojlovecd写了三篇关于WIF文章. 使用WIF实现单点登录Part III —— 正式实战 使用WIF的一些开源示例. https://github.com/ ...

  3. 使用WIF实现单点登录Part IV —— 常见问题

    InvalidOperationException: ID1073: 尝试使用 ProtectedData API 解密 Cookie 时出现 CryptographicException (有关详细 ...

  4. 使用WIF实现单点登录Part II —— Windows Identity Foundation基本原理

    在上一篇文章中,我们已经使用WIF构建了一个基于MVC4的简单的身份验证程序,在这篇文章里,我们将探讨一下到底什么是WIF,以及它的工作原理.然后在下一篇文章开始,我们将实际操作,实现单点登录功能. ...

  5. 使用WIF实现单点登录Part II —— Windows Identity Foundation基本原理 -摘自网络

    在上一篇文章中,我们已经使用WIF构建了一个基于MVC4的简单的身份验证程序,在这篇文章里,我们将探讨一下到底什么是WIF,以及它的工作原理.然后在下一篇文章开始,我们将实际操作,实现单点登录功能. ...

  6. 使用WIF实现单点登录Part I——Windows Identity Foundation介绍及环境搭建 -摘自网络

    上个月有一个星期的时间都在研究asp.net mvc统一身份验证及单点登录的实现.经过了一番的探索,最终决定使用微软的Windows Identity Foundation.但是这东西用的人貌似不多, ...

  7. 使用WIF实现单点登录Part I——Windows Identity Foundation介绍及环境搭建

    首先先说一下什么是WIF(Windows Identity Foundation).由于各种历史原因,身份验证和标识的管理一般都比较无规律可循.在软件里加入“身份验证”功能意味着要在你的代码里混进处理 ...

  8. 实战:ADFS3.0单点登录系列-集成MVC

    本文将讲解如何让MVC应用程序与ADFS集成,完成认证的过程. 目录: 实战:ADFS3.0单点登录系列-总览 实战:ADFS3.0单点登录系列-前置准备 实战:ADFS3.0单点登录系列-ADFS3 ...

  9. 实战:ADFS3.0单点登录系列-总览

    本系列将以一个实际项目为背景,介绍如何使用ADFS3.0实现SSO.其中包括SharePoint,MVC,Exchange等应用程序的SSO集成. 整个系列将会由如下几个部分构成: 实战:ADFS3. ...

随机推荐

  1. ANR

    /data/anr/traces.txt MySQL: select version();

  2. Redis多机功能介绍

    Redis多机功能目的:以单台Redis服务器过渡到多台Redis服务器 Redis单机在生产环境中存在的问题 1.内存容量不足 Redis使用内存来存书数据库中的数据,但是对于一台机器来说,硬件的内 ...

  3. 海外支付:遍布全球的Paypal

    海外支付:遍布全球的Paypal 吴剑 2015-11-26 原创文章,转载必需注明出处:http://www.cnblogs.com/wu-jian 吴剑 http://www.cnblogs.co ...

  4. 【转】appium_python_API文档

    1.contextscontexts(self): Returns the contexts within the current session. 返回当前会话中的上下文,使用后可以识别H5页面的控 ...

  5. 初探接口测试框架--python系列4

    点击标题下「蓝色微信名」可快速关注 坚持的是分享,搬运的是知识,图的是大家的进步,没有收费的培训,没有虚度的吹水,喜欢就关注.转发(免费帮助更多伙伴)等来交流,想了解的知识请留言,给你带来更多价值,是 ...

  6. emacs org mode 中的标签全参考

    把交叉的信息关联起来的最好的方式就是打标签. emacs 的 org 模式对标签的支持非常强大. 每一个标题都可以在最后包含标签列表.标签由字母.数字.'_' 和 '@' 组成.标签的前后必需有一个冒 ...

  7. python基础set

    1.set set是一个无序的不重复的集合 li=[11,22,33,11] s=set(li) print(s) {11,22,33}  set提供的方法 1.add(self, *args, ** ...

  8. excel中单元格计算

    首先,得明确excel中相对引用和绝对引用的概念,这里$符号起着关键作用,当在一个行或列的指示符前面加$则表示绝对引用,否则相对引用,具体: 1.相对引用,复制公式时地址跟着发生变化,如C1单元格有公 ...

  9. 华为OJ平台——放苹果(典型整数划分问题)

    题目描述: 输入m,n,分别表示苹果数与盘子的总数,要求输出苹果放在n个盘子的方法总数(注意511和151是一种情况),例如输入 7 3 输出8((7),(6,1),(5,2),(4,3),(5,1, ...

  10. mac 下安装nginx

    1,mac下的依赖: pcre-8.38.tar.gz nginx-1.4.7.tar.gz 2,解压pcre:进入器解压目录. EddydeMacBook-Pro:~ eddy$ cd /Users ...