Geneva 框架

来源:互联网 发布:淘一兔淘宝信誉查询 编辑:程序博客网 时间:2024/06/04 23:37
  1. WS-Trust

          安全令牌服务 (STS) 是基于 WS-Trust协议构建、签署和颁发安全令牌的服务组件,可处理不同类型凭据的身份验证。

          WS-Trust是WS-*规范族中的一员,也是OASIS其中的一项标准,专门处理有关安全tokens的发布,更新和验证,确保各方参与者的互操作处在一个可信任的安全数据交换环境中。

          从较高层次看,WS-Trust使用四种服务操作来描述一个约定:颁发、验证、续订和取消。客户端分别调用这些操作来请求安全令牌、验证安全令牌、续订已过期的安全令牌以及取消不应再继续使用的安全令牌。WS-Trust规范定义了每个操作的语法:

          请求安全性令牌时:使用 WS-Trust 规范中定义的<RequestSecurityToken> 消息进行请求的。

          返回安全性令牌时:使用 WS-Trust 规范中定义的<RequestSecurityTokenResponse> 消息进行返回的。

          本章只是通过代码实现STS的基本功能,后面会通过反编译截获生成的RST和RSTS消息文本,可以用来理解颁发的SAML令牌的结构,也可以对令牌进行本地持久化。

    2、Geneva框架

        Geneva 框架是.NET3.5基础上的,.NET4下发布了新的框架WIF。Geneva 框架可为开发人员提供相关工具来构建基于声明的应用程序和服务,还提供相关工具来构建自定义STS 和应用程序。使用 Geneva 框架构建自定义 STS,而无需编写用于公开 WS-Trust 终结点或构建包含声明的 SAML 令牌的所有探测功能

    3、整体流程     

         利用Geneva实现整个过程的流程如下:

            Client端向IP-STS端提供身份凭据,申请令牌;IP-STS颁发基于声明的令牌;Client端利用令牌访问RP端;RP端获得并信任IP-STS对客户端身份的声明,达到联盟认证的目的
   4、STS Server端
         利用Geneva框架开发Server端和Client端非常便利。只要按需继承关键的基类,并实现关键的函数即可。  这里采用IIS托管的WCF服务来发布STS。  WCF页面:
    1 <%@ServiceHost language=C# Factory=" STS.Core.STSServiceHostFactory" Service=" STS.Core.STSServiceConfiguration"%>

      

          为了自定义的需求,这里的工厂类和配置类都继承了Geneva框架的基类。

    1. Factory类和Configuration类
      复制代码
      1 using Microsoft.IdentityModel.Protocols.WSTrust;
      2  namespace STS.Core
      3 {
      4 public class STSServiceHostFactory : WSTrustServiceHostFactory
      5 {
      6 public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
      7 {
      8 ServiceHostBase serviceHost = base.CreateServiceHost(constructorString, baseAddresses);
      9 //可以在这里通过代码增加配置项、终结点、服务行为等
      10  
      11 return serviceHost;
      12 }
      13 public class STSServiceConfiguration : SecurityTokenServiceConfiguration
      14 {
      15 public STSServiceConfiguration () : base()
      16 {
      17 //这里可以修改STS的一些默认配置,如令牌的生存期等
      18
      19 //配置STS服务实现类
      20   this.SecurityTokenService = typeof(STSService);
      21 //设置用STS服务证书做安全令牌的签名证书
      22   this.SigningCredentials = new X509SigningCredentials(STSConfiguration.Certificate);
      23 }
      24 }
      25 }
      26  
      复制代码

         可以看到需要一个类STSService,而这个类正是实现WS-Trust的核心,我们也继承自Geneva框架的基类,只需要实现几个比较关键的函数,就可以方便的实现自定义STS服务:

              GetIssuerName():获取颁发者名称

              GetScope():获取颁发策略

              GetOutputClaimsIdentity():获取声明标识的集合,用来生成安全令牌主体

              令牌默认生存期是10个小时,如果需要修改令牌生存期,可以实现下面的函数

          GetTokenLifetime():获取令牌生存期

              令牌是加密的,如果需要公开一些信息,供客户端直接使用,可以实现下面的函数

              GetDisplayToken():获取展示令牌 

      1 using Microsoft.IdentityModel.Protocols.WSTrust;
      2  namespace STS.Core
      3 {
      4 public class STSService : SecurityTokenService
      5 {
      6 public STSService(SecurityTokenServiceConfiguration configuration)
      7 : base(configuration)
      8 {
      9 }
      10
      11 /// <summary>返回颁发者名称</summary>
      12 protected override string GetIssuerName()
      13 {
      14 // 设置令牌颁发者名称
      15 return STSConfiguration.SSO_STS_ISSUER_ADDRESS;
      16 }
      17
      18 /// <summary>分析令牌请求</summary>
      19 protected override Scope GetScope(IClaimsPrincipal principal, RequestSecurityToken request)
      20 {
      21 // 分析RST,逻辑直接在这里判断
      22 PolicyOptions options = PolicyOptions.Create(request);
      23
      24 // 地址是否需要验证
      25 if (!options.IsKnownRealm)
      26 {
      27 throw new InvalidRequestException("请求地址未通过验证");
      28 }
      29 //返回地址能不能跨域
      30 if (!options.ReplyToAddressIsWithinRealm)
      31 {
      32 throw new InvalidRequestException("不合法的返回地址");
      33 }
      34 // 令牌需不需要加密
      35 if (!options.UsesEncryption)
      36 {
      37 throw new InvalidRequestException("没找到加密证书");
      38 }
      39 // 是否需要SSL传输 (passive模式有效)
      40 //if (!options.UsesSsl)
      41 //{
      42 // if (!options.IsActive)
      43 // {
      44 // throw new InvalidRequestException("需要SSL");
      45 // }
      46 //}
      47 // 构造 scope
      48 return new PolicyScope(options, SecurityTokenServiceConfiguration.SigningCredentials);
      49 }
      50
      51 /// <summary>
      52 /// 生成安全令牌内容,返回声明标示的集合。
      53 /// </summary>
      54 protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal, RequestSecurityToken request, Scope scope)
      55 {
      56 ClaimsIdentity outputIdentity = new ClaimsIdentity();
      57
      58 // 这里开始根据主体的标识信息获取用户的详细信息,并按业务逻辑写入要颁发的安全令牌,主体的标识信息是在身份验证的时候写入的,下面会说明
      59 // 用户登录名
      60 outputIdentity.Claims.Add(new Claim(System.IdentityModel.Claims.ClaimTypes.Name, principal.Identity.Name, ClaimValueTypes.String));
      61 //也可以写一些自定义的声明,比如
      62 //用户是否是管理员
      63 outputIdentity.Claims.Add(new Claim("http://sts-server/claims/isadmin", "true", ClaimValueTypes.Boolean));
      64
      65 return outputIdentity;
      66 }
      67
      68 /// <summary>
      69 /// 可选,生成显示令牌,只有在RST的RequestDisplayToken标记为true时才生成
      70 /// </summary>
      71 protected override DisplayToken GetDisplayToken(string requestedDisplayTokenLanguage, IClaimsIdentity subject)
      72 {
      73 var displayClaims = new List<DisplayClaim>();
      74 if (subject.Claims != null)
      75 {
      76 foreach (var claim in subject.Claims)
      77 {
      78 switch (claim.ClaimType)
      79 {
      80 case WSIdentityConstants.ClaimTypes.Name:
      81 displayClaims.Add(GetStandardClaim(claim));
      82 break;
      83 default:
      84 break;
      85 }
      86 }
      87 }
      88 return new DisplayToken(requestedDisplayTokenLanguage, displayClaims);
      89 }
      90
      91 /// <summary>标准声明类型的DisplayClaim</summary>
      92 private DisplayClaim GetStandardClaim(Claim claim)
      93 {
      94 var displayClaim = DisplayClaim.CreateDisplayClaimFromClaimType(claim.ClaimType);
      95 displayClaim.DisplayValue = claim.Value;
      96 return displayClaim;
      97 }
      98 }
      99 }
      100
      复制代码

             其中PolicyScope类也是继承了Geneva框架的基类Scope,用于对RST进行分析,包括:

             请求地址是否为空;

             验证请求地址并获取RP证书用来加密安全令牌;

             请求是否主动模式;

             如果是被动模式返回地址是否跨域;

             是否启用SSL等。

         

             代码略过。

         

             这样STS的主体部分就完成了,但是还缺少一个类,用于验证客户端身份凭据。

             在这里客户端身份验证的方式采用UserName,而这个处理类也可以继承Geneva框架既有的基类:

      1using Microsoft.IdentityModel.Protocols.WSTrust;
      2namespace STS.Core
      3{
      4publicclass STSUserNameSecurityTokenHandler : UserNameSecurityTokenHandler
      5{
      6///<summary>可以进行身份验证</summary>
      7publicoverridebool CanValidateToken
      8{
      9get
      10{
      11returntrue;
      12}
      13}
      14
      15///<summary>身份验证</summary>
      16publicoverride ClaimsIdentityCollection ValidateToken(SecurityToken token)
      17{
      18if (token ==null)
      19{
      20ArgumentException e=new ArgumentException("无效的空令牌");
      21throw e;
      22}
      23UserNameSecurityToken usernameToken= token as UserNameSecurityToken;
      24if(usernameToken==null)
      25{
      26ArgumentException e=new ArgumentException("无效的UserName令牌");
      27throw e;
      28}
      29
      30// 声明集合,主要记录身份验证的信息
      31ClaimsIdentityCollection cc=new ClaimsIdentityCollection();
      32IClaimsIdentity identity=new ClaimsIdentity();
      33
      34//验证客户端身份代码……….
      35
      36//将验证结果写入标识的声明集合内,供颁发令牌时使用
      37//用户名
      38identity.Claims.Add(new Claim(System.IdentityModel.Claims.ClaimTypes.Name, usernameToken.UserName, "DoxtUserNameSecurityTokenHandler"));
      39//颁发时间
      40identity.Claims.Add(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant", XmlConvert.ToString(DateTime.Now, "yyyy-MM-ddTHH:mm:ssZ"),"http://www.w3.org/2001/XMLSchema#dateTime"));
      41cc.Add(identity);
      42
      43return cc;
      44}
      45
      46///<summary>
      47/// 拷贝
      48///</summary>
      49///<returns></returns>
      50publicoverride SecurityTokenHandler Clone()
      51{
      52returnnew STSUserNameSecurityTokenHandler();
      53}
      54}
      55}
      56
      57
      58     现在已经完成了Server端的代码,下面是WCF的配置文件,其中服务器证书和自定义的客户端身份验证类并没有在WCF的<behaviors>节点声明,而是放在Geneva框架的节点<microsoft.identityModel>内,需要注意:

      1<configSections>
      2<sectionname="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=0.6.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      3</configSections>
      4<microsoft.identityModel>
      5<service>
      6<securityTokenHandlers>
      7<!--自定义Username令牌处理类-->
      8<removetype="Microsoft.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler, Microsoft.IdentityModel, Version=0.6.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      9<addtype="STS.Core.STSUserNameSecurityTokenHandler, STS.Core"/>
      10</securityTokenHandlers>
      11<serviceCertificate>
      12<!--指定服务端证书-->
      13<certificateReferencefindValue="CN=sts-server" storeLocation="LocalMachine" storeName="My"/>
      14</serviceCertificate>
      15</service>
      16</microsoft.identityModel>
      17<system.serviceModel>
      18<services>
      19<servicename="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract">
      20<endpointaddress="wsHttp"
      21binding="ws2007HttpBinding" bindingConfiguration="wsHttpUserName"
      22contract="Microsoft.IdentityModel.Protocols.WSTrust.IWSTrust13SyncContract">
      23</endpoint>
      24<endpointaddress="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      25</service>
      26</services>
      27<bindings>
      28<ws2007HttpBinding>
      29<bindingname="wsHttpUserName">
      30<securitymode="Message">
      31<!--指定使用Username进行客户端身份验证,并且需要建立安全上下文-->
      32<messageclientCredentialType="UserName" negotiateServiceCredential="false" establishSecurityContext="true"/>
      33</security>
      34</binding>
      35</ws2007HttpBinding>
      36</bindings>
      37<behaviors>
      38<serviceBehaviors>
      39<behaviorname="stsBehavior">
      40<serviceMetadatahttpGetEnabled="false"/>
      41<serviceDebugincludeExceptionDetailInFaults="false"/>
      42</behavior>
      43</serviceBehaviors>
      44</behaviors>
      45</system.serviceModel>
      46
      47

    原创粉丝点击