ASP.NET Core3.1 Ocelot认证的实现

1.认证

当客户端通过Ocelot访问下游服务的时候,为了保护下游资源服务器会进行认证鉴权,这时候需要在Ocelot添加认证服务。添加认证服务后,随后Ocelot会基于授权密钥授权每个请求可以访问的资源。用户必须像往常一样在其Startup.cs中注册身份验证服务,但是他们为每次注册提供一个方案(身份验证提供者密钥),例如:

public void ConfigureServices(IServiceCollection services)
{
  var authenticationProviderKey = "TestKey";
  services.AddAuthentication()
    .AddJwtBearer(authenticationProviderKey, x =>
    {
    });
}

在此Ocelot认证项目示例中,TestKey是已注册此提供程序的方案,然后将其映射到网关项目Routes路由中:

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/customers",
      "DownstreamScheme": "http",
      "DownstreamHost": "localhost",
      "DownstreamPort": 9001,
      "UpstreamPathTemplate": "/customers",
      "UpstreamHttpMethod": [ "Get" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "TestKey",
        "AllowedScopes": []
      }
    }
  ]
}

Ocelot运行时,它将查看Routes.AuthenticationOptions.AuthenticationProviderKey并检查是否存在使用给定密钥注册的身份验证提供程序。如果不存在,则Ocelot将不会启动,如果存在,则Routes将在执行时使用该提供程序。如果对路由进行身份验证,Ocelot将在执行身份验证中间件时调用与之关联的任何方案。如果请求通过身份验证失败,Ocelot将返回http状态代码401。

2.JWT Tokens Bearer认证

Json Web Token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

2.1JWT令牌结构

在紧凑的形式中,JSON Web Tokens由dot(.)分隔的三个部分组成,它们是:Header头、Payload有效载荷、Signature签名。因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz(Header.Payload.Signature)。

2.1.1Header头

标头通常由两部分组成:令牌的类型,即JWT,以及正在使用的签名算法,例如HMAC SHA256或RSA。例如:

{
 "alg": "HS256",
 "typ": "JWT"
}

然后,这个JSON被编码为Base64Url,形成JWT的第一部分。

2.1.2Payload有效载荷

Payload部分也是一个JSON对象,用来存放实际需要传递的数据。JWT规定了7个官方字段,供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。例如:

{
 "sub": "1234567890",
 "name": "John Doe",
 "admin": true
}

注意,JWT默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。这个JSON对象也要使用Base64URL算法转成字符串。

2.1.3.Signature签名

Signature部分是前两部分的签名,防止数据篡改。首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用Header里面指定的签名算法(默认是HMAC SHA256),按照下面的公式产生签名:

HMACSHA256(
 base64UrlEncode(header) + "." +
 base64UrlEncode(payload),
 secret)

签名用于验证消息在此过程中未被更改,并且,在使用私钥签名的令牌的情况下,它还可以验证JWT的发件人是否是它所声称的人。把他们三个全部放在一起,输出是三个由点分隔的Base64-URL字符串,可以在HTML和HTTP环境中轻松传递,而与基于XML的标准(如SAML)相比更加紧凑。下面显示了一个JWT,它具有先前的头和有效负载编码,并使用机密签名:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoid3prNzAzIiwibmJmIjoiMTU5MjE0MzkzNyIsImV4cCI6MTU5MjE0Mzk5OCwiaXNzIjoiYXV0aC5qd3QuY2MiLCJhdWQiOiJkZW5nd3V8MjAyMC82LzE0IDIyOjEyOjE5In0
.4RiwhRy0rQkZjclOFWyTpmW7v0AMaL3aeve1L-eWIz0

其实一般发送用户名和密码获取token那是由Identity4来完成的,包括验证用户,生成JwtToken。但是项目这里是由System.IdentityModel.Tokens类库来生成JwtToken。最后返回jwt令牌token给用户。JwtToken解码可以通过https://jwt.io/中进行查看。

3.项目演示

3.1APIGateway项目

在该项目中启用身份认证来保护下游api服务,使用JwtBearer认证,将默认的身份验证方案设置为TestKey。在appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息:

{
  "Audience": {
    "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
    "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
    "Aud": "Catcher Wong"
  }
}

Startup添加身份认证代码如下:

public void ConfigureServices(IServiceCollection services)
{
  //获取appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息
  var audienceConfig = Configuration.GetSection("Audience");
  //获取安全秘钥
  var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));
  //token要验证的参数集合
  var tokenValidationParameters = new TokenValidationParameters
  {
    //必须验证安全秘钥
    ValidateIssuerSigningKey = true,
    //赋值安全秘钥
    IssuerSigningKey = signingKey,
    //必须验证签发人
    ValidateIssuer = true,
    //赋值签发人
    ValidIssuer = audienceConfig["Iss"],
    //必须验证受众
    ValidateAudience = true,
    //赋值受众
    ValidAudience = audienceConfig["Aud"],
    //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
    ValidateLifetime = true,
    //允许的服务器时间偏移量
    ClockSkew = TimeSpan.Zero,
    //是否要求Token的Claims中必须包含Expires
    RequireExpirationTime = true,
  };
  //添加服务验证,方案为TestKey
  services.AddAuthentication(o =>
  {
    o.DefaultAuthenticateScheme = "TestKey";
  })
  .AddJwtBearer("TestKey", x =>
    {
      x.RequireHttpsMetadata = false;
      //在JwtBearerOptions配置中,IssuerSigningKey(签名秘钥)、ValidIssuer(Token颁发机构)、ValidAudience(颁发给谁)三个参数是必须的。
      x.TokenValidationParameters = tokenValidationParameters;
    });
  //添加Ocelot网关服务时,包括Secret秘钥、Iss签发人、Aud受众
  services.AddOcelot(Configuration);
}
public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  //使用认证服务
  app.UseAuthentication();
  //使用Ocelot中间件
  await app.UseOcelot();
}

3.1.1Identity Server承载JWT Token

在第二小节介绍JWT Token认证时候,我们都知道一般发送用户名和密码获取Token那是由Identity4来完成的,包括验证用户,生成JWT Token。也就是说Identity Server承载了JWT Token认证功能。为了使用IdentityServer承载Token,请像往常一样在ConfigureServices中使用方案(密钥)注册IdentityServer服务。如果您不知道如何执行此操作,请查阅IdentityServer文档。

public void ConfigureServices(IServiceCollection services)
{
  var authenticationProviderKey = "TestKey";
  Action<IdentityServerAuthenticationOptions> options = o =>
    {
      o.Authority = "https://whereyouridentityserverlives.com";
      o.ApiName = "api";
      o.SupportedTokens = SupportedTokens.Both;
      o.ApiSecret = "secret";
    };
  services.AddAuthentication()
    .AddIdentityServerAuthentication(authenticationProviderKey, options);
  services.AddOcelot();
}

在Identity4中是由Authority参数指定OIDC服务地址,OIDC可以自动发现Issuer, IssuerSigningKey等配置,而o.Audience与x.TokenValidationParameters = new TokenValidationParameters { ValidAudience = "api" }是等效的。

3.2AuthServer项目

此服务主要用于客户端请求受保护的资源服务器时,认证后产生客户端需要的JWT Token,生成JWT Token关键代码如下:

[Route("api/[controller]")]
public class AuthController : Controller
{
  private IOptions<Audience> _settings;
  public AuthController(IOptions<Audience> settings)
  {
    this._settings = settings;
  }
  /// <summary>
  ///用户使用 用户名密码 来请求服务器
  ///服务器进行验证用户的信息
  ///服务器通过验证发送给用户一个token
  ///客户端存储token,并在每次请求时附送上这个token值, headers: {'Authorization': 'Bearer ' + token}
  ///服务端验证token值,并返回数据
  /// </summary>
  /// <param name="name"></param>
  /// <param name="pwd"></param>
  /// <returns></returns>
  [HttpGet]
  public IActionResult Get(string name, string pwd)
  {
    //验证登录用户名和密码
    if (name == "catcher" && pwd == "123")
    {
      var now = DateTime.UtcNow;
      //添加用户的信息,转成一组声明,还可以写入更多用户信息声明
      var claims = new Claim[]
      {
        //声明主题
        new Claim(JwtRegisteredClaimNames.Sub, name),
          //JWT ID 唯一标识符
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
          //发布时间戳 issued timestamp
        new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64)
      };
      //下面使用 Microsoft.IdentityModel.Tokens帮助库下的类来创建JwtToken

      //安全秘钥
      var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_settings.Value.Secret));

      //声明jwt验证参数
      var tokenValidationParameters = new TokenValidationParameters
      {
        //必须验证安全秘钥
        ValidateIssuerSigningKey = true,
        //赋值安全秘钥
        IssuerSigningKey = signingKey,
        //必须验证签发人
        ValidateIssuer = true,
        //赋值签发人
        ValidIssuer = _settings.Value.Iss,
        //必须验证受众
        ValidateAudience = true,
        //赋值受众
        ValidAudience = _settings.Value.Aud,
        //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
        ValidateLifetime = true,
        //允许的服务器时间偏移量
        ClockSkew = TimeSpan.Zero,
        //是否要求Token的Claims中必须包含Expires
        RequireExpirationTime = true,
      };
      var jwt = new JwtSecurityToken(
        //jwt签发人
        issuer: _settings.Value.Iss,
        //jwt受众
        audience: _settings.Value.Aud,
        //jwt一组声明
        claims: claims,
        notBefore: now,
        //jwt令牌过期时间
        expires: now.Add(TimeSpan.FromMinutes(2)),
        //签名凭证: 安全密钥、签名算法
        signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
      );
      //生成jwt令牌(json web token)
      var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
      var responseJson = new
      {
        access_token = encodedJwt,
        expires_in = (int)TimeSpan.FromMinutes(2).TotalSeconds
      };
      return Json(responseJson);
    }
    else
    {
      return Json("");
    }
  }
}
public class Audience
{
  public string Secret { get; set; }
  public string Iss { get; set; }
  public string Aud { get; set; }
}

appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息:

{
  "Audience": {
    "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
    "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
    "Aud": "Catcher Wong"
  }
}

3.3CustomerAPIServices项目

该项目跟APIGateway项目是一样的,为了保护下游api服务,使用JwtBearer认证,将默认的身份验证方案设置为TestKey。在appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息:

{
  "Audience": {
    "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
    "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
    "Aud": "Catcher Wong"
  }
}

Startup添加身份认证代码如下:

public void ConfigureServices(IServiceCollection services)
{
  //获取appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息
  var audienceConfig = Configuration.GetSection("Audience");
  //获取安全秘钥
  var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));
  //token要验证的参数集合
  var tokenValidationParameters = new TokenValidationParameters
  {
    //必须验证安全秘钥
    ValidateIssuerSigningKey = true,
    //赋值安全秘钥
    IssuerSigningKey = signingKey,
    //必须验证签发人
    ValidateIssuer = true,
    //赋值签发人
    ValidIssuer = audienceConfig["Iss"],
    //必须验证受众
    ValidateAudience = true,
    //赋值受众
    ValidAudience = audienceConfig["Aud"],
    //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
    ValidateLifetime = true,
    //允许的服务器时间偏移量
    ClockSkew = TimeSpan.Zero,
    //是否要求Token的Claims中必须包含Expires
    RequireExpirationTime = true,
  };
  //添加服务验证,方案为TestKey
  services.AddAuthentication(o =>
  {
    o.DefaultAuthenticateScheme = "TestKey";
  })
  .AddJwtBearer("TestKey", x =>
    {
      x.RequireHttpsMetadata = false;
      //在JwtBearerOptions配置中,IssuerSigningKey(签名秘钥)、ValidIssuer(Token颁发机构)、ValidAudience(颁发给谁)三个参数是必须的。
      x.TokenValidationParameters = tokenValidationParameters;
    });

  services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
  //使用认证服务
  app.UseAuthentication();
  app.UseMvc();
}

在CustomersController下添加一个需要认证方法,一个不需要认证方法:

[Route("api/[controller]")]
public class CustomersController : Controller
{
  //添加认证属性
  [Authorize]
  [HttpGet]
  public IEnumerable<string> Get()
  {
    return new string[] { "Catcher Wong", "James Li" };
  }
  [HttpGet("{id}")]
  public string Get(int id)
  {
    return $"Catcher Wong - {id}";
  }
}

3.4ClientApp项目

该项目是用来模拟客户端访问资源服务器整个认证流程测试项目,在Program主程序可以看到如下代码:

class Program
{
  static void Main(string[] args)
  {
    HttpClient client = new HttpClient();

    client.DefaultRequestHeaders.Clear();
    client.BaseAddress = new Uri("http://localhost:9000");

    // 1. without access_token will not access the service
    //  and return 401 .
    var resWithoutToken = client.GetAsync("/customers").Result;

    Console.WriteLine($"Sending Request to /customers , without token.");
    Console.WriteLine($"Result : {resWithoutToken.StatusCode}");

    //2. with access_token will access the service
    //  and return result.
    client.DefaultRequestHeaders.Clear();
    Console.WriteLine("\nBegin Auth....");
    var jwt = GetJwt();
    Console.WriteLine("End Auth....");
    Console.WriteLine($"\nToken={jwt}");

    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}");
    var resWithToken = client.GetAsync("/customers").Result;

    Console.WriteLine($"\nSend Request to /customers , with token.");
    Console.WriteLine($"Result : {resWithToken.StatusCode}");
    Console.WriteLine(resWithToken.Content.ReadAsStringAsync().Result);

    //3. visit no auth service
    Console.WriteLine("\nNo Auth Service Here ");
    client.DefaultRequestHeaders.Clear();
    var res = client.GetAsync("/customers/1").Result;

    Console.WriteLine($"Send Request to /customers/1");
    Console.WriteLine($"Result : {res.StatusCode}");
    Console.WriteLine(res.Content.ReadAsStringAsync().Result);

    Console.Read();
  }
  private static string GetJwt()
  {
    HttpClient client = new HttpClient();

    client.BaseAddress = new Uri( "http://localhost:9000");
    client.DefaultRequestHeaders.Clear();

    var res2 = client.GetAsync("/api/auth?name=catcher&pwd=123").Result;

    dynamic jwt = JsonConvert.DeserializeObject(res2.Content.ReadAsStringAsync().Result);

    return jwt.access_token;
  }
}

运行项目看看测试结果:

结合代码,我们能看到当客户端通过Ocelot网关访问下游服务http://localhost:9000/api/Customers/Get方法时候,因为该方法是需要通过认证才返回处理结果的,所以会进行JWT Token认证,如果发现没有Token,Ocelot则返回http状态代码401拒绝访问。如果我们通过GetJwt方法在AuthServer服务上登录认证获取到授权Token,然后再访问该资源服务器接口,立即就会返回处理结果,通过跟而未加认证属性的http://localhost:9000/api/Customers/Get/{id}方法对比,我们就知道,Ocelot认证已经成功了!

4.总结

该章节只是结合demo项目简单介绍在Ocelot中如何使用JWT Token认证。其实正式环境中,Ocelot是应该集成IdentityServer认证授权的,同样的通过重写Ocelot中间件我们还可以把configuration.json的配置信息存储到数据库或者缓存到Redis中。

参考文献:
Ocelot官网

到此这篇关于ASP.NET Core3.1 Ocelot认证的实现的文章就介绍到这了,更多相关ASP.NET Core3.1 Ocelot认证内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • ASP.NET Core3.1 Ocelot路由的实现

    1.路由 前一个章节我们已经介绍过Ocelot,相信大家也了解到,Ocelot的主要功能是接收客户端等传入的HTTP请求,并将其转发到下游服务.Ocelot当前仅以另一个http请求的形式支持此功能(将来可能是任何传输机制). Ocelot将一个请求路由到另一个请求.为了让Ocelot正常工作,您需要在配置中设置一个Route.下面我们就Ocelot基础项目构建简单介绍下路由功能. 2.Ocelot基础项目构建(APIGatewayBasicDemo) 现在我们根据GitHub贡献者开源项目来学

  • ASP.NET Core3.1 Ocelot负载均衡的实现

    1.负载均衡 Ocelot可以在每个路由的可用下游服务中实现负载均衡,这使我们更有效地选择下游服务来处理请求.负载均衡类型: LeastConnection:根据服务正在处理请求量的情况来决定哪个服务来处理新请求,即将新请求发送到具有最少现有请求的服务去处理.算法状态没有分布在Ocelot集群中. RoundRobin:遍历可用服务并发送请求.算法状态没有分布在Ocelot集群中. NoLoadBalancer:从配置或服务发现中获取第一个可用服务来处理新请求. CookieStickySess

  • ASP.NET Core3.1 Ocelot认证的实现

    1.认证 当客户端通过Ocelot访问下游服务的时候,为了保护下游资源服务器会进行认证鉴权,这时候需要在Ocelot添加认证服务.添加认证服务后,随后Ocelot会基于授权密钥授权每个请求可以访问的资源.用户必须像往常一样在其Startup.cs中注册身份验证服务,但是他们为每次注册提供一个方案(身份验证提供者密钥),例如: public void ConfigureServices(IServiceCollection services) { var authenticationProvide

  • asp.net core3.1cookie和jwt混合认证授权实现多种身份验证方案

    目录 认证授权 身份认证 授权 默认授权 选择授权 总结 开发了一个公司内部系统,使用asp.net core 3.1.在开发用户认证授权使用的是简单的cookie认证方式,然后开发好了要写几个接口给其它系统调用数据.并且只是几个简单的接口不准备再重新部署一个站点,所以就直接在MVC的项目里面加了一个API区域用来写接口.这时候因为是接口所以就不能用cookie方式进行认证,得加一个jwt认证,采用多种身份验证方案来进行认证授权. 认证授权 身份验证是确定用户身份的过程. 授权是确定用户是否有权

  • ASP.NET MVC 微信JS-SDK认证

    ASP.NET MVC微信JS-SDK认证,具体内容: 写在前面 前阵子因为有个项目需要做微信自定义分享功能,因而去研究了下微信JS-SDK相关知识. 此文做个简单的记(tu)录(cao)... 开始 所有的东西都从文档开始:微信JSSDK说明文档 项目需要用到的是分享接口不过使用微信JS-SDK之前,需要做JS接口认证. 认证如下: 步骤一:绑定域名 步骤二:引入JS文件 步骤三:通过config接口注入权限验证配置 步骤四:通过ready接口处理成功验证 步骤五:通过error接口处理失败验

  • asp.net5中的用户认证与授权(1)

    就在最近一段时间,微软又有大动作了,在IDE方面除了给我们发布了Viausl Studio 2013 社区版还发布了全新的Visual Studio 2015 Preview. asp.net5中,关于用户的认证和授权提供了非常丰富的功能,如果结合ef7的话,可以自动生成相关的数据库表,调用也很方便. 但是,要理解这么一大堆关于认证授权的类,或者想按照自己项目的特定要求对认证授权进行定制,确实很头疼.为了解决这个问题,需要从根本上理解认证和授权的机制,不过这不是个简单的事情,一些概念也比较抽象,

  • ASP.NET实现基于Forms认证的WebService应用实例

    本文实例讲述了ASP.NET实现基于Forms认证的WebService应用方法.分享给大家供大家参考.具体实现方法如下: 在安全性要求不是很高的ASP.Net程序中,基于Forms的身份验证是经常使用的一种方式,而如果需要对WebService进行身份验证,最常用的可能是基于Soap 标头的自定义身份验证方式.如果对两者做一下比较的话,显然,基于Forms的验证方式更加方便易用,能否将Forms验证方式应用到WebService中去呢? 从理论上讲,使用基于Forms的方式对WebServic

  • Asp.Net Core基于JWT认证的数据接口网关实例代码

    前言 近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对前后端分离的数据服务支持,于是想到我一直做.Net开发,问我是否对.Net Core有所了解?能不能做个简单Demo出来看看?我说,分道扬镳之后我不是调用别人的接口就是提供接口给别人调用,于是便有了以下示例代码. 示例要求能演示获取Token及如何使用该Token访问数据资源,在Demo中实现

  • 详解ASP.NET Core3.0 配置的Options模式

    上一章讲到了配置的用法及内部处理机制,对于配置,ASP.NET Core还提供了一种Options模式. 一.Options的使用 上一章有个配置的绑定的例子,可以将配置绑定到一个Theme实例中.也就是在使用对应配置的时候,需要进行一次绑定操作.而Options模式提供了更直接的方式,并且可以通过依赖注入的方式提供配置的读取.下文中称每一条Options配置为Option. 1.简单的不为Option命名的方式 依然采用这个例子,在appsettings.json中存在这样的配置: { "Th

  • ASP.NET Core 实现基本认证的示例代码

    HTTP基本认证 在HTTP中,HTTP基本认证(Basic Authentication)是一种允许网页浏览器或其他客户端程序以(用户名:口令) 请求资源的身份验证方式,不要求cookie,session identifier.login page等标记或载体. - 所有浏览器据支持HTTP基本认证方式 - 基本身证原理不保证传输凭证的安全性,仅被based64编码,并没有encrypted或者hashed,一般部署在客户端和服务端互信的网络,在公网中应用BA认证通常与https结合 http

随机推荐