您的当前位置:首页正文

OAuth学习(2)自定义OAuth服务端(WCFREST数据访问控制)

2020-11-09 来源:筏尚旅游网

上篇了解了如何调用 OAuth 授权来获取数据,本篇介绍如何开放OAuth授权,并控制服务端数据访问。[源码下载] 先看一下图: 这两天事太多,文章整理的断断续续 OK,步入正题,这里还是要借力:DevDefined.OAuth 框架。它提供了客户端访问,服务端管理Token的基

上篇了解了如何调用 OAuth 授权来获取数据,本篇介绍如何开放OAuth授权,并控制服务端数据访问。[源码下载]
先看一下图:

这两天事太多,文章整理的断断续续

OK,步入正题,这里还是要借力: DevDefined.OAuth 框架。它提供了客户端访问,服务端管理Token的基础功能。

1. OAuthChannel
定义了服务端用户模型,OAuth的拦截器,OAuthWebServiceHostFactory(继承于WebServiceHostFactory,用于添加拦截器),以及 RequestToken 和 AccessToken 保持在内存里的容器及存取类 (InMemoryTokenRepository,InMemoryTokenStore)

OAuthWebServiceHostFactory 添加拦截器,使用了 WebServiceHost2 (Microsoft.ServiceModel.Web.dll 里,是 Microsoft 发布的WCF REST Starter Kit的一部分)
WebServiceHost2 重写了 ServiceHost 里 OnOpening 方法添加拦截器。WebServiceHost2的源代码猛击这里
OAuthWebServiceHostFactory:

using System;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using Microsoft.ServiceModel.Web;
using DevDefined.OAuth.Provider;
using OAuthChannel.Repositories;

namespace OAuthChannel
{
 public class OAuthWebServiceHostFactory : WebServiceHostFactory
 {
 public IOAuthProvider OAuthProvider { get; set; }
 public ITokenRepository AccessTokenRepository { get; set; }

 protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
 {
 var serviceHost = new WebServiceHost2(serviceType, true, baseAddresses);
 var interceptor = new OAuthChannel.OAuthInterceptor(OAuthProvider, AccessTokenRepository);
 serviceHost.Interceptors.Add(interceptor);
 return serviceHost;
 }
 }
}

拦截器(OAuthInterceptor.cs)将请求的 OAuth (Request Header中) 转换成 OAuthChannel.Models.AccessToken

public class AccessToken : TokenBase
{
	public string UserName { get; set; }
	public string[] Roles { get; set; }
	public DateTime ExpireyDate { get; set; }
}



2. OAuth WCF Rest Service
首先创建一个 WCF Rest Service:

定义一个基础数据模型,供Sample访问:
namespace OAuthWcfRestService
{
 public class Contact
 {
 public int Id { get; set; }
 public string Name { get; set; }
 public string Email { get; set; }
 public string Owner { get; set; }
 }

 public class DataModel
 {
 public static List Contacts;

 static DataModel()
 {
 Contacts = new List {
 new Contact(){ Id=0, Name="Felix", Email="Felix@test.com", Owner = "jane" },
 new Contact(){ Id=1, Name="Wendy", Email="Wendy@test.com", Owner = "jane"},
 new Contact(){ Id=2, Name="John", Email="John@test.com", Owner = "john"},
 new Contact(){ Id=3, Name="Philip", Email="Philip@mail.com", Owner = "john"}
 };
 }
 }
}
Contacts 中的数据只有属于 Owner 的“用户”才可以访问,因此 OAuthService 中实现如下:
namespace OAuthWcfRestService
{
 [ServiceContract]
 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
 public class OAuthService
 {
 [WebGet(UriTemplate = "Contacts")]
 public List Contacts()
 {
 var name = Thread.CurrentPrincipal.Identity.Name;
 return DataModel.Contacts.Where(c => c.Owner == name).ToList();
 } 
 }
}
上面的 name 从 Thread.CurrentPrincipal.Identity.Name 而来,即访问当前服务的客户端ID。这个ID是由OAuth服务的拦截器(Interceptor)实现由 AccessToken(String) 转换成服务端用户模型。

在 web.config 中,利用 WCF 对 ASP.NET 的兼容机制,使用 Form 认证:定义了两个用户:john 和 jane


 
 
 
 
 
 
 
 
 
 
 
 

并修改 Global.asax 的 WebServiceHostFactory,改为 OAuthWebServiceHostFactory
public class Global : HttpApplication
{
 void Application_Start(object sender, EventArgs e)
 {
 RegisterRoutes();
 }

 private void RegisterRoutes()
 {
 var oauthWebServiceHostFactory = new OAuthChannel.OAuthWebServiceHostFactory 
 { 
 AccessTokenRepository = OAuthServicesLocator.AccessTokenRepository,
 OAuthProvider = OAuthServicesLocator.Provider 
 };
 RouteTable.Routes.Add(new ServiceRoute("OAuthService", oauthWebServiceHostFactory, typeof(OAuthService)));
 }
}

作为一个基本的OAuth授权服务,我们还需要提供:
1. 获取 RequestToken 的服务
2. 获取 AccessToken 的服务
RequestToken.ashx :返回 RequestToken

using System;
using System.Web.UI;
using DevDefined.OAuth.Framework;
using DevDefined.OAuth.Provider;

namespace OAuthWcfRestService
{
 public partial class RequestToken : System.Web.IHttpHandler
 {
 public bool IsReusable
 {
 get { return true; }
 }

 public void ProcessRequest(System.Web.HttpContext context)
 {
 IOAuthContext oauthContext = new OAuthContextBuilder().FromHttpRequest(context.Request);
 IOAuthProvider provider = OAuthManager.Provider;
 IToken token = provider.GrantRequestToken(oauthContext);
 context.Response.Write(token);
 context.Response.End();
 }
 }
}
AccessToken.ashx :交换 RequestToken 返回 AccessToken

using System;
using System.Web.UI;
using DevDefined.OAuth.Framework;
using DevDefined.OAuth.Provider;

namespace OAuthWcfRestService
{
 public partial class AccessToken : System.Web.IHttpHandler
 {
 public bool IsReusable
 {
 get { return true; }
 }

 public void ProcessRequest(System.Web.HttpContext context)
 {
 IOAuthContext oauthContext = new OAuthContextBuilder().FromHttpRequest(context.Request);
 IOAuthProvider provider = OAuthManager.Provider;
 IToken accessToken = provider.ExchangeRequestTokenForAccessToken(oauthContext);
 context.Response.Write(accessToken);
 context.Response.End();
 }
 }
}
当然我们还需要提供用户登录和授权的页面:Login.aspx 和 UserAuthorize.aspx Form登录就不累述了, UserAuthorize.aspx 中实现授权的方法如下:
private void ApproveRequestForAccess(string tokenString)
{ 
 OAuthChannel.Models.RequestToken requestToken = RequestTokenRepository.GetToken(tokenString);
 var accessToken = new OAuthChannel.Models.AccessToken
 {
 ConsumerKey = requestToken.ConsumerKey,
 Realm = requestToken.Realm,
 Token = Guid.NewGuid().ToString(),
 TokenSecret = Guid.NewGuid().ToString(),
 UserName = HttpContext.Current.User.Identity.Name,
 ExpireyDate = DateTime.Now.AddMinutes(1),
 Roles = new string[] { }
 };
 AccessTokenRepository.SaveToken(accessToken);
 requestToken.AccessToken = accessToken;
 RequestTokenRepository.SaveToken(requestToken);
}

3. 应用

Default.aspx 发起请求获取RequestToken,授权成功后回调 Callback.ashx

namespace OAuthConsumerSample
{
 public partial class _Default : Page
 {
 protected void oauthRequest_Click(object sender, EventArgs e)
 {
	 OAuthSession session = OAuthSessionFactory.CreateSession();
 IToken requestToken = session.GetRequestToken();
 if (string.IsNullOrEmpty(requestToken.Token))
 {
 throw new Exception("The request token was null or empty");
 }
 Session[requestToken.Token] = requestToken;
 string callBackUrl = "http://localhost:" + HttpContext.Current.Request.Url.Port + "/Callback.ashx";
 string authorizationUrl = session.GetUserAuthorizationUrlForToken(requestToken, callBackUrl);
 Response.Redirect(authorizationUrl, true);
 }
 }
}
Callback.ashx
namespace OAuthConsumerSample
{
 public partial class Callback : System.Web.IHttpHandler, System.Web.SessionState.IRequiresSessionState
 {
 public void ProcessRequest(System.Web.HttpContext context)
 {
 var session = OAuthSessionFactory.CreateSession();
 string requestTokenString = context.Request["oauth_token"];
 var requestToken = (IToken)context.Session[requestTokenString];
 IToken accessToken = session.ExchangeRequestTokenForAccessToken(requestToken);
 context.Session[requestTokenString] = null;
 context.Session[accessToken.Token] = accessToken;
 context.Response.Redirect("ViewData.ashx?oauth_token=" + accessToken.Token);
 }

 public bool IsReusable
 {
 get { return true; }
 }
 }
}

显示全文