使用Owin中间件搭建OAuth2.0认证授权服务器

时间:2022-03-22 23:31:40

标签:

  前言

这里主要总结下本人最近半个月关于搭建OAuth2.0服务器工作的经验。至于为何需要OAuth2.0、为何是Owin、什么是Owin等问题,不再赘述。我假定读者是使用Asp.Net,并需要搭建OAuth2.0服务器,对于涉及的Asp.Net Identity(Claims Based Authentication)、Owin、OAuth2.0等知识点已有基本了解。若不了解,请先参考以下文章:

从何开始?

在对前言中所列的各知识点有初步了解之后,我们从何处下手呢?
这里推荐一个demo: OWIN OAuth 2.0 Authorization Server
除了demo外,还推荐准备好 katanaproject的源代码

接下来,我们主要看这个demo

Demo:Authorization Server

从OAuth2.0的rfc文档中,我们知道OAuth有多种授权模式,这里只关注授权码方式。
首先来看Authorization Server项目,里面有三大块:

Clients

Authorization Server

Resource Server

RFC6749 图示:
Clients分别对应各种授权方式的Client,这里我们只看对应授权码方式的AuthorizationCodeGrant项目;
Authorization Server即提供OAuth服务的认证授权服务器;
Resource Server即Client拿到AccessToken后携带AccessToken访问的资源服务器(这里仅简单提供一个/api/Me显示用户的Name)。
另外需要注意Constants项目,里面设置了一些关键数据,包含接口地址以及Client的Id和Secret等。

Client:AuthorizationCodeGrant

AuthorizationCodeGrant项目使用了DotNetOpenAuth.OAuth2封装的一个WebServerClient类作为和Authorization Server通信的Client。
(这里由于封装了底层的一些细节,致使不使用这个包和Authorization Server交互时可能会遇到几个坑,这个稍后再讲)
这里主要看几个关键点:

1.运行项目后,出现页面,点击【Authorize】按钮,第一次重定向用户至 Authorization Server

if (!string.IsNullOrEmpty(Request.Form.Get("submit.Authorize"))) { var userAuthorization = _webServerClient.PrepareRequestUserAuthorization(new[] { "bio", "notes" }); userAuthorization.Send(HttpContext); Response.End(); }

这里 new[] { “bio”, “notes” } 为需要申请的scopes,或者说是Resource Server的接口标识,或者说是接口权限。然后Send(HttpContext)即重定向。

2.这里暂不论重定向用户至Authorization Server后的情况,假设用户在Authorization Server上完成了授权操作,那么Authorization Server会重定向用户至Client,在这里,具体的回调地址即之前点击【Authorize】按钮的页面,而url上带有一个一次性的code参数,用于Client再次从服务器端发起请求到Authorization Server以code交换AccessToken。关键代码如下:

if (string.IsNullOrEmpty(accessToken)) { var authorizationState = _webServerClient.ProcessUserAuthorization(Request); if (authorizationState != null) { ViewBag.AccessToken = authorizationState.AccessToken; ViewBag.RefreshToken = authorizationState.RefreshToken; ViewBag.Action = Request.Path; } }

我们发现这段代码在之前点击Authorize的时候也会触发,但是那时并没有code参数(缺少code时,可能_webServerClient.ProcessUserAuthorization(Request)并不会发起请求),所以拿不到AccessToken。

3.拿到AccessToken后,剩下的就是调用api,CallApi,试一下,发现返回的就是刚才用户登陆Authorization Server所使用的用户名(Resource Server的具体细节稍后再讲)。

4.至此,Client端的代码分析完毕(RefreshToken请自行尝试,自行领会)。没有复杂的内容,按RFC6749的设计,Client所需的就只有这些步骤。对于Client部分,唯一需要再次郑重提醒的是,一定不能把AccessToken泄露出去,比如不加密直接放在浏览器cookie中。

先易后难,接着看看Resource Server

我们先把Authorization Server放一放,接着看下Resource Server。
Resource Server非常简单,App_Start中Startup.Auth配置中只有一句代码:

app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions());

然后,唯一的控制器MeController也非常简单:

[Authorize] public class MeController : ApiController { public string Get() { return this.User.Identity.Name; } }

有效代码就这些,就实现了非用户授权下无法访问,授权了就能获取用户登陆用户名。(其实webconfig里还有一项关键配置,稍后再说)

那么,Startup.Auth中的代码是什么意思呢?为什么Client访问api,而User.Identity.Name却是授权用户的登陆名而不是Client的登陆名呢?