基于令牌的身份验证

基于令牌的身份验证主要区别于以前常用的常用的基于cookie的身份验证,基于cookie的身份验证在B/S架构中使用比较多,但是在Web Api中因其特殊性,基于cookie的身份验证已经不适合了,因为并不是每一个调用api的客户端都是从浏览器发起,我们面临的客户端可能是手机、平板、或者app。

使用基于Token令牌的身份验证有一些好处:

  • 服务器的可伸缩性:发送到服务器的令牌是字包含的,不依赖与共享会话存储
  • 松散耦合:前端应用程序位于特定的身份验证机制耦合,令牌是从服务器生成的,并且API构建方式可理解该令牌并进行身份验证
  • 适用于移动设备:cookie依赖于浏览器,存储于本机平台,使用token将简化流程

构建后端API

步骤一: 创建Web Api 项目

为了进行代码演示,创建一个相对比较干净的环境,我们新建一个项目演示本次功能,本文使用Visual Studio 2017和 .NTE Framework 4.7。

在Vs中选择新建项目,选择ASP.NET Web 应用程序(.NET Framework) ,命名为OauthExample或者随便你喜欢的名字,然后下一步,选择空模板。ok

步骤二:安装Owin包,并设置“StarUp”类

项目右键,管理Nuget程序包,分别安装

Microsoft.AspNet.WebApi.Owin

Microsoft.Owin.Host.SystemWeb

也可以在程序包管理器输入如下代码安装:

  1. Install-Package Microsoft.AspNet.WebApi.Owin
  2. Install-Package Microsoft.Owin.Host.SystemWeb

等待安装完成。

右键项目,移除Global.asax,右键项目,添加OWIN StartUp 类,然后修改代码如下:

  1. using System.Web.Http;
  2. using Microsoft.Owin;
  3. using Owin;
  4.  
  5. [assembly: OwinStartup(typeof(OAuthExample.Startup))]
  6.  
  7. namespace OAuthExample
  8. {
  9. public class Startup
  10. {
  11. public void Configuration(IAppBuilder app)
  12. {
  13. // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
  14. HttpConfiguration config = new HttpConfiguration();
  15. WebApiConfig.Register(config);
  16. app.UseWebApi(config);
  17. }
  18. }
  19. }

简要说明

  • assembly属性设置了启动时要触发的类
  • HttpConfiguration对象用于配置API路由等,我们将对象传递给Register方法
  • UasWebApi接收对象config,该方法将把Web Api连接到我们的OWIN服务管道

完成后编译一下,检查是否能通过,如果有问题检查一下Nuget包是否安装正确。

步骤三:添加对OAuth承载令牌生成的支持

安装Owin包,Microsoft.Owin.Security.OAuth,再次打开StartUp文件,修改代码如下(斜体):

  1. using System;
  2. using System.Web.Http;
  3. using Microsoft.Owin;
  4. using Microsoft.Owin.Security.OAuth;
  5. using Owin;
  6.  
  7. [assembly: OwinStartup(typeof(OAuthExample.Startup))]
  8.  
  9. namespace OAuthExample
  10. {
  11. public class Startup
  12. {
  13. public void Configuration(IAppBuilder app)
  14. {
  15. // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
  16.  
  17. OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
  18. {
  19. AllowInsecureHttp = true,
  20. TokenEndpointPath = new PathString("/oauth/token"),
  21. AccessTokenExpireTimeSpan = TimeSpan.FromDays(),
  22. Provider = new CustomAuthorizationServerProvider()
  23. };
  24. // Token Generation
  25. app.UseOAuthAuthorizationServer(OAuthServerOptions);
  26. app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
  27.  
  28. HttpConfiguration config = new HttpConfiguration();
  29. WebApiConfig.Register(config);
  30. app.UseWebApi(config);
  31. }
  32. }
  33. }

在这里,我们从类“OAuthAuthorizationServerOptions”创建了新实例,并设置选项如下:

  • 允许客户端使用http协议请求
  • 令牌生成路径为"/oauth/token" ,即通过路径host: port:/oauth/token 获取令牌信息
  • 设置令牌的有效时间为一天,如果用户在令牌发出24小时候使用令牌进行身份验证,请求将被拒绝,并返回状态码
  • 我们在名为“CustomAuthorizationServerProvider”的类中实现了如何用户票据的验证和发放

最后我们将此选项传递给扩展方法“ UseOAuthAuthorizationServer”,以便将身份验证中间件添加到管道中。

步骤四:实现“CustomAuthorizationServerProvider”类

在项目中添加名为“ Providers”的新文件夹,然后添加名为“ SimpleAuthorizationServerProvider”的新类,在下面粘贴代码片段:

  1. using System.Security.Claims;
  2. using System.Threading.Tasks;
  3. using Microsoft.Owin.Security.OAuth;
  4.  
  5. namespace OAuthExample.Providers
  6. {
  7. public class CustomAuthorizationServerProvider : OAuthAuthorizationServerProvider
  8. {
  9. /// <summary>
  10. /// Called to validate that the origin of the request is a registered "client_id", and that the correct credentials for that client are
  11. /// present on the request. If the web application accepts Basic authentication credentials,
  12. /// context.TryGetBasicCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request header. If the web
  13. /// application accepts "client_id" and "client_secret" as form encoded POST parameters,
  14. /// context.TryGetFormCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request body.
  15. /// If context.Validated is not called the request will not proceed further.
  16. /// </summary>
  17. /// <param name="context">The context of the event carries information in and results out.</param>
  18. public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
  19. {
  20. context.Validated();
  21. }
  22.  
  23. /// <summary>
  24. /// Called when a request to the Token endpoint arrives with a "grant_type" of "password". This occurs when the user has provided name and password
  25. /// credentials directly into the client application's user interface, and the client application is using those to acquire an "access_token" and
  26. /// optional "refresh_token". If the web application supports the
  27. /// resource owner credentials grant type it must validate the context.Username and context.Password as appropriate. To issue an
  28. /// access token the context.Validated must be called with a new ticket containing the claims about the resource owner which should be associated
  29. /// with the access token. The application should take appropriate measures to ensure that the endpoint isn’t abused by malicious callers.
  30. /// The default behavior is to reject this grant type.
  31. /// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
  32. /// </summary>
  33. /// <param name="context">The context of the event carries information in and results out.</param>
  34. public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
  35. {
  36.  
  37. context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
  38.  
  39. //这里是验证用户名和密码,可以根据项目情况自己实现
  40. if (!(context.UserName == "zhangsan" && context.Password == ""))
  41. {
  42. context.SetError("invalid_grant", "The user name or password is incorrect.");
  43. return;
  44. }
  45.  
  46. //可以随便添加
  47. var identity = new ClaimsIdentity(context.Options.AuthenticationType);
  48. identity.AddClaim(new Claim("sub", context.UserName));
  49. identity.AddClaim(new Claim("role", "user"));
  50.  
  51. context.Validated(identity);
  52.  
  53. }
  54. }
  55. }

步骤五:允许ASP.NET Web Api跨域请求

使用nuget安装程序包,Install-Package Microsoft.Owin.Cors

然后在Startup类中添加如下代码,最终代码如下:

  1. using System;
  2. using System.Web.Http;
  3. using Microsoft.Owin;
  4. using Microsoft.Owin.Security.OAuth;
  5. using OAuthExample.Providers;
  6. using Owin;
  7.  
  8. [assembly: OwinStartup(typeof(OAuthExample.Startup))]
  9.  
  10. namespace OAuthExample
  11. {
  12. public class Startup
  13. {
  14. public void Configuration(IAppBuilder app)
  15. {
  16. // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
  17.  
  18. OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
  19. {
  20. AllowInsecureHttp = true,
  21. TokenEndpointPath = new PathString("/token"),
  22. AccessTokenExpireTimeSpan = TimeSpan.FromDays(),
  23. Provider = new CustomAuthorizationServerProvider()
  24. };
  25.  
  26. // Token Generation
  27. app.UseOAuthAuthorizationServer(OAuthServerOptions);
  28. app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
  29.  
  30. HttpConfiguration config = new HttpConfiguration();
  31. WebApiConfig.Register(config);
  32. app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
  33. app.UseWebApi(config);
  34. }
  35. }
  36. }

代码测试

我们添加一个测试空的Order控制,用来测试一下上面的实现:

  1. [RoutePrefix("api/Orders")]
  2. public class OrdersController : ApiController
  3. {
  4. [Authorize]
  5. [Route("")]
  6. public IHttpActionResult Get()
  7. {
  8. return Ok(Order.CreateOrders());
  9. }
  10.  
  11. }
  12.  
  13. #region Helpers
  14.  
  15. public class Order
  16. {
  17. public int OrderID { get; set; }
  18. public string CustomerName { get; set; }
  19. public string ShipperCity { get; set; }
  20. public Boolean IsShipped { get; set; }
  21.  
  22. public static List<Order> CreateOrders()
  23. {
  24. List<Order> OrderList = new List<Order>
  25. {
  26. new Order {OrderID = , CustomerName = "Taiseer Joudeh", ShipperCity = "Amman", IsShipped = true },
  27. new Order {OrderID = , CustomerName = "Ahmad Hasan", ShipperCity = "Dubai", IsShipped = false},
  28. new Order {OrderID = ,CustomerName = "Tamer Yaser", ShipperCity = "Jeddah", IsShipped = false },
  29. new Order {OrderID = ,CustomerName = "Lina Majed", ShipperCity = "Abu Dhabi", IsShipped = false},
  30. new Order {OrderID = ,CustomerName = "Yasmeen Rami", ShipperCity = "Kuwait", IsShipped = true}
  31. };
  32.  
  33. return OrderList;
  34. }
  35. }
  36.  
  37. #endregion

下面使用PostMan进行模拟测试.

在未授权时,直接访问 http://localhost:56638/api/orders得到如下结果:

模拟授权访问,先获取令牌:

将令牌附加到Order请求,再次尝试访问:

可以看到已经能正常获取到数据,打开调试,看一下方法中的变量如下:

总结

一直觉得WebApi和MVC很多都一样的东西,在实际应用中还是有不少区别,关于OAuth、JWT等等在WebApi中使用较多,本文是参照文末连接做的一个总结,细看下原po的时间都已经是14年的文章了。马上要aspnet core 3.2都要发布了,现在却还在补以前的知识,惭愧的很!

参考链接:

Token Based Authentication using ASP.NET Web API 2, Owin, and Identity

Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin

在ASP.NET Web API 2中使用Owin基于Token令牌的身份验证的更多相关文章

  1. 在ASP.NET Web API 2中使用Owin OAuth 刷新令牌(示例代码)

    在上篇文章介绍了Web Api中使用令牌进行授权的后端实现方法,基于WebApi2和OWIN OAuth实现了获取access token,使用token访问需授权的资源信息.本文将介绍在Web Ap ...

  2. ASP.NET Web API 2 中的属性路由使用(转载)

    转载地址:ASP.NET Web API 2 中的属性路由使用

  3. 在ASP.NET Web API项目中使用Hangfire实现后台任务处理

    当前项目中有这样一个需求:由前端用户的一个操作,需要触发到不同设备的消息推送.由于推送这个具体功能,我们采用了第三方的服务.而这个服务调用有时候可能会有延时,为此,我们希望将消息推送与用户前端操作实现 ...

  4. ASP.NET Web API 2 中的特性路由

    ASP.NET MVC 5.1 开始已经支持基于特性的路由(http://attributerouting.net),ASP.NET WEB API 2 同时也支持了这一特性. 启用特性路 由只需要在 ...

  5. ASP.NET Web API 2中的错误处理

    前几天在webapi项目中遇到一个问题:Controller构造函数中抛出异常时全局过滤器捕获不到,于是网搜一把写下这篇博客作为总结. HttpResponseException 通常在WebAPI的 ...

  6. [翻译]ASP.NET Web API 2 中的全局错误处理

    目录 已存在的选项 解决方案预览 设计原则 什么时候去用 方案详情 示例 附录: 基类详情 原文链接 Global Error Handling in ASP.NET Web API 2 由于翻译水平 ...

  7. (转)【ASP.NET Web API】Authentication with OWIN

    概述 本文说明了如何使用 OWIN 来实现 ASP.NET Web API 的验证功能,以及在客户端与服务器的交互过程中,避免重复提交用户名和密码的机制. 客户端可以分为两类: JavaScript: ...

  8. ASP.NET Web API实现微信公众平台开发(一)服务器验证

    最近朋友的微信公众号准备做活动,靠固定的微信公众平台模版搞定不了,于是请我代为开发微信后台.鉴于我也是第一次尝试开发微信后台,所以也踩了不少坑,此系列博客将会描述微信公众号各项功能的实现. 先决条件 ...

  9. integration asp.net web api with autofac and owin

    There is an example project showing Web API in conjunction with OWIN self hosting https://github.com ...

随机推荐

  1. 数据挖掘--OPTICS

    OPTICS是基于DBSCAN算法的缺陷提出来的一个算法. 核心思想:为每个数据对象计算出一个顺序值(ordering).这些值代表了数据对象的基于密度的族结构,位于同一个族的数据对象具有相近的顺序值 ...

  2. hydra使用,实例介绍

    hydra 是一个网络帐号破解工具,支持多种协议.其作者是van Hauser,David Maciejak与其共同维护.hydra在所有支持GCC的平台能很好的编译,包括Linux,所有版本的BSD ...

  3. web-程序逻辑问题

    题目 查看源码 http://ctf5.shiyanbar.com/web/5/index.txt 代码如下 <html> <head> welcome to simplexu ...

  4. django学习-视图练习

    写一个真正有用的视图 每个视图必须要做的只有两件事: 返回一个包含被请求页面内容的HttpResponse对象,或抛出一个异常,比如Http404. 至于你还想干些什么,随便你. 你的视图可以从数据库 ...

  5. zz 机器学习系统或者SysML&DL笔记

    机器学习系统或者SysML&DL笔记(一)  Oldpan  2019年5月12日  0条评论  971次阅读  1人点赞 在使用过TVM.TensorRT等优秀的机器学习编译优化系统以及Py ...

  6. 论文阅读笔记六十三:DeNet: Scalable Real-time Object Detection with Directed Sparse Sampling(CVPR2017)

    论文原址:https://arxiv.org/abs/1703.10295 github:https://github.com/lachlants/denet 摘要 本文重新定义了目标检测,将其定义为 ...

  7. Linux性能优化实战学习笔记:第八讲

    一.环境准备 1.在第6节的基础上安装dstat wget http://mirror.centos.org/centos/7/os/x86_64/Packages/dstat-0.7.2-12.el ...

  8. [LeetCode] 598. Range Addition II 范围相加之二

    Given an m * n matrix M initialized with all 0's and several update operations. Operations are repre ...

  9. 阅读java编程思想之一切都是对象

    温故而知新--- 1. 用句柄操作对象 现实生活中,我们可以把遥控器当作句柄,电视机当作对象.当我们拿到句柄(遥控器)的时候,我们是可以控制对象的(电视机).比如说调节音量大小,色彩等.那么在程序里, ...

  10. 第四次实验报告:使用Packet Tracer理解RIP路由协议

    目录 1 实验目的 2 实验内容 3. 实验报告 3.1 建立网络拓扑结构 3.2 配置参数 3.3 测试网络连通性 3.4 理解RIP路由表建立和更新 4. 理解RIP消息传得慢 5. 拓展 1 实 ...