音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

ASP.NET Core3.1 Ocelot认证的实现
日期:2021-09-07 22:51:27   来源:脚本之家

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认证内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    ASP编程JSP编程PHP编程.NET编程python编程