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

4 篇文章 0 订阅

image

前言

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
    

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

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

    Microsoft.AspNet.WebApi.Owin
    Microsoft.Owin.Host.SystemWeb
    Microsoft.AspNet.Identity.Owin
    Microsoft.Owin.Cors
    EntityFramework

  6. 在项目根目录下添加 “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());
    		}
    	}
    }
    
  7. 删除 Global.asax

    • NOTE: 设置了 Startup 类, 就不需要 Global.asax 了,可以删除,也可以留着
  8. 在项目根目录下添加验证类 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);
    		}
    	}
    }
    
  9. 修改 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)
  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在.NET Core 6中实现OAuth2.0服务端,包括JWT,与.NET Core 5的实现方式基本相同。以下是一个简单的示例: 1. 创建一个ASP.NET Core Web API项目。 2. 安装IdentityServer4和Microsoft.AspNetCore.Authentication.JwtBearer NuGet包。 3. 在Startup.cs中配置IdentityServer,并添加JwtBearer认证。示例代码如下: ```csharp public void ConfigureServices(IServiceCollection services) { // 添加IdentityServer服务 services.AddIdentityServer() .AddInMemoryClients(Config.Clients) .AddInMemoryApiResources(Config.Apis) .AddDeveloperSigningCredential(); // 添加JwtBearer认证 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "http://localhost:5000"; // IdentityServer地址 options.RequireHttpsMetadata = false; // HTTPS设置为false,方便测试 options.Audience = "api1"; // API资源名称 }); services.AddControllers(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseIdentityServer(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } ``` 4. 创建客户端和API资源,并将它们添加到IdentityServer中。示例代码如下: ```csharp public static class Config { public static IEnumerable<Client> Clients => new List<Client> { new Client { ClientId = "client1", ClientSecrets = { new Secret("secret1".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "api1" } } }; public static IEnumerable<ApiResource> Apis => new List<ApiResource> { new ApiResource("api1", "My API") }; } ``` 5. 实现授权终结点。示例代码如下: ```csharp [HttpPost] [Route("/connect/token")] public async Task<IActionResult> Token([FromBody] TokenRequest request) { if (request.GrantType == "password") { var user = _userService.ValidateCredentials(request.UserName, request.Password); if (user != null) { var accessToken = await _tokenService.CreateAccessTokenAsync(user); return Ok(new { access_token = accessToken, token_type = "Bearer", expires_in = (int)_tokenService.Options.Expiration.TotalSeconds }); } } return BadRequest(new { error = "unsupported_grant_type" }); } ``` 6. 实现API资源的保护。示例代码如下: ```csharp [Authorize] [HttpGet] [Route("test")] public IActionResult Test() { return Ok(new { message = "Hello, World!" }); } ``` 以上是一个基本的OAuth2.0服务端实现,包括JWT。你可以根据自己的需求进行调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值