系列目录:
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形式的资源服务器