asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code)

授权码模式定义

通过客户端的后台服务器,与“服务提供商”的认证服务器进行认证。

1、用户访问客户端,后者将前者导向认证服务器。
2、用户选择是否给予客户端授权。
3、假设用户给予授权,认证服务器首先生成一个授权码,并返回给用户,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
4、客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
5、认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
6、Client拿着access token去访问Resource资源

授权码模式的工作流程图

图 1 (网上搜到的授权码工作流程图说明)

 

之前看上边的流程图,看了不下10遍,还是搞不懂,这个图真心画的不好理解!

我们一步步来,AuthorizationServer与ResourceServer还是用之前的项目

新建项目:AuthorizationCodeGrant

 

HomeController.cs也简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public ActionResult Index()
   {
       ViewBag.AccessToken = Request.Form["AccessToken"] ?? "";
       ViewBag.RefreshToken = Request.Form["RefreshToken"] ?? "";
       ViewBag.Action = "";
       ViewBag.ResourceResponse = "";
 
       var authorizationServerUri = new Uri("http://localhost:8270/");
       var authorizationServer = new AuthorizationServerDescription
       {
           AuthorizationEndpoint = new Uri(authorizationServerUri, "OAuth/Authorize"),
           TokenEndpoint = new Uri(authorizationServerUri, "OAuth/Token")
       };
 
       // 刷新AccessToken
       var client = new WebServerClient(authorizationServer, "123456", "abcdef");
       if (string.IsNullOrEmpty(ViewBag.AccessToken))
       {
           var authorizationState = client.ProcessUserAuthorization(Request);
           if (authorizationState != null)
           {
               ViewBag.AccessToken = authorizationState.AccessToken;
               ViewBag.RefreshToken = authorizationState.RefreshToken;
               ViewBag.Action = Request.Path;
           }
       }
 
       // 授权申请
       if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestAuthorize")))
       {
           var grantRequest = client.PrepareRequestUserAuthorization(new[] { "scopes1", "scopes2" });
           grantRequest.Send(HttpContext);
           Response.End();
       }
        
       // 申请资源
       if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestResource")))
       {
           var resourceServerUri = new Uri("http://localhost:8001/");
           var resourceRequest = new HttpClient(client.CreateAuthorizingHandler(ViewBag.AccessToken));
           ViewBag.ResourceResponse = resourceRequest.GetStringAsync(new Uri(resourceServerUri, "api/Values")).Result;
       }
 
       return View();
   }

Index.cshtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Authorization Code Grant Client</title>
</head>
<body>
    <form id="form1" action="@ViewBag.Action" method="POST">
        <div>
            <input id="AccessToken" name="AccessToken" value="@ViewBag.AccessToken" type="hidden" />
            <input id="Authorize" name="btnRequestAuthorize" value="向认证服务器索要授权" type="submit" />
            <input id="Resource" name="btnRequestResource" value="访问资源(Resource)" type="submit" />
        </div>
        <div>@ViewBag.ResourceResponse</div>
    </form>
</body>
</html>

运行项目  

授权过程

点击“向认证服务索要授权”,根据HomeController.cs文件的设置,页面预计会跳转到"http://localhost:8270/OAuth/Authorize"

所以我们需要在认证服务中新增处理授权码模式的处理逻辑

在项目AuthorizationServer中新增OAuthController.cs、Authorize.cshtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class OAuthController : Controller
 {
     public ActionResult Authorize()
     {
         if (Response.StatusCode != 200)
         {
             return View("AuthorizeError");
         }
 
         var authentication = HttpContext.GetOwinContext().Authentication;
         var ticket = authentication.AuthenticateAsync("Application").Result;
         var identity = ticket != null ? ticket.Identity : null;
         if (identity == null)
         {
             authentication.Challenge("Application");
             return new HttpUnauthorizedResult(); //用户登录凭证失效就报401错误,并且跳转至AccountController中的Login中
         }
 
         ViewBag.IdentityName = identity.Name;
         ViewBag.Scopes = (Request.QueryString.Get("scope") ?? "").Split(' ');
 
         if (Request.HttpMethod == "POST")
         {          // 点击btnGrant就确认授权,返回token等信息
             if (!string.IsNullOrEmpty(Request.Form.Get("btnGrant")))
             {
                 identity = new ClaimsIdentity(identity.Claims, "Bearer", identity.NameClaimType, identity.RoleClaimType);
                 foreach (var scope in ViewBag.Scopes)
                 {
                     identity.AddClaim(new Claim("urn:oauth:scope", scope));
                 }
                 authentication.SignIn(identity);
             }
 
             if (!string.IsNullOrEmpty(Request.Form.Get("btnOtherLogin")))
             {
                 authentication.SignOut("Application");
                 authentication.Challenge("Application");
                 return new HttpUnauthorizedResult();
             }
         }
 
         return View();
     }
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Authorize</title>
</head>
<body>
    <h1>认证页面</h1>
    <form method="POST">
        <p>登录用户:@ViewBag.IdentityName</p>
        <p>第三方应用需要你给他开放以下权限</p>
        <ul>
            @foreach (var scope in ViewBag.Scopes)
            {
                <li>@scope</li>
            }
        </ul>
        <p>
            <input type="submit" name="btnGrant" value="确认授权" />
            <input type="submit" name="btnOtherLogin" value="以不同用户登录" />
        </p>
    </form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class AccountController : Controller
 {
     public ActionResult Login()
     {
         var authentication = HttpContext.GetOwinContext().Authentication;
         if (Request.HttpMethod == "POST")
         {
             // 默认用户登录成功         // 生产环境需要单独整合第三方登录信息
             var username = Request.Form["username"];
 
             authentication.SignIn(
                 new AuthenticationProperties { IsPersistent = true },
                 new ClaimsIdentity(
                     new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, username) }, "Application"));
         }
 
         return View();
     }
 
     public ActionResult Logout()
     {
         return View();
     }
 }

 

运行项目,成功跳转至认证登录页面

 

点击登录,此时url地址为:

http://localhost:8270/OAuth/Authorize?client_id=123456&redirect_uri=http%3A%2F%2Flocalhost%3A4825%2F&state=IUKeWFTR1HKi4hlzKOOPgw&scope=scopes1%20scopes2&response_type=code

7.1 client_id为客户端ID,即之前我们在AuthorizationCodeGrant项目设置的clientID

7.2 redirect_uri、state为之前登录时就确定的值

7.3 scope为用户确定授权的范围

7.4 response_type=code,即指定为授权码模式

确认授权

此时url有变化:http://localhost:4825/?code=efab38fc30c741a198b20663ec60869a36c6b25ff21f4c9986bcb9c9ae8d20eb&state=tjB9jXhNiHvIr4Ko9VhEkw

注意:这一步会会默认获取Token

 

点击访问资源

 

完全能够对上;

url中的code即认证服务返回的授权码,之后Client请求Token会用这个code来交换

这个就是授权码模式的特色的地方了

 

 

 自此,整个授权码模式已经完毕了哦

 

asp.net权限认证系列

  1. asp.net权限认证:Forms认证
  2. asp.net权限认证:HTTP基本认证(http basic)
  3. asp.net权限认证:Windows认证
  4. asp.net权限认证:摘要认证(digest authentication)
  5. asp.net权限认证:OWIN实现OAuth 2.0 之客户端模式(Client Credential)
  6. asp.net权限认证:OWIN实现OAuth 2.0 之密码模式(Resource Owner Password Credential)
  7. asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code)
  8. asp.net权限认证:OWIN实现OAuth 2.0 之简化模式(Implicit)

 

posted @   ljr忒修斯之船  阅读(6103)  评论(3编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示