zoukankan      html  css  js  c++  java
  • HttpClient调用受JWT保护的Api

    准备工作:

    一个Asp.Net Core Api 程序,程序的功能大概有两个:模拟验证用户登录,权限认证模块给用户颁发Jwt,用户带token来调用Api资源。

    首先简单介绍一下JWT的数据结构,JWT由头部载荷签名这三部分组成,中间以「.」分隔。

    头部以 JSON 格式表示,用于指明令牌类型和加密算法。形式如下,表示使用 JWT 格式,加密算法采用 HS256。

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

    载荷用来存储服务器需要的数据,比如用户信息,要注意的是重要的机密信息最好不要放到这里,比如密码。

    {
        "name": "zhouxieyi",
        "introduce": "a test token"
    }

    另外,JWT 还规定了 7 个字段供开发者选用。

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

    签名使用HMACSHA256算法计算得出,这个方法有两个参数,前一个参数是 (base64 编码的头部 + base64 编码的载荷)用点号相连,后一个参数是自定义的字符串密钥,密钥不要暴露在客户端。

    我们在appsettings.json中存放了我们自定义的token信息如下:

    "tokenManagement": {
        "issuer": "webapi.cn",
        "name": "zhouxieyi",
        "introduce": "a test token",
        "audience": "WebApi",
        "secret": "qwertyuiopasdfghjklzxcvbnm",
        "accessExpiration": 30,
        "refreshExpiration": 60
      }

    下一步配置StartUp.cs,将身份认证中间件加入引用,并引入NuGet:Microsoft.AspNetCore.Authentication.JwtBearer,在Configure中配置好服务。

    创建用户User,UserService来模拟用户登录,这里我们假使所有的用户都合法。用户通过校验后,利用AuthenticationService颁发token,根据这两步逻辑,我们编写接口和控制器来完成。

    代码如下:

    using System.Text;
    using authapi.Models;
    using authapi.Services;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.IdentityModel.Tokens;
    
    namespace authapi
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllers();
                services.Configure<TokenManagement>(Configuration.GetSection("tokenManagement"));
                var token = Configuration.GetSection("tokenManagement").Get<TokenManagement>();
    
                services.AddAuthentication(x =>
                {
                    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;
                    options.SaveToken = true;
    
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(token.Secret)),
                        ValidIssuer = token.Issuer,
                        ValidAudience = token.Audience,
                        ValidateIssuer = false,
                        ValidateAudience = false
                    };
                });
    
                services.AddScoped<IUserService, UserService>();
                services.AddScoped<IAuthenticationService, TokenAuthenticateService>();
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseHttpsRedirection();
    
                app.UseRouting();
    
                app.UseAuthentication();
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    }
    StartUp.cs
    public interface IAuthenticationService
    {
        bool IsAuthenticated(User user, out string token);
    }
    
    public class TokenAuthenticateService : IAuthenticationService
    {
        private readonly IUserService _userService;
        private readonly TokenManagement _tokenManagement;
    
        public TokenAuthenticateService(IUserService userService,
            IOptions<TokenManagement> tokenManagement)
        {
            _userService = userService;
            _tokenManagement = tokenManagement.Value;
        }
    
        public bool IsAuthenticated(User user, out string token)
        {
            token = string.Empty;
            if (!_userService.Validate(user))
                return false;
            var claims = new[]
            {
                new Claim(ClaimTypes.Name,user.UserName)
            };
    
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenManagement.Secret));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
            var jwtToken = new JwtSecurityToken(
                _tokenManagement.Issuer,
                _tokenManagement.Audience, 
                claims,
                expires: DateTime.Now.AddMinutes(_tokenManagement.AccessExpiration),
                signingCredentials: credentials);
    
            token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
    
            return true;
        }
    }
    AuthenticationService
    public interface IUserService
    {
        bool Validate(User user);
    }
    
    public class UserService : IUserService
    {
        public bool Validate(User user)
        {
            return true;
        }
    }
    UserService
    using authapi.Models;
    using authapi.Services;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    
    namespace authapi.Controllers
    {
        [ApiController]
        [Route("api/[controller]")]
        public class AuthenticationController : ControllerBase
        {
            private readonly IAuthenticationService _authenticationService;
    
            public AuthenticationController(IAuthenticationService authenticationService)
            {
                _authenticationService = authenticationService;
            }
    
            [AllowAnonymous]
            [HttpPost, Route("requestToken")]
            public ActionResult RequestToken([FromBody] User user)
            {
                if (!ModelState.IsValid)
                {
                    return BadRequest("Model Error");
                }
    
                if (_authenticationService.IsAuthenticated(user, out var token))
                {
                    return Ok(token);
                }
    
                return BadRequest("Invalid Request");
            }
        }
    }
    AuthenticationController
    using Microsoft.AspNetCore.Mvc;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.AspNetCore.Authorization;
    
    namespace authapi.Controllers
    {
        [ApiController]
        [Route("api/[controller]")]
        public class WeatherForecastController : ControllerBase
        {
            private static readonly string[] Summaries = {
                "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
            };
    
            [Authorize]
            [HttpGet]
            public IEnumerable<WeatherForecast> Get()
            {
                var rng = new Random();
                return Enumerable.Range(1, 5).Select(index => new WeatherForecast
                {
                    Date = DateTime.Now.AddDays(index),
                    TemperatureC = rng.Next(-20, 55),
                    Summary = Summaries[rng.Next(Summaries.Length)]
                }).ToArray();
            }
        }
    }
    WeatherForecastController
    public class TokenManagement
    {
        [JsonPropertyName("secret")]
        public string Secret { get; set; }
    
        [JsonPropertyName("issuer")]
        public string Issuer { get; set; }
    
        [JsonPropertyName("audience")]
        public string Audience { get; set; }
    
        [JsonPropertyName("accessExpiration")]
        public int AccessExpiration { get; set; }
    
        [JsonPropertyName("refreshExpiration")]
        public int RefreshExpiration { get; set; }
    
    }
    
    public class User
    {
        [Required]
        [JsonPropertyName("username")]
        public string UserName { get; set; }
    
        [Required]
        [JsonPropertyName("password")]
        public string PassWord { get; set; }
    }
    
    public class WeatherForecast
    {
        public DateTime Date { get; set; }
    
        public int TemperatureC { get; set; }
    
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    
        public string Summary { get; set; }
    }
    Models

    运行程序,使用PostMan测试:

    先利用用户获取Token

     设置Token类型,输入我们刚刚获取的Token,访问Api成功获取数据。


    HttpClient:

    创建一个Console程序,创建HttpClient,HttpClient类实例充当发送 HTTP 请求的会话。每个 HttpClient 实例都使用其自己的连接池,并从其他实例所执行的请求隔离其请求 HttpClient。

    利用.NET Json处理拓展包:using System.Net.Http.Json,可以很方便的带Json对象发送请求。具体逻辑流程是使用client带用户信息请求token,获取成功后将token绑定到Header中:Authorization : Bearer + token即可,在请求Api即可获取资源。

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Net.Http;
    using System.Net.Http.Json;
    using System.Threading.Tasks;
    
    namespace any
    {
        public class Program
        {
            static async Task Main(string[] args)
            {
                var client = new HttpClient();
                var uri = new Uri("https://localhost:5001");
                var user = new User
                {
                    UserName = "client",
                    PassWord = "123321"
                };
    
                //利用SendAsync发送请求,在Request中设置Content或Header传入。
                //var postRequest = new HttpRequestMessage(
                //    HttpMethod.Post,
                //    uri.AbsoluteUri + "api/Authentication/RequestToken")
                //{
                //    Content = JsonContent.Create(user),
                //};
                //var response = await client.SendAsync(postRequest);
    
                //使用PostAsJsonAsync传递json
                var response = await client.PostAsJsonAsync(uri.AbsoluteUri + "api/Authentication/RequestToken", user);
                var token = await response.Content.ReadAsStringAsync();
    
                Console.WriteLine($"token: {token}");
    
                //设置Header
                client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
    
                var result = await client.GetStringAsync(uri.AbsoluteUri + "api/WeatherForecast");
    
                Console.WriteLine($"string result: {result}");
    
                var jsonResponse = await client.GetAsync(uri.AbsoluteUri + "api/WeatherForecast");
    
                var jsonObjs = await jsonResponse.Content.ReadFromJsonAsync<List<WeatherForecast>>();
    
                if (jsonObjs != null)
                {
                    foreach (var weatherForecast in jsonObjs)
                    {
                        Console.WriteLine($"Date is {weatherForecast.Date}, is {weatherForecast.Summary} day");
                    }
                }
                
            }
    
        }
    }
    Program.cs

    result:

  • 相关阅读:
    237.Delete Node in a Linked List
    235.Lowest Common Ancestor of a Binary Search Tree
    234.Palindrome Linked List
    232.Implement Queue using Stacks
    231.Power of Two
    226.Invert Binary Tree
    225.Implement Stack using Queues
    Vue概述
    Git分布式版本控制工具
    分布式RPC框架Apache Dubbo
  • 原文地址:https://www.cnblogs.com/Xieyiincuit/p/14765328.html
Copyright © 2011-2022 走看看