在客户端 API 中使用 JWT 令牌

Posted

技术标签:

【中文标题】在客户端 API 中使用 JWT 令牌【英文标题】:Using JWT Token in Client API 【发布时间】:2021-08-12 04:57:14 【问题描述】:

我在 .NET5 中有一个 API,使用 JWTBearer 来保护 API。现在我想配置我的客户端应用程序以使用从 api/gettoken 生成的令牌。它在招摇中工作得很好,但我不知道如何配置我的 MVC 和 API(消费 API)来使用这个令牌。有人可以通过在启动时提供配置服务和配置方法来提供帮助吗

致格伦,

我有 3 个项目 JWT.IDP、JWT.API、JWT.MVC。 JWT.IDP 颁发令牌,我的意图是在 JWT.API 中使用该令牌并从 JWT.MVC 调用 JWT.API 函数。 IDP 运行良好,我可以生成令牌并且我的 JWT.MVC 登录控制器能够接收它。下面代码中的最后一个函数(GetWeatherData)是根据您给出的想法编码的。如果我不通过令牌,我以前会得到 401 错误,现在我得到 500 Internal Server Error

namespace JWT.MVC.Controllers

    public class LoginController : Controller
    

        public IActionResult DoLogin()
        

            return View();
        


        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DoLogin([Bind("EmailOrName,Password")] LoginRequestModel loginRequestModel)
        
            var apiName = $"https://localhost:44318/api/User/login";

            HttpClient httpClient = new HttpClient();
            HttpResponseMessage response = await httpClient.PostAsJsonAsync(apiName, loginRequestModel);
            var jasonString = await response.Content.ReadAsStreamAsync();

            var data = await JsonSerializer.DeserializeAsync<IEnumerable<AccessibleDb>>
                    (jasonString, new JsonSerializerOptions()  PropertyNameCaseInsensitive = true );

            foreach (var item in data)
            
                item.UserName = loginRequestModel.EmailOrName;
            

            return View("SelectDatabase" , data);
        

      
        public async Task<IActionResult> PostLogin(string db, string user)
        
            TokenRequestModel tokenRequestModel = new TokenRequestModel()  Database = db, UserName = user ;

            var apiName = $"https://localhost:44318/api/User/tokenonly";

            HttpClient httpClient = new HttpClient();
            HttpResponseMessage response = await httpClient.PostAsJsonAsync(apiName, tokenRequestModel);
            var jasonString = await response.Content.ReadAsStreamAsync();

            var data = await JsonSerializer.DeserializeAsync<AuthenticationModel>
                    (jasonString, new JsonSerializerOptions()  PropertyNameCaseInsensitive = true );

            var stream = data.Token;
            var handler = new JwtSecurityTokenHandler();
            var jsonToken = handler.ReadToken(stream);
            var tokenS = jsonToken as JwtSecurityToken;
            var selectedDb = tokenS.Claims.First(claim => claim.Type == "Database").Value;

            ViewBag.SelectedDb = selectedDb;

            return View(data);
        

        public async Task<IActionResult> GetWeatherData(string token)
        

            var apiName = $"https://localhost:44338/weatherforecast";

            HttpClient httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
            HttpResponseMessage response = await httpClient.GetAsync(apiName);
            if (!response.IsSuccessStatusCode)
            
                ViewBag.Error = response.StatusCode;
                return View("Weatherdata");
            
            var jasonString = await response.Content.ReadAsStreamAsync();
            var data = await JsonSerializer.DeserializeAsync<WeatherForecast>
                    (jasonString, new JsonSerializerOptions()  PropertyNameCaseInsensitive = true );

        
            return View("Weatherdata" , data);
        
    

JWT.MVC 的启动类如下


 public void ConfigureServices(IServiceCollection services)
        
            services.AddControllersWithViews();
            services.AddAuthentication("Bearer")
             .AddJwtBearer("Bearer", options =>
             

                 options.Audience = "SecureApiUser";
                 options.Authority = "https://localhost:44318";
                 options.TokenValidationParameters = new TokenValidationParameters
                 
                     ValidateAudience = false
                 ;
             );

          
        


JWT.API 的启动类如下

  public void ConfigureServices(IServiceCollection services)
        

            services.AddControllers();
            //Copy from IS4
            services.AddAuthentication("Bearer")
              .AddJwtBearer("Bearer", options =>
              
                  
                  options.Audience = "SecureApiUser";
                  options.Authority = "https://localhost:44318";
                  options.TokenValidationParameters = new TokenValidationParameters
                  
                      ValidateAudience = false
                  ;
              );

          
            //End
            services.AddSwaggerGen(c =>
            
                c.SwaggerDoc("v1", new OpenApiInfo  Title = "JWT.API", Version = "v1" );
            );
        

JWT.IDP 的启动类如下

  public void ConfigureServices(IServiceCollection services)
        

            services.AddControllers();

            //Configuration from AppSettings
            services.Configure<JwtSettings>(Configuration.GetSection("JWT"));
            //User Manager Service
            services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<IdentityDbContext>();
            services.AddScoped<IUserService, UserService>();
            //Adding DB Context with MSSQL
            services.AddDbContext<IdentityDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("IdentityDbConnectionString"),
                    b => b.MigrationsAssembly(typeof(IdentityDbContext).Assembly.FullName)));

            //Adding Athentication - JWT
            services.AddAuthentication(options =>
            
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            )
                .AddJwtBearer(o =>
                
                    o.RequireHttpsMetadata = false;
                    o.SaveToken = false;
                    o.TokenValidationParameters = new TokenValidationParameters
                    
                        ValidateIssuerSigningKey = true,
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ClockSkew = TimeSpan.FromMinutes(Convert.ToInt32(Configuration["JWT:DurationInMinutes"])),
                        ValidIssuer = Configuration["JWT:Issuer"],
                        ValidAudience = Configuration["JWT:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Key"]))
                    ;
                );

           

            services.AddSwaggerGen(c =>
            
                c.SwaggerDoc("v1", new OpenApiInfo  Title = "JWT.IDP", Version = "v1" );
            );
        

JWT 设置如下

 "JWT": 
    "key": "C1CF4B7DC4C4175B6618DE4F55CA4",
    "Issuer": "http://localhost:44318",
    "Audience": "SecureApiUser",
    "DurationInMinutes": 60
  ,

【问题讨论】:

您好 Jacob,请尝试 Microsoft Docs。这确实是一个非常有用的资源,你应该能够在那里找到你的答案。例如,从阅读Authenticate with bearer tokens 开始。如果您对实施有具体问题,请回来,我们会尽力为您提供帮助! 谢谢丹尼斯。我会尽力让你知道 【参考方案1】:

简短的回答是

httpClient.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", [token])

保持“Bearer”不变,它只是一个常数。将 [token] 替换为您使用的 OAuth 协议返回给我们的 base 64 编码令牌值。

【讨论】:

您好 GlennSills,感谢您提供如此简单的解决方案。我仍在努力解决这个问题。我完成了发布令牌​​的 API,但如果我有多个客户端使用该令牌。我有以下问题,请您帮忙。 1.我们是否必须在启动时提供任何东西,或者只有您的上述建议就足够了。 2. JWT 的 aud 设置,我们如何传递多个受众 3. 我的客户端 API 或 MVC 如何将自己标识为受众之一 1) 不,把它放在启动中是行不通的。想想看,令牌是短暂的——你得到一个然后你进行身份验证。如果您有很多使用相同令牌的请求,并且您想避免在每个位置对其进行编码,那么我建议编写一个初始化 HttpClient 对象的包装类。然后让所有需要使用 HttpClient 和 at 令牌调用的地方。 2) 当您请求令牌时,您可以请求 1 或多个受众。当服务器创建一个令牌时,它可以用于 1 个或多个受众。 3) 当您在服务器上配置客户端时,您定义了在请求令牌时允许请求的一组受众。 您好格伦,非常感谢您的解释。请把我当作首发。我真的很高兴与您联系。我理解第一点,我会弄清楚如何做到这一点。但关于第 2 点和第 3 点,我需要你的帮助。如果我发布我的代码,您能提供帮助吗?

以上是关于在客户端 API 中使用 JWT 令牌的主要内容,如果未能解决你的问题,请参考以下文章

在 vert.x 中获取用户并验证 JWT 令牌

如何从使用 JWT 令牌的 Web 客户端应用程序中注销用户

即使令牌是在邮递员中创建的,JWT 令牌也会返回空

在 .net core api 中存储/验证存储在 HttpOnly cookie 中的 JWT 令牌

使用 JWT 令牌保护 asp.net 核心 Web api 时如何从 Azure AD 获取用户

如何使 AzureAD 和自定义 JWT 令牌在 Web API 中并行工作?