DotNetOpenAuth Part 1 : Authorization 验证服务实现及关键源码解析

DotNetOpenAuth 是 .Net 环境下OAuth 开源实现框架。基于此,可以方便的实现 OAuth 验证(Authorization)服务、资源(Resource)服务。针对 DotNetOpenAuth,近期打算整理出三篇随笔:

  DotNetOpenAuth Part 1 : OAuth2 Authorization 验证服务实现及关键源码解析

  DotNetOpenAuth Part 2 : OAuth2 Resource 资源服务实现及关键源码解析

  DotNetOpenAuth Part 3 : OAuth2 Client 访问实现几关键源码解析

本篇是这一系列的 Part 1。

OAuth Authorization 服务负责为用户下发 Access Token,而后用户用这一 Token 去请求资源和逻辑访问。Client 端发送来附带 ClientId 和 ClientSecret 的 request,Authorization 端验证请求的合法性,生成 Access Token 并下发,这就是OAuth 验证服务的主要职责。

【实现层面】

基于 DotNetOpenAuth 开发 Authorization 服务的主要工作是实现 IAuthorizationServerHost 接口,而其中关键要实现的方法有两个,它们分别是 GetClient 和 CreateAccessToken。

1 IClientDescription GetClient(string clientIdentifier);

由 Client 端发送来的标示 clientIdentifier 获取该 Client 在验证服务端的详细信息,以备做 ClientSecret 比对验证。这里 ClientDescription 的来源可以是验证服务端DB,也可以是其他可提供该数据的外部服务。

1 AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage);

 基于策略,生成 AccessToken,并返回给请求验证的 Client。

方法实现示意

GetClient,

 1 public IClientDescription GetClient(string clientIdentifier)
 2 {
 3     if (string.Equals(clientIdentifier, "iSun", StringComparison.CurrentCulture))
 4     {
 5         var client = new Client()       // Just initiate a Client instance, and in production it comes from DB or other service.
 6         {
 7             Name = "iSun",
 8             ClientSecret = "1",
 9             ClientTypeValue = 1
10         };
11
12         return client;
13     }
14
15     throw new ArgumentOutOfRangeException("clientIdentifier");
16 }

CreateAccessToken,

 1 public AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage)
 2 {
 3     var accessToken = new AuthorizationServerAccessToken();
 4
 5     accessToken.Lifetime = TimeSpan.FromMinutes(2);
 6     accessToken.ResourceServerEncryptionKey = ... ...
 7     accessToken.AccessTokenSigningKey = ... ...
 8
 9     var result = new AccessTokenResult(accessToken);
10     return result;
11 }

实现了 IAuthorizationServerHost 接口下的两个关键方法,下面要做的仅仅是使用该 AuthorizationServerHost 实例初始化 AuthorizationServer 类,并调用其 HandleTokenRequestAsync 方法。

1 private readonly AuthorizationServer authorizationServer = new AuthorizationServer(new iSunAuthorizationServerHost(AuthService.Configuration));
2
3 public async Task<ActionResult> Token()
4 {
5     var response = await authorizationServer.HandleTokenRequestAsync(Request);
6     return response.AsActionResult();
7 }

【原理层面】

实现层面的介绍就是这些,是不是很简单?但仅知道这些显然不够,你始终还是不明 ... 但觉厉。DotNetOpenAuth Authorization 框架如何做到仅这两个方法就实现了验证服务呢?好在 DotNetOpenAuth 是开源项目,我们研究一下源码就会豁然开朗。先上一张 Authorization 内部调用的示意图(点击打开大图)。

如图所示,调用始自 AuthorizationServer.HandleTokenRequestAsync,后依次经步骤1 - 9,终于 AuthorizationServerHost.CreateAccessToken 调用。其中步骤7、8、9是整个 Token 下发过程中的关键方法调用。Step 7 GetClient 获取 request 端的详细信息,之后Step 8 IsValidClientSecret 验证 ClientSecret 合法性,若验证通过则Step 9 CreateAccessToken 并下发予请求的 Client 端。逻辑示意图如下(点击打开大图)。

关键步骤7、8、9源码片段如下:

调用 GetClient,并进行 ClientSecret Validation(DotNetOpenAuth.OAuth2.ChannelElements.ClientAuthenticationModule 中 TryAuthenticateClientBySecret 方法)。

 1 protected static ClientAuthenticationResult TryAuthenticateClientBySecret(IAuthorizationServerHost authorizationServerHost,
 2     string clientIdentifier, string clientSecret) {
 3     Requires.NotNull(authorizationServerHost, "authorizationServerHost");
 4
 5     if (!string.IsNullOrEmpty(clientIdentifier)) {
 6         var client = authorizationServerHost.GetClient(clientIdentifier);       // Step 7: GetClient returns IClientDescription
 7         if (client != null) {
 8             if (!string.IsNullOrEmpty(clientSecret)) {
 9                 if (client.IsValidClientSecret(clientSecret)) {                 // Step 8: Validate ClientSecret
10                     return ClientAuthenticationResult.ClientAuthenticated;
11                 } else { // invalid client secret
12                     return ClientAuthenticationResult.ClientAuthenticationRejected;
13                 }
14             }15         }16     }17 }

ClientDescription.IsValidClientSecret 方法(DotNetOpenAuth.OAuth2.ClientDescription 中 IsValidClientSecret 方法)。

1 public virtual bool IsValidClientSecret(string secret) {
2     Requires.NotNullOrEmpty(secret, "secret");
3
4     return MessagingUtilities.EqualsConstantTime(secret, this.secret);
5 }

调用 CreateAccessToken 代码片段,若 Client 验证未通过,则返回 InvalidRequest(line 23)(DotNetOpenAuth.OAuth2.AuthorizationServer 中方法 HandleTokenRequestAsync)。

 1 AccessTokenRequestBase requestMessage = await this.Channel.TryReadFromRequestAsync<AccessTokenRequestBase>(request, cancellationToken);
 2 if (requestMessage != null) {
 3     // Step 9: Call AuthorizationServerHost.CreateAccessToken to generate Token
 4     var accessTokenResult = this.AuthorizationServerServices.CreateAccessToken(requestMessage);
 5     ErrorUtilities.VerifyHost(accessTokenResult != null, "IAuthorizationServerHost.CreateAccessToken must not return null.");
 6
 7     IAccessTokenRequestInternal accessRequestInternal = requestMessage;
 8     accessRequestInternal.AccessTokenResult = accessTokenResult;
 9
10     var successResponseMessage = this.PrepareAccessTokenResponse(requestMessage, accessTokenResult.AllowRefreshToken);
11     successResponseMessage.Lifetime = accessTokenResult.AccessToken.Lifetime;
12     ......
13     responseMessage = successResponseMessage;
14 } else {
15     // Validation failed, return error with InvalidRequest
16     responseMessage = new AccessTokenFailedResponse() { Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest };
17 }

【小结】

应用 DotNetOpenAuth 框架开发 OAuth 验证服务比较容易,但如果不了解其实现原理,开发过程中不免心虚,遇到问题也不能快速解决。所以,不仅知其然,知其所以然还是很重要的。(此篇所示代码多为示意用途,若想下载Demo,参考资料2处有详细代码,我在本篇就不贴出了。需要 DotNetOpenAuth 源码和 Sample 的,可前往官网 http://www.dotnetopenauth.net/



参考资料:

1、[OAuth]基于DotNetOpenAuth实现Client Credentials Grant  http://www.cnblogs.com/dudu/p/oauth-dotnetopenauth-client-credentials-grant.html

2、DotNetOpenAuth实践之搭建验证服务器  http://www.cnblogs.com/idefav2010/p/DotNetOpenAuth.html

3、http://www.dotnetopenauth.net/

时间: 2024-10-19 18:00:29

DotNetOpenAuth Part 1 : Authorization 验证服务实现及关键源码解析的相关文章

netty服务端启动--ServerBootstrap源码解析

netty服务端启动--ServerBootstrap源码解析 前面的第一篇文章中,我以spark中的netty客户端的创建为切入点,分析了netty的客户端引导类Bootstrap的参数设置以及启动过程.显然,我们还有另一个重要的部分--服务端的初始化和启动过程没有探究,所以这一节,我们就来从源码层面详细分析一下netty的服务端引导类ServerBootstrap的启动过程. spark中netty服务端的创建 我们仍然以spark中对netty的使用为例,以此为源码分析的切入点,首先我们看

Spring-cloud &amp; Netflix 源码解析:Eureka 服务注册发现接口 ****

http://www.idouba.net/spring-cloud-source-eureka-client-api/?utm_source=tuicool&utm_medium=referral *************************** 先关注下netflix eureka server 原生提供的接口.https://github.com/Netflix/eureka/wiki/Eureka-REST-operations 这是对非java的服务使用eureka时可以使用的r

Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析

Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析 说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们应该如何进行网络请求的优化与处理呢? 到底有没有一些好的建议与方案呢? 下面这个文章将揭晓上面的问题,让你对SpringCloud微服务网络请求性能有一个全新的认识. 目录简介 01.网络请求异常分类 02.开发中注意问题 03.原始的处理方式 04.如何减少代码耦合性 05.异常统一处理步骤 06

【一起学源码-微服务】Nexflix Eureka 源码十:服务下线及实例摘除,一个client下线到底多久才会被其他实例感知?

前言 前情回顾 上一讲我们讲了 client端向server端发送心跳检查,也是默认每30钟发送一次,server端接收后会更新注册表的一个时间戳属性,然后一次心跳(续约)也就完成了. 本讲目录 这一篇有两个知识点及一个疑问,这个疑问是在工作中真真实实遇到过的. 例如我有服务A.服务B,A.B都注册在同一个注册中心,当B下线后,A多久能感知到B已经下线了呢? 不知道大家有没有这个困惑,这篇文章最后会对此问题答疑,如果能够看到文章的结尾,或许你就知道答案了,当然答案也会在结尾揭晓. 目录如下: C

【一起学源码-微服务】Nexflix Eureka 源码十三:Eureka源码解读完结撒花篇~!

前言 想说的话 [一起学源码-微服务-Netflix Eureka]专栏到这里就已经全部结束了. 实话实说,从最开始Eureka Server和Eureka Client初始化的流程还是一脸闷逼,到现在Eureka各种操作都了然于心了. 本专栏从12.17开始写,一直到今天12.30(文章在平台是延后发布的),这将近半个月的时间确实收获很多.每天都会保持一定的时间学习,只要肯下功夫,没有学不会的东西. 2020年将继续保持学习的节奏,自己定的目标是把spring cloud几个重要的组件都学一遍

ZooKeeper单机服务端的启动源码阅读

程序的入口QuorumPeerMain public static void main(String[] args) { // QuorumPeerMain main = new QuorumPeerMain(); try { // 初始化服务端,并运行服务端 // todo 跟进去看他如何处理 服务端的配置文件,以及根据服务端的配置文件做出来那些动作 main.initializeAndRun(args); 点击查看上图原文地址( zhaoyu_nb) 初始化和启动总览 跟进initializ

Shiro安全框架入门篇(登录验证实例详解与源码)

一.Shiro框架简单介绍 Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权.Shiro在JavaSE和JavaEE项目中都可以使用.它主要用来处理身份认证,授权,企业会话管理和加密等.Shiro的具体功能点如下: (1)身份认证/登录,验证用户是不是拥有相应的身份: (2)授权,即权限验证,验证某个已认证的用户是否拥有某个权限:即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色.或者细粒度的验证某个用户对某个资源是否具有某个权限: (3)会话管理,即用户登录

Eureka服务续约(Renew)源码分析

主要对Eureka的Renew(服务续约),从服务提供者发起续约请求开始分析,通过阅读源码和画时序图的方式,展示Eureka服务续约的整个生命周期.服务续约主要是把服务续约的信息更新到自身的Eureka Server中,然后再同步到其它Eureka Server中. Renew(服务续约)操作由Service Provider定期调用,类似于heartbeat.目的是隔一段时间Service Provider调用接口,告诉Eureka Server它还活着没挂,不要把它T了.通俗的说就是它们两之

linux服务之Lamp的源码安装(centos)

Linux+Apache+Mysql+Php源码安装 一.安装环境: 系统:Centos6.5x64 Apache: httpd-2.4.10.tar.gz Mysql: mysql-5.6.20-linux-glibc2.5-x86_64.tar.gz Php: php-5.6.0.tar.gz Apr: apr-1.5.1.tar.gz Apr-util: apr-util-1.5.3.tar.gz Libxml2: libxml2-2.9.1.tar.gz 默认安装路径我们统一安装到/us