这几天一直在研究DotNetOpenAuth,源码处处是坑啊!写此文只为大家更顺利掌握DotNetOpenAuth使用方法,尽量少走弯路。
说明一下:我的环境是Win7 64 VS2015 update2
废话少说,踏坑开始:
新建项目:
右键引用:
搜索 dotnetopenauth,勾选包括预发行版:
提示安装一大堆东西:
可以看到Web.config添加了很多东西,稍后再讲这个
diff --git a/MvcOAuthServer/MvcAuth2Client/Web.config b/MvcOAuthServer/MvcAuth2Client/Web.config index 5da5b22..ef7ea07 --- a/MvcOAuthServer/MvcAuth2Client/Web.config +++ b/MvcOAuthServer/MvcAuth2Client/Web.config @@ -, +, @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=301880 @@ -, +, @@ <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> - </configSections> + <sectionGroup name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection, DotNetOpenAuth.Core"> + <section name="messaging" type="DotNetOpenAuth.Configuration.MessagingElement, DotNetOpenAuth.Core" requirePermission="false" allowLocation="true" /> + <section name="reporting" type="DotNetOpenAuth.Configuration.ReportingElement, DotNetOpenAuth.Core" requirePermission="false" allowLocation="true" /> + <section name="oauth" type="DotNetOpenAuth.Configuration.OAuthElement, DotNetOpenAuth.OAuth" requirePermission="false" allowLocation="true" /><section name="openid" type="DotNetOpenAuth.Configuration.OpenIdElement, DotNetOpenAuth.OpenId" requirePermission="false" allowLocation="true" /></sectionGroup></configSections> <connectionStrings> - <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-MvcAuth2Client-20160504102822.mdf;Initial Catalog=aspnet-MvcAuth2Client-20160504102822;Integrated Security=True" - providerName="System.Data.SqlClient" /> + <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-MvcAuth2Client-20160504102822.mdf;Initial Catalog=aspnet-MvcAuth2Client-20160504102822;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings> <appSettings> <add key="webpages:Version" value="3.0.0.0" /> @@ -, +, @@ </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /> - <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" /> + <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> </dependentAssembly> </assemblyBinding> - </runtime> + <!-- This prevents the Windows Event Log , and references relink + to MVC so libraries such will work with it. + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /> + <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> + </dependentAssembly> + </assemblyBinding> + --></runtime> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> @@ -, +, @@ <compiler language=" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" /> </compilers> </system.codedom> -</configuration> \ No newline at end of file +<system.net> + <defaultProxy enabled="true" /> + <settings> + <!-- This setting causes .NET to check certificate revocation lists (CRL) + before trusting HTTPS certificates. But this setting tends to not + be allowed in shared hosting environments. --> + <!--<servicePointManager checkCertificateRevocationList="true"/>--> + </settings> + </system.net><dotNetOpenAuth> + <messaging> + <untrustedWebRequest> + <whitelistHosts> + <!-- Uncomment to enable communication with localhost (should generally not activate in production!) --> + <!--<add name="localhost" />--> + </whitelistHosts> + </untrustedWebRequest> + </messaging> + <!-- Allow DotNetOpenAuth to publish usage statistics to library authors to improve the library. --> + <reporting enabled="true" /> + <!-- This is an optional configuration section where aspects of dotnetopenauth can be customized. --><!-- For a complete set of configuration options see http://www.dotnetopenauth.net/developers/code-snippets/configuration-options/ --><openid> + <provider> + </provider><relyingParty> + <security requireSsl="false"> + <!-- Uncomment the trustedProviders tag if your relying party should only accept positive assertions from a closed set of OpenID Providers. --> + <!--<trustedProviders rejectAssertionsFromUntrustedProviders="true"> + <add endpoint="https://www.google.com/accounts/o8/ud" /> + </trustedProviders>--> + </security> + <behaviors> + <!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible + with OPs that use Attribute Exchange (in various formats). --> + <add type="DotNetOpenAuth.OpenId.RelyingParty.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth.OpenId.RelyingParty" /> + </behaviors> + </relyingParty></openid></dotNetOpenAuth><uri> + <!-- The uri section is necessary to turn on .NET 3.5 support for IDN (international domain names), + which is necessary for OpenID urls with unicode characters in the domain/host name. + It escaping mode, which OpenID and OAuth require. --> + <idn enabled="All" /> + <iriParsing enabled="true" /> + </uri></configuration> \ No newline at end of file
其实真正用到的只有这三个:
可以把其它的删掉。
添加一个空控制器:
修改AuthorizeController的内容:
public class AuthorizeController : Controller { private static readonly WebServerClient Client; private static AuthorizationServerDescription authServerDescription = new AuthorizationServerDescription { TokenEndpoint = new Uri("http://localhost:50172/OAuth/Token"), AuthorizationEndpoint = new Uri("http://localhost:50172/OAuth/Authorize"), }; static AuthorizeController() { Client = new WebServerClient(authServerDescription, "sampleconsumer", "samplesecret"); } public async Task<ActionResult> Index() { IAuthorizationState authorization = await Client.ProcessUserAuthorizationAsync(Request, Response.ClientDisconnectedToken); if (authorization == null) { List<string> scopes = new List<string>(); scopes.Add("http://tempuri.org/IDataApi/GetName"); var request = await Client.PrepareRequestUserAuthorizationAsync(scopes, cancellationToken: Response.ClientDisconnectedToken); await request.SendAsync(); return Content("done"); } else { TimeSpan timeLeft = authorization.AccessTokenExpirationUtc.Value - DateTime.UtcNow; string token = authorization.AccessToken; )); return Content(result); } } }
运行吧http://localhost:18180/Authorize
报错了吧:
是不是想哭,这个问题我在网上找了很久,还是在http://*.com/ 上找到了答案
凶手就是System.Net.Http.Formatting
这个是Microsoft.AspNet.WebApi.Client 包里的,DotNetOpenAuth所依赖的版本是4.0.0,所以要替换下,打开Nuget包管理器,搜索Microsoft.AspNet.WebApi.Client,
在右侧版本下拉框中拉到最下面,选4.0.20505
结果又报错了:
这里需要在选项中依赖项行为从最低改成忽略依赖项:
再点更新就出现了熟悉的窗口了:
可以看到Web.config中的变化:
<dependentAssembly> <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" /> </dependentAssembly>
确认下版本:
再次运行,又报错了
是不是又要哭了,没关系,接着来。
打开:https://github.com/DotNetOpenAuth
先别着急下,看看这个:
看到这个标志就别下载了,下载下来后生成是通不过的,怎么样,是不是又想哭了,官方给的样例都是跑不起来的
那看另外一个源码吧:
https://github.com/DotNetOpenAuth/DotNetOpenAuth
还好这个是编译通过的,下到本地,解压,打开,重新生成,这里会自动加载程序包
如果你的VS没有开启自动下载程序包的话需要手动开启:
提示重新生成成功,可是在输出窗口又出了一堆错:
看到这里是不是要疯了。。。
很多人在这个时候就崩溃了,我也是,可是我在另外一台电脑上输出窗口是关闭了的,我没看到这个提示,于是我把OAuthAuthorizationServer设置为启动项目就跑起来了:
结果发现即便输出窗口报错也是能跑起来的,这个时候你的内心一定是极度崩溃的。。。
好了,服务端开启后再开启客户端:OAuthClient
然后你就会发现源码里的这个跑起来是没有问题的,并没有报我们自己项目里的那个错误,好吧,这个时候就要看看到底是哪里不同
还是这三个dll:
DotNetOpenAuth.Core
DotNetOpenAuth.OAuth2
DotNetOpenAuth.OAuth2.Client
在VS中看两个项目中都是5.0.0.0,完全一样,但当你在Windows中打开时就有区别了:
左边是自己项目里的,右边是源码中引用的,废话不多说,把自己项目里的这三个都替换成源码里的
再次运行:http://localhost:18180/Authorize
提示要求Https,这里可以改下配置
修改dotNetOpenAuth.messaging节点:
<!-- Relaxing SSL requirements is useful for simple samples, but NOT a good idea in production. --> <messaging relaxSslRequirements="true"> <untrustedWebRequest> <whitelistHosts> <!-- Uncomment to enable communication with localhost (should generally not activate in production!) --> <!--<add name="localhost" />--> </whitelistHosts> </untrustedWebRequest> </messaging>
但是生产环境不建议这么做,稍后会介绍搭建Https。
修改后再运行就没问题了,当然,现在还没有搭建服务端,那就先用源码的服务端来测试吧。
修改源码-->Sample-->OAuth2-->OAuthAuthorizationServer 中Web.config中的数据库连接:
<connectionStrings> <add name="DatabaseConnectionString" connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DotNetOpenAuth;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" providerName="System.Data.SqlClient"/> </connectionStrings>
运行,Home中有一个创建数据库按钮
点击即可创建数据库
此时可以看到数据库新增了5张表:
并增加了测试Client:
此时再打开我们自己的网站:http://localhost:18180/Authorize
会跳转到登录页面:
要求输入OpenID,这里的OpenID就是授权的系统,一般是一个Url,因为URL是唯一的,这里先用源码中提供的:http://localhost:4864/
这里需要说明一下,源码启动后会在IIS Express中启动多个网站:
输入OpenID后点击登录:
会跳转到OpenID网站的登录页面,如果是Google的话就会跳转到Google的登录页面:
这里使用下面的用户名和密码登录即可:
登录后会询问用户是否可以授权:
授权后我们就拿到access token 了,之后就可以使用token访问其它资源了。
再看一下数据库的变化:
到这里差不多就可以了。
顺便看看怎么使用Google和Facebook的服务:
需要引用DotNetOpenAuth.ApplicationBlock,这个dll在源码里可以找到。
添加GoogleController:
写入代码:
public class GoogleController : Controller { private static readonly GoogleClient googleClient = new GoogleClient { ClientIdentifier = ConfigurationManager.AppSettings["googleClientID"], ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(ConfigurationManager.AppSettings["googleClientSecret"]), }; public async Task<ActionResult> Index() { IAuthorizationState authorization = await googleClient.ProcessUserAuthorizationAsync(Request, Response.ClientDisconnectedToken); if (authorization == null) { var request = await googleClient.PrepareRequestUserAuthorizationAsync(scopes: new[] { GoogleClient.Scopes.UserInfo.Profile, GoogleClient.Scopes.UserInfo.Email }, cancellationToken: Response.ClientDisconnectedToken); await request.SendAsync(); Response.End(); return Content("Done"); } else { IOAuth2Graph oauth2Graph = await googleClient.GetGraphAsync(authorization, cancellationToken: Response.ClientDisconnectedToken); var name = HttpUtility.HtmlEncode(oauth2Graph.Email); return Content("成功授权! Email: " + name); } } }
在web.config中加入配置:
打开https://console.developers.google.com/apis
设置回调地址:
因为Google要求是Https的,所以我们的服务器也要配置Https:
打开项目属性,启用SSL
启动网站,导航到:https://localhost:44337/google
页面跳转到Google进行登录:
登录成功询问授权:
然后就可以调用Google的服务了,比如这里可以拿到Email。
使用Facebook登录的原理是一样的:
public class FacebookController : Controller { private static readonly FacebookClient facebookClient = new FacebookClient { ClientIdentifier = ConfigurationManager.AppSettings["facebookAppID"], ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(ConfigurationManager.AppSettings["facebookAppSecret"]), }; public async Task<ActionResult> Index() { IAuthorizationState authorization = await facebookClient.ProcessUserAuthorizationAsync(Request, Response.ClientDisconnectedToken); if (authorization == null) { var request = await facebookClient.PrepareRequestUserAuthorizationAsync(scopes: new[] { FacebookClient.Scopes.Email, FacebookClient.Scopes.UserBirthday, //FacebookClient.Scopes.ReadFriendlists, FacebookClient.Scopes.ReadInsights, FacebookClient.Scopes.UserAboutMe, //FacebookClient.Scopes.UserActivities },cancellationToken: Response.ClientDisconnectedToken); await request.SendAsync(); Response.End(); return Content("Done"); } else { IOAuth2Graph oauth2Graph = await facebookClient.GetGraphAsync(authorization, cancellationToken: Response.ClientDisconnectedToken); //// IOAuth2Graph oauth2Graph = facebookClient.GetGraph(authorization, new[] { FacebookGraph.Fields.Defaults, FacebookGraph.Fields.Email, FacebookGraph.Fields.Picture, FacebookGraph.Fields.Birthday }); var name = HttpUtility.HtmlEncode(oauth2Graph.Name); return Content(name); } } }
当然,可以看到上面的代码中注释了两行,原因如下:
这就是使用DotNetOpenAuth的Client进行认证,当然你也可以自己实现,先到授权服务器拿到授权码,然后带着这个授权码去获取token,拿到token后访问接口服务
只不过实现个过程比较繁琐,而OAUTH2是个标准,所以直接使用封装好的Client也没有什么问题。
下一篇会介绍使用DotNetOpenAuth搭建自己的授权服务器。