ASP.NET WebApi 如何使用 OAuth2.0 认证

简介: ASP.NET WebApi 如何使用 OAuth2.0 认证

前言

OAuth 2.0 是一种开放标准的授权框架,用于授权第三方应用程序访问受保护资源的流程。

OAuth 2.0 认证是指在这个框架下进行的身份验证和授权过程。

在 OAuth 2.0 认证中,涉及以下主要参与方:

  1. 资源所有者(Resource Owner): 拥有受保护资源的用户。
  2. 客户端(Client): 第三方应用程序,希望访问资源所有者的受保护资源。
  3. 授权服务器(Authorization Server): 负责验证资源所有者的身份并颁发访问令牌。
  4. 资源服务器(Resource Server): 存储受保护资源的服务器,用于接收和响应客户端请求。

OAuth 2.0 认证的流程通常包括以下步骤:

  1. 客户端注册: 客户端向授权服务器注册,并获得客户端标识和客户端密钥。
  2. 请求授权: 客户端向资源所有者请求授权,以获取访问受保护资源的权限。
  3. 授权许可: 资源所有者同意授权,授权服务器颁发授权码给客户端。
  4. 获取访问令牌: 客户端使用授权码向授权服务器请求访问令牌。
  5. 访问受保护资源: 客户端使用访问令牌向资源服务器请求访问受保护资源。

OAuth 2.0 认证的优势在于可以实现用户授权而无需透露密码,同时提供了更安全和灵活的授权机制,更好地保护用户数据和系统安全。

以下是一个 ASP.NET WebApi 简单使用 OAuth2.0 认证的 Step By Step 例子。

Step By Step 步骤

  1. 新建一个空 ASP.NET WebApi 项目,比如 TokenExample
  2. 在 Models 目录下新建一个 Product 实体类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TokenExample.Models
{
  public class Product
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
  }
}

3.在 Controllers 目录下新建一个 ProductsController 控制器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TokenExample.Models;
namespace TokenExample.Controllers
{
  public class ProductsController : ApiController
  {
    // 初始化数据
    Product[] products = new Product[]
    {
      new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
      new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
      new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
    };
    // 查找所有的产品
    public IEnumerable<Product> GetAllProducts()
    {
      return products;
    }
    // 根据 id 查找产品
    public Product GetProductById(int id)
    {
      var product = products.FirstOrDefault((p) => p.Id == id);
      if (product == null)
      {
        throw new HttpResponseException(HttpStatusCode.NotFound);
      }
      return product;
    }
    
    // 根据 类别 查找产品
    public IEnumerable<Product> GetProductsByCategory(string category)
    {
      return products.Where(p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
    }
  }
}

4.将网站部署到 IIS, 端口为 8080,使用 Postman 工具测试以下 api:

GET http://localhost:8080/api/Products
GET http://localhost:8080/api/Products/1
GET http://localhost:8080/api/Products?category=Groceries

5.可以看到这些 API 都是可以公开访问的,没有任何验证

6.在 WebApi 项目右键,选择 “管理 Nuget 程序包”,打开 Nuget 包管理器 GUI, 安装以下包:

Microsoft.AspNet.WebApi.Owin

Microsoft.Owin.Host.SystemWeb

Microsoft.AspNet.Identity.Owin

Microsoft.Owin.Cors

EntityFramework

7.在项目根目录下添加 “Startup” 类, 这是 Owin 的启动类(注意是项目根目录,即跟 Global.asax 同一位置)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
[assembly: OwinStartup(typeof(TokenExample.Startup))]
namespace TokenExample
{
  public class Startup
  {
    public void Configuration(IAppBuilder app)
    {
      HttpConfiguration config = new HttpConfiguration();
      ConfigureOAuth(app);
      WebApiConfig.Register(config);
      app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
      app.UseWebApi(config);
    }
    public void ConfigureOAuth(IAppBuilder app)
    {
      OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
      {
        AllowInsecureHttp = true,
        // 这里设置获取 token 有 url path
        TokenEndpointPath = new PathString("/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
        Provider = new SimpleAuthorizationServerProvider()
      };
      app.UseOAuthAuthorizationServer(OAuthServerOptions);
      app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    }
  }
}

8.删除 Global.asax

  • NOTE: 设置了 Startup 类, 就不需要 Global.asax 了,可以删除,也可以留着

9.在项目根目录下添加验证类 SimpleAuthorizationServerProvider

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
namespace TokenExample
{
  public class SimpleAuthorizationServerProvider: OAuthAuthorizationServerProvider
  {
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
      context.Validated();
    }
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
      // 设置允许跨域
      context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
      /*
       * 对用户名、密码进行数据校验,这里我们省略
      using (AuthRepository _repo = new AuthRepository())
      {
        IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
        if (user == null)
        {
          context.SetError("invalid_grant", "The user name or password is incorrect.");
          return;
        }
      }
      */
      var identity = new ClaimsIdentity(context.Options.AuthenticationType);
      identity.AddClaim(new Claim("sub", context.UserName));
      identity.AddClaim(new Claim("role", "user"));
      context.Validated(identity);
    }
  }
}

10.修改 ProductsController 类,在 Action 上增加 [Authorize] 特性,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TokenExample.Models;
namespace TokenExample.Controllers
{
  public class ProductsController : ApiController
  {
    Product[] products = new Product[]
    {
      new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
      new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
      new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
    };
    // [Authorize] 特性是启用 OAuth 的 Access Token 验证,让 CORS 起作用
    [Authorize]
    public IEnumerable<Product> GetAllProducts()
    {
      return products;
    }
    [Authorize]
    public Product GetProductById(int id)
    {
      var product = products.FirstOrDefault((p) => p.Id == id);
      if (product == null)
      {
        throw new HttpResponseException(HttpStatusCode.NotFound);
      }
      return product;
    }
    // [AllowAnonymous] 特性是允许匿名访问,即无需 Access Token 验证
    [AllowAnonymous]
    public IEnumerable<Product> GetProductsByCategory(string category)
    {
      return products.Where(p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
    }
  }
}

测试

  1. 重新在 Postman 运行以下命令:
GET http://localhost:8080/api/Products
返回:
{
  "Message": "已拒绝为此请求授权。"
}
这是预期的

2.在 Postman 运行以下命令:

POST/GET http://localhost:23477/token
参数 BODY x-www-form-urlencoded 格式:
grant_type=password
username=admin 
password=123456
返回:
{
  "access_token": "ESWxgOCWDDPBRg37cX2RIAb8h--AYgz55rheYumSEU9YVjikYowyih1EdkVUg5vEeuLEeuhZPFJFGe33N3yvieYCzVQ2r0FKYBj0vydKnHAZ7CpLry4DaOhZ8JKIxa159QiBZubA_YgtFliUggSefiosrXW-FaUUO-m5th4YwInw2_5aGPL73uB5FYE0LcLN51U8ZlqoeLDChO3MdTigTc90rVUNiiZ3UBHn-HWvSnI",
  "token_type": "bearer",
  "expires_in": 86399
}

3.在以下 api 的 Headers 加上:

GET http://localhost:8080/api/Products
Headers
Key: Authorization
Value: bearer ESWxgOCWDDPBRg37cX2RIAb8h--AYgz55rheYumSEU9YVjikYowyih1EdkVUg5vEeuLEeuhZPFJFGe33N3yvieYCzVQ2r0FKYBj0vydKnHAZ7CpLry4DaOhZ8JKIxa159QiBZubA_YgtFliUggSefiosrXW-FaUUO-m5th4YwInw2_5aGPL73uB5FYE0LcLN51U8ZlqoeLDChO3MdTigTc90rVUNiiZ3UBHn-HWvSnI

4.重新运行,即可正常访问,至此就完成了简单的 ASP.NET WebApi 使用 OAuth2.0 认证

总结

  1. OAuth2.0 有 Client 和 Scope 的概念,JWT 没有,如果只是拿来用于颁布 Token 的话,二者没区别,如本例
  2. OAuth2.0 和 JWT 在使用 Token 进行身份验证时有相似之处,但实际上它们是完全不同的两种东西,OAuth2.0 是授权认证的框架,JWT 则是认证验证的方式方法(轻量级概念)
  3. OAuth2.0 更多用在使用第三方账号登录的情况(比如使用 weibo,qq,github 等登录某个 app)


相关文章
|
15天前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
2月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
38 0
|
2月前
|
存储 开发框架 .NET
ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间
ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间
|
2月前
|
开发框架 .NET API
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
|
Web App开发 .NET
ASP“.NET研究”.NET中的认证与授权
  用户认证   .net提供了3种用户认证的方式,分别是Windows,Forms,Passport。这几种形式的定义可以在网站根目录下Web.config中的authentication节点中看见。
1204 0
|
1月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
30 7
|
1月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
41 0
|
2月前
|
开发框架 前端开发 安全
ASP.NET MVC 如何使用 Form Authentication?
ASP.NET MVC 如何使用 Form Authentication?
|
2月前
|
开发框架 .NET
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
96 0
|
5月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
163 0