最近想要整理自己代码封装成库,也十分想把自己的设计思路贴出来让大家指正,奈何时间真的不随人意。

  想要使用 OWIN 做中间件服务,该服务中包含 管线、授权 两部分。于是决定使用 webapi 、OAuth2 来做。

  在搭建途中,几乎是步步遇坎,由于对 OAuth2 内部流转的不了解,在网上到处找大牛的文献介绍,也整理不少,最后贴出。

  在捋顺出验证整个内部过程后,遇到了如何使用 js 来发送请求达到验证,以及解决了遇到的跨域问题。

  目前仅整理出了 授权码模式 ,闲言少叙,说说自己的理解吧。

1. 授权码理论,此部分摘要网上介绍较为详细的贴图

1.1 结合例子来说,当我们与某网站进行合作,需要得到他们的授权信息,在双方协商后,确立了

  1.1.1 http://127.0.0.1:10000 对方授权地址

  1.1.2 grant_type : authorization_code 授权码模式

  1.1.3 response_type : code 授权类型

  1.1.4 client_id : lightxun 客户端ID

  1.1.5 redirect_uri : http://localhost:58632 返回接收 authorization_code 的地址

  1.1.6 state : login 状态,我用来做标识当前请求状态

1.2 当我们在某网站进行登录时,会可以快捷的使用QQ、微博等账号进行授权登录。那么我们第一步点击登录方式,页面会调转到 对方授权地址,同时携带以上参数,最终获得授权码,触发【A】Authorization Request

<a href="http://127.0.0.1:10000/authorize?grant_type=authorization_code&response_type=code&client_id=lightxun&redirect_uri=http://localhost:58632/&state=login" target="_blank">authorize</a>

  1.2.1  在某网站后台授权中 首先进行验证被注册的重定向url, 此处我的做法,在其内部将传来的 client_id 与 之前协商的 client_id 进行对比,如无误,则通过验证之前协商的 redirect_uri,为了安全,防止钓鱼,该方法对应为 OpenAuthorizationServerProvider 下的 ValidateClientRedirectUri 方法。此类为继承于 OAuthAuthorizationServerProvider ,并重写其中几部重要的处理方法。

/// <summary>
/// 验证 redirect_uri, 用于验证被注册的跳转Url
/// </summary>
public override async Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
  //验证uri 为了安全,防钓鱼
  if(context.ClientId == OpenAuthorizationClients.Client.Id)
  {
    //将传来的redirectUri 与 参数验证对比, 所以该参数最好取自数据库
    context.Validated(OpenAuthorizationClients.Client.RedirectUri);
  }
}

  1.2.2 在通过了上面的方法验证后,会验证 authorization_code 请求,该方法对应为 OpenAuthorizationServerProvider 下的 ValidateAuthorizeRequest 方法

/// <summary>
/// 验证 authorization_code 的请求
/// </summary>
public override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
{
  // IsAuthorizationCodeGrantType : 如果“response_type”查询字符串参数为“code”,则为 True
  // IsImplicitGrantType : 如果“response_type”查询字符串参数为“token”,则为 True
  if (context.AuthorizeRequest.ClientId == OpenAuthorizationClients.Client.Id &&
    (context.AuthorizeRequest.IsAuthorizationCodeGrantType || context.AuthorizeRequest.IsImplicitGrantType))
  {
    // 满足以上条件, 标记为已验证
    context.Validated();
  }
  else
  {
    context.Rejected();
  }
}

  1.2.3 接着开始处理 authorization_code 请求,来生成授权码,该过程当中整理了一下逻辑,通过 state 来判断当前请求的状态, 如果是 login 则证明需要登录,登录后会将state修改为 validate 并重新发送验证请求。如果是 validate 则说明已成功登录,可以生成授权码了。该方法对应为 OpenAuthorizationServerProvider 下的 AuthorizeEndpoint 方法

/// <summary>
/// 处理登录逻辑
/// <summary>
[HttpPost]
[Route("OAuth/Login")]
public Model.ApiResult Login([FromBody]dynamic obj)
{
  ///验证用户名密码   IOwinContext _context = (OwinContext)Request.Properties["MS_OwinContext"];
  IOwinRequest _request = _context.Request;
  IOwinResponse _response = _context.Response;   string _redirectUri = HttpUtility.UrlDecode(_request.Headers["redirect_uri"]);
  string _clientId = _request.Headers["client_id"];
  string _host = _request.Host.Value;   return new Model.ApiResult
  {
    Data = $"/authorize?grant_type=authorization_code&response_type=code&client_id={_clientId}&redirect_uri={_redirectUri}&state=validate",
    Msg = "for test"
  };
、}
/// <summary>
/// 生成 authorization_code(authorization code 授权方式)、生成 access_token (implicit 授权模式)
/// </summary>
public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{   //implicit 授权方式
  if (context.AuthorizeRequest.IsImplicitGrantType)
  {
    var identity = new ClaimsIdentity("Bearer");
    context.OwinContext.Authentication.SignIn(identity);
    context.RequestCompleted();
  }
  //authorization code 授权方式
  else if (context.AuthorizeRequest.IsAuthorizationCodeGrantType)
  {
    // 通过state 来判断, 是登录还是 已登录的获取 code阶段
    switch (context.AuthorizeRequest.State)
    {
      //如果是登录状态, 则直接跳转, 进行账户验证
      case "login":
        context.Response.Redirect("http://" + context.Request.Host.Value + "/Page/OAuth/Login.html");
        context.RequestCompleted();
        break;
      case "validate":
        var redirectUri = context.Request.Query["redirect_uri"];
        var clientId = context.Request.Query["client_id"];
        var identity = new ClaimsIdentity(new GenericIdentity(clientId, OAuthDefaults.AuthenticationType));         var authorizeCodeContext = new AuthenticationTokenCreateContext(
          context.OwinContext,
          context.Options.AuthorizationCodeFormat,
          new AuthenticationTicket(
            identity,
            new AuthenticationProperties(new Dictionary<string, string>
            {
              {"client_id", clientId},
              {"redirect_uri", redirectUri}
            })
            {
              IssuedUtc = DateTimeOffset.UtcNow,
              ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan)
            }));         await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);
        context.Response.Redirect(redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token));
        context.RequestCompleted();
        break;
      default:         break;
    }
  }
}

  1.2.4 生成 authorization_code 并返回 , 该方法对应 OpenAuthorizationCodeProvider 下的 Create 方法。该类继承于 AuthenticationTokenProvider, 触发【B】Authorization Grant

/// <summary>
/// 生成 authorization_code
/// </summary>
public override void Create(AuthenticationTokenCreateContext context)
{
  context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
  _authenticationCodes[context.Token] = context.SerializeTicket();
}

1.3 在接收到授权码之后,我们携带授权码、授权类型、重定向地址,以及设置请求header中加入 Authorization 参数。去寻要 token。触发【C】Authorization Grant

  1.3.1 发送请求 js 代码如下

$.ajax({
  async: true,
  type: 'post',
  url: 'http://127.0.0.1:10000/token',
  beforeSend: function(xhr){
    xhr.setRequestHeader('Authorization', "Basic " + Base64_Encode("lightxun:shinichi"))
  },
  data: {
    grant_type: 'authorization_code',
    code: _code,  //授权码
    redirect_uri: "http://localhost:58632/"
  },
  dataType: 'json',
  contentType: 'application/json;charset=utf-8',
  success: function (data) {
    _token = data.access_token;
    _refreshToken = data.refresh_token;
  }
});

  1.3.2 后台接收请求处理,首先验证 client 身份信息(ClientId 及 ClientSecret),该方法对应 OpenAuthorizationServerProvider 下的 ValidateClientAuthentication

/// <summary>
/// 验证Client的身份(ClientId以及ClientSecret)
/// 验证 client 信息, 验证从Basic架构的请求头或Form表单提交过来的客户端凭证
/// </summary>
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
  string clientId;
  string clientSecret;
  if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
  {
    context.TryGetFormCredentials(out clientId, out clientSecret);
  }   if (clientId != OpenAuthorizationClients.Client.Id)
  {
    context.SetError("invalid_client", "client is not valid");
    return;
  }
  context.Validated();
}

  1.3.3  验证后,开始将 authorization_code 解析成 access_token,该方法对应 OpenAuthorizationCodeProvider 下的 Receive

/// <summary>
/// 由 authorization_code 解析成 access_token
/// </summary>
public override void Receive(AuthenticationTokenReceiveContext context)
{
  string value;
  if (_authenticationCodes.TryRemove(context.Token, out value))
  {
    context.DeserializeTicket(value);
  }
}

  1.3.4 验证 token,该方法对应 OpenAuthorizationServerProvider 下的 ValidateTokenRequest

/// <summary>
/// 验证 access_token 的请求
/// </summary>
public override async Task ValidateTokenRequest(OAuthValidateTokenRequestContext context)
{
  if (context.TokenRequest.IsAuthorizationCodeGrantType || context.TokenRequest.IsRefreshTokenGrantType)
  {
    context.Validated();
  }
  else
  {
    context.Rejected();
  }
}

  1.3.5 生成 token,该方法对应 OpenRefreshTokenProvider 下的 Create 。触发【D】Access Token

/// <summary>
/// 生成 refresh_token
/// </summary>
public override void Create(AuthenticationTokenCreateContext context)
{
  context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
  context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(60);   context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
  _refreshTokens[context.Token] = context.SerializeTicket();
}

1.4 最后我们携带着 token 去请求资源即可。触发【E】Access Token 和 【F】Protected Resource

$.ajax({
  async: true,
  type: 'post',
  url: 'http://127.0.0.1:10000/token',
  beforeSend: function (xhr) {
    xhr.setRequestHeader('Authorization', "Basic " + Base64_Encode("lightxun:shinichi"))
  },
  data: {
    grant_type: 'refresh_token',
    refresh_token: _refreshToken,
  },
  dataType: 'json',
  contentType: 'application/json;charset=utf-8',
  success: function (data) {
    _token = data.access_token;
    _refreshToken = data.refresh_token;
  }
})

今天整理的有点儿多,还有许多没有写到位,后续慢慢补充,也会把全额代码贴出来,包括 OAuth 部分全额配置及代码。

期间参考过的大牛博文连接如下

https://www.cnblogs.com/xishuai/p/aspnet-webapi-owin-oauth2.html

https://www.cnblogs.com/YamatAmain/p/5029466.html

https://www.code996.cn/post/2018/token-front/

https://www.cnblogs.com/xizz/archive/2015/12/18/5056195.html

https://cloud.tencent.com/developer/article/1090017

https://cloud.tencent.com/developer/article/1340117

https://cloud.tencent.com/developer/article/1157890

https://cloud.tencent.com/developer/article/1096046

http://blogread.cn/it/article/7808?f=wb_blogread

---- 以下为跨域文献

http://jcblog.net.cn/2016/07/19/webapi%E8%B7%A8%E5%9F%9F%E8%AF%B7%E6%B1%82%EF%BC%88cors%EF%BC%89%E9%85%8D%E7%BD%AE/

https://www.cnblogs.com/baiyunchen/p/5769884.html

Owin + WebApi + OAuth2 搭建授权模式(授权码模式 Part I)的更多相关文章

  1. OAuth2.0学习(1-6)授权方式3-密码模式(Resource Owner Password Credentials Grant)

    授权方式3-密码模式(Resource Owner Password Credentials Grant) 密码模式(Resource Owner Password Credentials Grant ...

  2. IdentityServer4实现OAuth2.0四种模式之授权码模式

    接上一篇:IdentityServer4实现OAuth2.0四种模式之隐藏模式 授权码模式隐藏码模式最大不同是授权码模式不直接返回token,而是先返回一个授权码,然后再根据这个授权码去请求token ...

  3. webapi框架搭建系列博客

    webapi框架搭建系列博客 webapi框架搭建-创建项目(一) webapi框架搭建-创建项目(二)-以iis为部署环境的配置 webapi框架搭建-创建项目(三)-webapi owin web ...

  4. 基于OWIN WebAPI 使用OAUTH2授权服务【授权码模式(Authorization Code)】

    之前已经简单实现了OAUTH2的授权码模式(Authorization Code),但是基于JAVA的,今天花了点时间调试了OWIN的实现,基本就把基于OWIN的OAUHT2的四种模式实现完了.官方推 ...

  5. Spring Cloud2.0之Oauth2环境搭建(授权码模式和密码授权模式)

    oauth2 server 微服务授权中心,    github源码  https://github.com/spring-cloud/spring-cloud-security 对微服务接口做一些权 ...

  6. Microsoft.Owin.Security.OAuth搭建OAuth2.0授权服务端

    Microsoft.Owin.Security.OAuth搭建OAuth2.0授权服务端 目录 前言 OAuth2.0简介 授权模式 (SimpleSSO示例) 使用Microsoft.Owin.Se ...

  7. 基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

    适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...

  8. SimpleSSO:使用Microsoft.Owin.Security.OAuth搭建OAuth2.0授权服务端

    目录 前言 OAuth2.0简介 授权模式 (SimpleSSO示例) 使用Microsoft.Owin.Security.SimpleSSO模拟OpenID认证 通过authorization co ...

  9. (转)基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

    适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...

随机推荐

  1. Android图表库hellocharts详解

    感谢大佬:https://www.cnblogs.com/huolongluo/p/5988644.html 因为项目需要搞一个折线图,按照日期显示相应的成绩,所以有了本文. 以前用过一次XCL-ch ...

  2. WebLogic11g- 集群化

    其实网上关于weblogic集群的资料非常多[大部分都是从创建新的domain开始,我这篇先介绍怎么样把原本普通的domain改造为集群环境],如果觉得不够,可以啃weblogic的官方文档.下面给几 ...

  3. shell脚本三剑客之awk

    shell脚本之awk命令 AWK 是一种处理文本文件的语言,是一个强大的文本分析工具 适合小型文本数据 1.工作原理 2.AWK格式 3.按行输入文本 4.按字段输入文本 5.通过管道符号,双引号调 ...

  4. 使用Hot Chocolate和.NET 6构建GraphQL应用(8) —— 实现Mutate添加数据

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在讨论完GraphQL中的查询需求后,这篇文章我们将演示如何实现GraphQL中的数据添加任务. 思路 在G ...

  5. 《PHP程序员面试笔试宝典》——如何准备集体面试?

    本文摘自<PHP程序员面试笔试宝典>. PHP面试技巧分享,PHP面试题,PHP宝典尽在"琉忆编程库". 集体面试也被称为群面.无领导小组面试.由于计算机发展至今,软件 ...

  6. 基于 Kintex-7 XC7K325T的半高PCIe x4双路万兆光纤收发卡

    一.板卡概述 板卡采用Xilinx公司的XC7K325T-2FFG900I芯片作为主处理器,可应用于万兆网络.高速数据采集.存储:光纤隔离网闸等领域. 二.功能和技术指标: 板卡功能 参数内容 主处理 ...

  7. Solution -「Code+#2」「洛谷 P4033」白金元首与独舞

    \(\mathcal{Description}\)   link.   给定一个 \(n\times m\) 的网格图,一些格子指定了走出该格的方向(上下左右),而有 \(k\) 格可以任意指定走出方 ...

  8. 大家好 这里是yi术家

    好久没有来打卡了的说,可能你以为我忽然一时的热度刷题,但是事情并不是这样. 我在之前的章节里写到,我觉得每天的打卡有点浪费资源和时间,所以决定一周打一次卡. 可是上周好像也忘了打卡的样子. 这样任性的 ...

  9. 带你玩转CompletableFuture异步编程

    前言 最近在忙生活的第一个OKR,这个等等后面具体聊聊,今天开始恢复每周一篇原创,感谢小伙伴的不离不弃.这篇文章也是最近在Code Review的时候,看到的大家代码,想整体推下大家异步编程的思想,由 ...

  10. Clickhouse - MergeTree原理

    Clickhouse - MergeTree原理 MergeTree引擎以及隶属于MergeTree引擎族的所有引擎是Clickhouse表引擎中最重要, 最强大的引擎. MergeTree引擎族中的 ...