OAuth 学习(2) 自定义OAuth服务端(WCF REST数据访问控制)
上篇了解了如何调用 OAuth 授权来获取数据,本篇介绍如何开放OAuth授权,并控制服务端数据访问。[源码下载] 先看一下图: 这两天事太多,文章整理的断断续续 OK,步入正题,这里还是要借力:DevDefined.OAuth 框架。它提供了客户端访问,服务端管理Token的基
上篇了解了如何调用 OAuth 授权来获取数据,本篇介绍如何开放OAuth授权,并控制服务端数据访问。[源码下载]
先看一下图:
这两天事太多,文章整理的断断续续
OK,步入正题,这里还是要借力: DevDefined.OAuth 框架。它提供了客户端访问,服务端管理Token的基础功能。
定义了服务端用户模型,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 ITokenRepositoryAccessTokenRepository { 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 ListContacts 中的数据只有属于 Owner 的“用户”才可以访问,因此 OAuthService 中实现如下: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"} }; } } } namespace OAuthWcfRestService { [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class OAuthService { [WebGet(UriTemplate = "Contacts")] public List上面的 name 从 Thread.CurrentPrincipal.Identity.Name 而来,即访问当前服务的客户端ID。这个ID是由OAuth服务的拦截器(Interceptor)实现由 AccessToken(String) 转换成服务端用户模型。Contacts() { var name = Thread.CurrentPrincipal.Identity.Name; return DataModel.Contacts.Where(c => c.Owner == name).ToList(); } } }
在 web.config 中,利用 WCF 对 ASP.NET 的兼容机制,使用 Form 认证:定义了两个用户:john 和 jane
并修改 Global.asax 的 WebServiceHostFactory,改为 OAuthWebServiceHostFactorypublic 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 :返回 RequestTokenusing 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 返回 AccessTokenusing 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.ashxnamespace 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; } } } }
上一篇: PHP设计模式之观察者模式实例_PHP
下一篇: 如何解剖一个网站?