DotNetOpenAuth实践之WCF资源服务器配置

系列目录:

DotNetOpenAuth实践系列(源码在这里)

 

上一篇我们写了一个OAuth2的认证服务器,我们也获取到access_token,那么这个token怎么使用呢,我们现在就来揭开

一般获取access_token用处就是访问接口资源,不然也用不到怎么大费周章的还要获取个token再去访问资源

而接口有几类:

WCF服务接口,WebApi,还有自己用如ashx,aspx写的接口提供给前端调用的接口

其中WCF接口DotNetOpenAuth.Sample例子中已经做了

这些接口公开对外就需要一个认证的过程才能访问(当然内部或者内网使用的也不用这么麻烦,毕竟认证也是影响性能的)

废话不多说,我们下面开始搭建资源服务器

首先,搭建WCF服务接口的资源认证

一、创建WCF资源服务器项目

2、添加DotNetOpenAuth到项目中

3、添加WCF服务,为了方便,我们添加支持ajax的wcf服务

 

4、重写ServiceAuthorizationManager

  1 using System;
  2 using System.Collections.Generic;
  3 using System.IdentityModel.Policy;
  4 using System.Linq;
  5 using System.Net.Http;
  6 using System.Security.Cryptography;
  7 using System.Security.Principal;
  8 using System.ServiceModel;
  9 using System.ServiceModel.Channels;
 10 using System.ServiceModel.Security;
 11 using System.ServiceModel.Web;
 12 using System.Threading;
 13 using System.Threading.Tasks;
 14 using System.Web;
 15 using DotNetOpenAuth.Messaging;
 16 using DotNetOpenAuth.OAuth2;
 17 using ProtocolException = System.ServiceModel.ProtocolException;
 18 
 19 namespace WCFRescourcesServer.Code
 20 {
 21     public class IdefavAuthorizationManager : ServiceAuthorizationManager
 22     {
 23         public IdefavAuthorizationManager()
 24         {
 25         }
 26 
 27         protected override bool CheckAccessCore(OperationContext operationContext)
 28         {
 29             if (!base.CheckAccessCore(operationContext))
 30             {
 31                 return false;
 32             }
 33 
 34             var httpDetails = operationContext.RequestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
 35             var requestUri = operationContext.RequestContext.RequestMessage.Properties.Via;
 36 
 37             return Task.Run(async delegate
 38             {
 39                 ProtocolFaultResponseException exception = null;
 40                 try
 41                 {
 42                     var principal = await VerifyOAuth2Async(
 43                         httpDetails,
 44                         requestUri,
 45                         operationContext.IncomingMessageHeaders.Action ?? operationContext.IncomingMessageHeaders.To.AbsolutePath);
 46                     if (principal != null)
 47                     {
 48                         var policy = new OAuthPrincipalAuthorizationPolicy(principal);
 49                         var policies = new List<IAuthorizationPolicy> { policy };
 50 
 51                         var securityContext = new ServiceSecurityContext(policies.AsReadOnly());
 52                         if (operationContext.IncomingMessageProperties.Security != null)
 53                         {
 54                             operationContext.IncomingMessageProperties.Security.ServiceSecurityContext = securityContext;
 55                         }
 56                         else
 57                         {
 58                             operationContext.IncomingMessageProperties.Security = new SecurityMessageProperty
 59                             {
 60                                 ServiceSecurityContext = securityContext,
 61                             };
 62                         }
 63 
 64                         securityContext.AuthorizationContext.Properties["Identities"] = new List<IIdentity> { principal.Identity, };
 65 
 66                         return true;
 67                     }
 68                     else
 69                     {
 70                         return false;
 71                     }
 72                 }
 73                 catch (ProtocolFaultResponseException ex)
 74                 {
 75 
 76                     exception = ex;
 77                 }
 78                 catch (ProtocolException ex)
 79                 {
 80 
 81                 }
 82 
 83                 if (exception != null)
 84                 {
 85                     
 86                     // Return the appropriate unauthorized response to the client.
 87                     var outgoingResponse = await exception.CreateErrorResponseAsync(CancellationToken.None);
 88                     if (WebOperationContext.Current != null)
 89                         this.Respond(WebOperationContext.Current.OutgoingResponse, outgoingResponse);
 90                 }
 91 
 92                 return false;
 93             }).GetAwaiter().GetResult();
 94         }
 95 
 96         private static async Task<IPrincipal> VerifyOAuth2Async(HttpRequestMessageProperty httpDetails, Uri requestUri, params string[] requiredScopes)
 97         {
 98             var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer((RSACryptoServiceProvider)Common.Configuration.SigningCertificate.PublicKey.Key, (RSACryptoServiceProvider)Common.Configuration.EncryptionCertificate.PrivateKey));
 99             return await resourceServer.GetPrincipalAsync(httpDetails, requestUri, requiredScopes: requiredScopes);
100         }
101 
102         private void Respond(OutgoingWebResponseContext responseContext, HttpResponseMessage responseMessage)
103         {
104             responseContext.StatusCode = responseMessage.StatusCode;
105             responseContext.SuppressEntityBody = true;
106             foreach (var header in responseMessage.Headers)
107             {
108                 responseContext.Headers[header.Key] = header.Value.First();
109             }
110         }
111     }
112 }

5、实现OAuthPrincipalAuthorizationPolicy接口

 1 namespace OAuthResourceServer.Code {
 2     using System;
 3     using System.Collections.Generic;
 4     using System.IdentityModel.Claims;
 5     using System.IdentityModel.Policy;
 6     using System.Linq;
 7     using System.Security.Principal;
 8     using System.Web;
 9 
10     public class OAuthPrincipalAuthorizationPolicy : IAuthorizationPolicy {
11         private readonly Guid uniqueId = Guid.NewGuid();
12         private readonly IPrincipal principal;
13 
14         /// <summary>
15         /// Initializes a new instance of the <see cref="OAuthPrincipalAuthorizationPolicy"/> class.
16         /// </summary>
17         /// <param name="principal">The principal.</param>
18         public OAuthPrincipalAuthorizationPolicy(IPrincipal principal) {
19             this.principal = principal;
20         }
21 
22         #region IAuthorizationComponent Members
23 
24         /// <summary>
25         /// Gets a unique ID for this instance.
26         /// </summary>
27         public string Id {
28             get { return this.uniqueId.ToString(); }
29         }
30 
31         #endregion
32 
33         #region IAuthorizationPolicy Members
34 
35         public ClaimSet Issuer {
36             get { return ClaimSet.System; }
37         }
38 
39         public bool Evaluate(EvaluationContext evaluationContext, ref object state) {
40             evaluationContext.AddClaimSet(this, new DefaultClaimSet(Claim.CreateNameClaim(this.principal.Identity.Name)));
41             evaluationContext.Properties["Principal"] = this.principal;
42             return true;
43         }
44 
45         #endregion
46     }
47 }

6、配置WCF

<system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_OpenApi" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
          <security mode="Message">
            <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
            <message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="WCFRescourcesServer.OpenApiAspNetAjaxBehavior">
          <enableWebScript />
        </behavior>
        <behavior name="WCFRescourcesServer.Services1Behavior">
          
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="myS">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
          <serviceAuthorization serviceAuthorizationManagerType="WCFRescourcesServer.Code.IdefavAuthorizationManager, WCFRescourcesServer" principalPermissionMode="Custom" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <services>
      <service name="WCFRescourcesServer.Service1" behaviorConfiguration="myS">
        <endpoint address="" behaviorConfiguration="WCFRescourcesServer.Services1Behavior" binding="wsHttpBinding" contract="WCFRescourcesServer.IService1" />
      </service>
    </services>
  </system.serviceModel>

 

 

下面这段是关键的一句,配置WCF访问验证

<serviceAuthorization serviceAuthorizationManagerType="WCFRescourcesServer.Code.IdefavAuthorizationManager, WCFRescourcesServer" principalPermissionMode="Custom"/>

二、创建WCFClient访问WCF服务

创建一个IdefavOAuth2Client的Asp.net项目然后添加服务器引用

 

访问WCF代码如下:

 1 protected async void Button1_Click(object sender, EventArgs e)
 2         {
 3             var authServer = new AuthorizationServerDescription()
 4             {
 5                 
 6                 TokenEndpoint = new Uri("http://localhost:53022/OAuth/token "),
 7                 ProtocolVersion = ProtocolVersion.V20
 8             };
 9             //var wcf= new UserAgentClient(authServer, "idefav", "1");
10             WebServerClient Client= new WebServerClient(authServer, "idefav", "1");
11 
12             var code =await Client.GetClientAccessTokenAsync(new string[] { "http://localhost:55044/IService1/DoWork" });
13             string token = code.AccessToken;
14             Service1Reference.Service1Client service1Client=new Service1Client();
15             var httpRequest = (HttpWebRequest)WebRequest.Create(service1Client.Endpoint.Address.Uri);
16             ClientBase.AuthorizeRequest(httpRequest,token);
17             var httpDetails = new HttpRequestMessageProperty();
18             httpDetails.Headers[HttpRequestHeader.Authorization] = httpRequest.Headers[HttpRequestHeader.Authorization];
19             
20             using (var scope = new OperationContextScope(service1Client.InnerChannel))
21             {
22                 
23                 if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name))
24                 {
25                     OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpDetails;
26                 }
27                 else
28                 {
29                     OperationContext.Current.OutgoingMessageProperties.Add(HttpRequestMessageProperty.Name, httpDetails);
30                 }
31                 
32                 Button1.Text= service1Client.DoWork();
33             }
34 
35 
36         }

注意:

标示的地方是可以从服务引用生成的代码上面看的到

 

当然这个Scope可以在WCF中配置

如果这里客户端和资源服务器不一致也会拒绝访问

 

我们来测试一下,把端口改成55045然后运行项目

 

结果:

 

 

 

最后,还有个注意点

DotNetOpenAuth v5.0.0-alpha3 从Nuget上面安装的有Bug,需要去github下载源代码然后编译成dll替换掉Nuget的dll

我已经编译好了一份,百度网盘:

链接:http://pan.baidu.com/s/1jGlMZye 密码:hw2o

 

 

这里的是WCF资源服务器以及客户端访问

 

下篇将会讲WebAPI形式的资源服务器

posted on 2015-10-18 23:03  idefav2010  阅读(2722)  评论(6编辑  收藏  举报