基于Geneva框架的STS实现--IP/STS端

 

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

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

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

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

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

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

     

  3. Geneva框架
  4.       Geneva 框架是.NET3.5基础上的,.NET4下发布了新的框架WIF。

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

     

  5. 整体流程
  6.       利用Geneva实现整个过程的流程如下:
          Client端向IP-STS端提供身份凭据,申请令牌;IP-STS颁发基于声明的令牌;Client端利用令牌访问RP端;RP端获得并信任IP-STS对客户端身份的声明,达到联盟认证的目的。

      

  1. STS Server端
  2.       利用Geneva框架开发Server端和Client端非常便利。只要按需继承关键的基类,并实现关键的函数即可。

          这里采用IIS托管的WCF服务来发布STS。

          WCF页面:

     

    1 <%@ServiceHost language=C# Factory=" STS.Core.STSServiceHostFactory" Service=" STS.Core.STSServiceConfiguration"%>

      

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

     

    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():获取展示令牌 

     

    Service类
    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框架既有的基类:

     

    Handler类
    1 using Microsoft.IdentityModel.Protocols.WSTrust;
    2 namespace STS.Core
    3 {
    4 public class STSUserNameSecurityTokenHandler : UserNameSecurityTokenHandler
    5 {
    6 /// <summary>可以进行身份验证</summary>
    7 public override bool CanValidateToken
    8 {
    9 get
    10 {
    11 return true;
    12 }
    13 }
    14
    15 /// <summary>身份验证 </summary>
    16 public override ClaimsIdentityCollection ValidateToken(SecurityToken token)
    17 {
    18 if (token == null)
    19 {
    20 ArgumentException e = new ArgumentException("无效的空令牌");
    21 throw e;
    22 }
    23 UserNameSecurityToken usernameToken = token as UserNameSecurityToken;
    24 if(usernameToken == null)
    25 {
    26 ArgumentException e = new ArgumentException("无效的UserName令牌");
    27 throw e;
    28 }
    29
    30 // 声明集合,主要记录身份验证的信息
    31 ClaimsIdentityCollection cc = new ClaimsIdentityCollection();
    32 IClaimsIdentity identity = new ClaimsIdentity();
    33
    34 //验证客户端身份代码……….
    35
    36 //将验证结果写入标识的声明集合内,供颁发令牌时使用
    37 //用户名
    38 identity.Claims.Add(new Claim(System.IdentityModel.Claims.ClaimTypes.Name, usernameToken.UserName, "DoxtUserNameSecurityTokenHandler"));
    39 //颁发时间
    40 identity.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"));
    41 cc.Add(identity);
    42
    43 return cc;
    44 }
    45
    46 /// <summary>
    47 /// 拷贝
    48 /// </summary>
    49 /// <returns></returns>
    50 public override SecurityTokenHandler Clone()
    51 {
    52 return new STSUserNameSecurityTokenHandler();
    53 }
    54 }
    55 }
    56
    57
    58

        

         现在已经完成了Server端的代码,下面是WCF的配置文件,其中服务器证书和自定义的客户端身份验证类并没有在WCF的<behaviors>节点声明,而是放在Geneva框架的节点<microsoft.identityModel>内,需要注意:

     

    Web.Config
    1 <configSections>
    2 <section name="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 <remove type="Microsoft.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler, Microsoft.IdentityModel, Version=0.6.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    9 <add type="STS.Core.STSUserNameSecurityTokenHandler, STS.Core"/>
    10 </securityTokenHandlers>
    11 <serviceCertificate>
    12 <!--指定服务端证书-->
    13 <certificateReference findValue="CN=sts-server" storeLocation="LocalMachine" storeName="My"/>
    14 </serviceCertificate>
    15 </service>
    16 </microsoft.identityModel>
    17 <system.serviceModel>
    18 <services>
    19 <service name="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract">
    20 <endpoint address="wsHttp"
    21 binding="ws2007HttpBinding" bindingConfiguration="wsHttpUserName"
    22 contract="Microsoft.IdentityModel.Protocols.WSTrust.IWSTrust13SyncContract">
    23 </endpoint>
    24 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
    25 </service>
    26 </services>
    27 <bindings>
    28 <ws2007HttpBinding>
    29 <binding name="wsHttpUserName">
    30 <security mode="Message">
    31 <!--指定使用Username进行客户端身份验证,并且需要建立安全上下文-->
    32 <message clientCredentialType="UserName" negotiateServiceCredential="false" establishSecurityContext="true"/>
    33 </security>
    34 </binding>
    35 </ws2007HttpBinding>
    36 </bindings>
    37 <behaviors>
    38 <serviceBehaviors>
    39 <behavior name="stsBehavior">
    40 <serviceMetadata httpGetEnabled="false"/>
    41 <serviceDebug includeExceptionDetailInFaults="false"/>
    42 </behavior>
    43 </serviceBehaviors>
    44 </behaviors>
    45 </system.serviceModel>
    46
    47

     


posted @ 2010-11-18 17:11  reni  阅读(1024)  评论(1编辑  收藏  举报