简体   繁体   English

在 gRPC ASP.NET 中使用 JWT 时出现授权错误

[英]Authorization error while using JWT in gRPC ASP.NET

I used gRPC to try to make JWT authorization, but when I try to authorization, I get an error on the client:我使用 gRPC 尝试进行 JWT 授权,但是当我尝试授权时,客户端出现错误:

Grpc.Core.RpcException: "Status(StatusCode="Unimplemented", Detail="Bad gRPC response. HTTP status code: 404")"

I can't figure out what this is about.我想不通这是关于什么的。

My gRPC Server:我的 gRPC 服务器:

Startup.cs:启动.cs:

using AspNetCore.Identity.Mongo;
using GrpcServiceTiEventsy.MongoDB.Identity;
using GrpcServiceTiEventsy.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;
using MongoDB.Driver;
using System.Security.Claims;
using System.Text;

namespace GrpcServiceTiEventsy
{
    public class Startup
    {

        private readonly SymmetricSecurityKey _securityKey;
        public static MongoClient MongoDbClient { get; private set; }
        public static IConfiguration Configuration { get; private set; }


        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            _securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Token:SecretKey"]));
            MongoDbClient = new MongoClient(Configuration.GetConnectionString("MongoDB"));
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();

            services.AddSingleton<IMongoClient, MongoClient>(sp => MongoDbClient);

            services.AddIdentityMongoDbProvider<DatabaseAccount, DatabaseRole>(identity =>
                {
                    identity.Password.RequiredLength = 8;
                    identity.Password.RequireNonAlphanumeric = false;
                    identity.Password.RequireLowercase = false;
                    identity.Password.RequireUppercase = false;
                    identity.Password.RequireDigit = false;
                },
                mongo =>
                    mongo.ConnectionString = Configuration.GetConnectionString("MongoDB") + "/TiEventsyIdentity"
                );

            services.AddAuthorization(options =>
            {
                options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>
                {
                    policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
                    policy.RequireClaim(ClaimTypes.Name);
                });
            });

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(
                opt =>
                {
                    opt.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateActor = false,

                        ValidateIssuer = true,
                        ValidIssuer = Configuration["Token:Issuer"],

                        ValidateAudience = true,
                        ValidAudience = Configuration["Token:Audience"],

                        ValidateLifetime = true,

                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = _securityKey
                    };
                });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<GreeterService>();
                endpoints.MapGrpcService<LoginService>();
                endpoints.MapGrpcService<RefreshedTokenService>();
                endpoints.MapGrpcService<RegistrationService>();
            });
        }
    }

}

TokensGenerator.cs: TokensGenerator.cs:

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Grpc.Core;
using GrpcServiceTiEventsy.MongoDB.Identity;
using Microsoft.IdentityModel.Tokens;
using MongoDB.Driver;

namespace GrpcServiceTiEventsy.Models.Tokens
{
    public static class TokensGenerator
    {

        private static readonly SymmetricSecurityKey SecurityKey 
            = new (Encoding.ASCII.GetBytes(Startup.Configuration["Token:SecretKey"]));

        public static async Task<(string, string)> CreateTokens(DatabaseAccount account) =>
            (CreateJwt(account), await CreateRefreshToken(account));
        

        public static async Task<(string, string)>  CreateTokens(DatabaseAccount account, string jwtToken, string refreshToken)
        {
            if (ValidateJwtToken(jwtToken) && ValidateRefreshToken(account, refreshToken))
                return (CreateJwt(account), await CreateRefreshToken(account));

            throw new RpcException(Status.DefaultSuccess,"Tokens are not real");
        }

        private static bool ValidateRefreshToken(DatabaseAccount account, string token)
            => account.RefreshToken == token;

        private static bool ValidateJwtToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();

            try
            {
                tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateActor = false,

                    ValidateIssuer = true,
                    ValidIssuer = Startup.Configuration["Token:Issuer"],

                    ValidateAudience = true,
                    ValidAudience = Startup.Configuration["Token:Audience"],

                    ValidateLifetime = false,

                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = SecurityKey

                }, out var validatedToken);

                return true;
            }
            catch
            {
                return false;
            }
            
        }

        private static string CreateJwt(DatabaseAccount account)
        {
            var claims = new[] { new Claim(ClaimTypes.Name, account.UserName) };

            var credentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256);

            var expiresDays = Convert.ToInt32(Startup.Configuration["Token:LifetimeDay"]);

            var tokenHandler = new JwtSecurityTokenHandler();
            
            var token = new JwtSecurityToken(
                Startup.Configuration["Token:Issuer"],
                Startup.Configuration["Token:Audience"], 
                claims,
                expires: DateTime.Now.AddDays(expiresDays), 
                signingCredentials: credentials);

            return tokenHandler.WriteToken(token);
        }

        private static async Task<string> CreateRefreshToken(DatabaseAccount account)
        {
            var randomNumber = new byte[32];

            using (var rng = RandomNumberGenerator.Create())
                rng.GetBytes(randomNumber);
            var token = Convert.ToBase64String(randomNumber);

            var identityDb = Startup.MongoDbClient.GetDatabase("TiEventsyIdentity");
            var accountsCollection = identityDb.GetCollection<DatabaseAccount>("Users");

            var user = await accountsCollection.Find(ac => ac.Id == account.Id).FirstOrDefaultAsync();

            if (user != null)
            {
                var filter = Builders<DatabaseAccount>.Filter.Eq(s => s.Id, account.Id);
                var update = Builders<DatabaseAccount>.Update.Set(s => s.RefreshToken, token);

                await accountsCollection.UpdateOneAsync(filter, update);
            }
            else throw new RpcException(Status.DefaultSuccess,"No such user exists");
            

            return token;
        }
    }
}

GreeterService.cs: GreeterService.cs:

using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;

namespace GrpcServiceTiEventsy.Services
{
    public class GreeterService : Greeter.GreeterBase
    {
        private readonly ILogger<GreeterService> _logger;
        public GreeterService(ILogger<GreeterService> logger)
        {
            _logger = logger;
        }

        [Authorize]
        public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
        {
            return Task.FromResult(new HelloReply
            {
                Message = "Hello " + request.Name
            });
        }
    }
}

My Client:我的客户:

Program.cs:程序.cs:

using System;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Net.Client;
using GrpcServiceTiEventsy;


Console.WriteLine("Param...");

var channel = GrpcChannel.ForAddress("https://localhost:5001");

Console.WriteLine("Log...");

var l = new Login.LoginClient(channel);
var tokens = await l.LogAsync(new LoginRequest { Email = "igorka@gmail.com", Password = "pas@jKH$KJJT34592345" });
Console.WriteLine($"\n Access: {tokens.AccessToken}" +
                  $"\n Refresh: {tokens.RefreshToken}" +
                   "\n");

var credentials = CallCredentials.FromInterceptor((context, metadata) =>
{
    metadata.Add("Authorization", $"Bearer {tokens.AccessToken}");
    return Task.CompletedTask;
});

channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
});

Console.WriteLine("Test Auth...");
var te = new Greeter.GreeterClient(channel);
var outD = await te.SayHelloAsync(new HelloRequest { Name = "igorka@gmail.com" });
Console.WriteLine("Out: " +outD);

Error inferred here var outD = await te.SayHelloAsync(new HelloRequest { Name = "igorka@gmail.com" });此处推断错误var outD = await te.SayHelloAsync(new HelloRequest { Name = "igorka@gmail.com" });

If you wait for additional information, you will denigrate it in comments.如果您等待其他信息,您将在评论中诋毁它。

After a long debug and reading articles, I made the right Startup.cs.经过长时间的调试和阅读文章,我做出了正确的 Startup.cs。

Here is the working Startup.cs:这是工作的 Startup.cs:

using AspNetCore.Identity.Mongo;
using GrpcServiceTiEventsy.MongoDB.Identity;
using GrpcServiceTiEventsy.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;
using MongoDB.Driver;
using System.Text;
using Microsoft.AspNetCore.Identity;

namespace GrpcServiceTiEventsy
{
    public class Startup
    {

        private readonly SymmetricSecurityKey _securityKey;
        public static MongoClient MongoDbClient { get; private set; }
        public static IConfiguration Configuration { get; private set; }


        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            _securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Token:SecretKey"]));
            MongoDbClient = new MongoClient(Configuration.GetConnectionString("MongoDB"));
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();

            services.AddSingleton<IMongoClient, MongoClient>(sp => MongoDbClient);

            services.AddIdentityMongoDbProvider<DatabaseAccount, DatabaseRole>(identity =>
                {
                    identity.Password.RequiredLength = 8;
                    identity.Password.RequireNonAlphanumeric = false;
                    identity.Password.RequireLowercase = false;
                    identity.Password.RequireUppercase = false;
                    identity.Password.RequireDigit = false;
                },
                mongo =>
                    mongo.ConnectionString = Configuration.GetConnectionString("MongoDB") + "/TiEventsyIdentity"
                ).AddDefaultTokenProviders();


            services
                .AddAuthorization()
                .AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(
                opt =>
                {
                    opt.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateActor = false,

                        ValidateIssuer = true,
                        ValidIssuer = Configuration["Token:Issuer"],

                        ValidateAudience = true,
                        ValidAudience = Configuration["Token:Audience"],

                        ValidateLifetime = true,


                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = _securityKey
                    };
                });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<GreeterService>();
                endpoints.MapGrpcService<LoginService>();
                endpoints.MapGrpcService<RefreshedTokenService>();
                endpoints.MapGrpcService<RegistrationService>();
            });
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 ASP.NET Core 6 gRPC 服务中验证 AWS Cognito JWT - Validating AWS Cognito JWT in a ASP.NET Core 6 gRPC service ASP.NET Core 2.0 JWT 验证失败,并显示“用户授权失败:(空)”错误 - ASP.NET Core 2.0 JWT Validation fails with `Authorization failed for user: (null)` error 通过使用JWT令牌在Web API上声明角色授权-Asp.net核心标识 - Authorization by a Claim of a Role on Web API using JWT Token- Asp.net Core Identity 使用 ASP.NET Core 的 JWT 令牌无效令牌错误 - JWT token invalid token error using ASP.NET Core 勾选IP和JWT 在ASP.NET核中授权Web Api - Check IP with JWT Authorization in ASP.NET Core Web Api 在 ASP.NET Core 中的 Swagger 中使用 JWT(授权:承载) - Use JWT (Authorization: Bearer) in Swagger in ASP.NET Core ASP.NET 核心 - JWT 授权总是抛出 401 - ASP.NET Core - JWT authorization always throws 401 具有用户权限的基于JWT令牌的授权Asp.net core 2.0 - JWT Token based Authorization with user permission Asp.net core 2.0 Asp.Net Web Api JWT 授权始终获得 401 - Asp.Net Web Api JWT Authorization With Identity Always Getting 401 授权成功后,在 ASP.NET Core 3.1 MVC 中从 AWS Cognito 检索 JWT 令牌 - Retrieving JWT token from AWS Cognito in ASP.NET Core 3.1 MVC after successful authorization
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM