• ASP.NET Web API 2基于令牌的身份验证


    基于令牌的认证

        我们知道WEB网站的身份验证一般通过session或者cookie完成的,登录成功后客户端发送的任何请求都带上cookie,服务端根据客户端发送来的cookie来识别用户。

        WEB API使用这样的方法不是很适合,于是就有了基于令牌的认证,使用令牌认证有几个好处:可扩展性、松散耦合、移动终端调用比较简单等等,别人都用上了,你还有理由不用吗?

        下面我们花个20分钟的时间来实现一个简单的WEB API token认证:

    Step 1: 新建一个空的WEB API项目,项目名称就设置为WebApi

    Step 2: 在Models目录下新建一个 Product 类 :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace WebApi.Models
    {
        public class Product
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Category { get; set; }
            public decimal Price { get; set; }
        }
    }

    Step 3:在Controllers目录下新建一个 ProductsController 类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Web.Http;
    
    using WebApi.Models;
    
    namespace WebApi.Controllers
    {
        [RoutePrefix("api/Products")]
        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;
            }
    
            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));
            }
        }
    }

    F5运行后就可以使用这个简单的WebApi了,测试api可以使用Postman工具:

        获取所有数据  http://localhost:1234/api/products

        获取内码为1的数据  http://localhost:1234/api/products/1

        查询category=的数据  http://localhost:1234/api/products?category=Groceries

        可以看到这个产品的API是公开访问的,没有任何验证,这样不是很安全,下一步我将加上token验证。

    Step 4:安装所需的NuGet包:

    打开NuGet包管理器控制台,然后输入如下指令:

    Install-Package Microsoft.AspNet.WebApi.Owin -Version 5.1.2
    Install-Package Microsoft.Owin.Host.SystemWeb -Version 2.1.0
    Install-Package Microsoft.AspNet.Identity.Owin -Version 2.0.1
    Install-Package Microsoft.Owin.Cors -Version 2.1.0
    Install-Package EntityFramework -Version 6.0.0

    Step 5:在项目根目录下添加Owin“Startup”类

    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(WebApi.Startup))]
    namespace WebApi
    {
        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,
                    TokenEndpointPath = new PathString("/token"),
                    AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                    Provider = new SimpleAuthorizationServerProvider()
                };
                app.UseOAuthAuthorizationServer(OAuthServerOptions);
                app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
            }
        }
    }

    Step 6:删除Global.asax 

    我们已经设置了Setup类,就不需要Global了,删掉干净;

    Step 7:在项目根目录下添加验证类 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 WebApi
    {
        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);
    
            }
        }
    }

    Step 7:让CORS起作用

    在ASP.NET Web API中启用OAuth的Access Token验证非常简单,只需在相应的Controller或Action加上[Authorize]标记

    修改ProductsController类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Web.Http;
    
    using WebApi.Models;
    
    namespace WebApi.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]
            [Route("")]
            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]
            public IEnumerable<Product> GetProductsByCategory(string category)
            {
                return products.Where(p => string.Equals(p.Category, category,
                        StringComparison.OrdinalIgnoreCase));
            }
        }
    }

    现在我们再次直接GET http://localhost:23477/api/products/ 会返回401错误,请求被拒绝

    获取token, POST   http://localhost:23477/token

    参数BODY x-www-form-urlencoded 格式:

    grant_type=password

    username=admin 

    password=123456

    返回200状态,内容为:

    {
      "access_token": "eLjAu3Alm2YWjJKXmX_fLY07P6vbIzxasFECkDap3KIE0Ydp7IGhTgrzWLtPdgrK46rfAB-OmJSG5C8Bh-PkfG3XrGS0uDea2KBXyoWSR11evTGJiVIyXny3Ih2DkH04qH2T_Ar4kIjcAngPtUnsEVex26tV4QHIrjCq5SlkOdfdAa9Pnl98QVwYH47yO-zlc55bwMgpR2J4fQLyNzWVHNZpH3DbOcHQ3Yenemr6XhM",
      "token_type": "bearer",
      "expires_in": 86399
    }

    只要在http请求头中加上Authorization:bearer Token就可以成功访问API就成功了:

    GET   http://localhost:23477/api/products/

    Authorization : bearer eLjAu3Alm2YWjJKXmX_fLY07P6vbIzxasFECkDap3KIE0Ydp7IGhTgrzWLtPdgrK46rfAB-OmJSG5C8Bh-PkfG3XrGS0uDea2KBXyoWSR11evTGJiVIyXny3Ih2DkH04qH2T_Ar4kIjcAngPtUnsEVex26tV4QHIrjCq5SlkOdfdAa9Pnl98QVwYH47yO-zlc55bwMgpR2J4fQLyNzWVHNZpH3DbOcHQ3Yenemr6XhM

    这样我们就完成了简单的WEB API的token验证~

    不过这个程序有个问题,如果GetProductById也加上验证那么根据ID获取product的接口 http://localhost:23477/api/products/1 会报错

    需要修改成 http://localhost:23477/api/products?id=1 

    不知道是哪里出的问题

    本文代码: http://pan.baidu.com/s/1jGxZVKU

    PostMan工具请移步这里看介绍 http://www.cnblogs.com/wade-xu/p/4228954.html

  • 相关阅读:
    Swift3.0 函数闭包与 Block
    深入理解RunLoop
    Autorelease对象什么时候释放?
    Mysql数据库分库分表Springboot+mybatis+druid+shardingsphere
    Mysql数据库读写分离Springboot+mybatis+druid+shardingsphere
    docker-compose安装mysql主从集群
    监听ZK节点数据变化的几种方式
    c语言替换avx指令集,Dlib支持CPU指令集编译问题(SSE4.2或者AVX)
    centos7防火墙
    centOS7安装nginx及nginx配置
  • 原文地址:https://www.cnblogs.com/relax/p/4956441.html
Copyright © 2020-2023  润新知