ASP.NET Core 托管的 Blazor 模板中的授权问题

3

我在API的控制器函数上使用了[Authorize]属性,但它总是显示数据而未起作用。当我调试身份用户时,发现它并未被认证,但它总是发送JSON数据,而不是应该发送未经身份验证的响应。有什么帮助可以知道为什么[Authorize]属性无法正常工作吗?

using System;
using System.IO.Compression;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Serialization;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using MedicalDivision.Server.Security;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace test.Server
{
    public class Startup
    {
        private X509Certificate2 Cert { get; }
        private IConfiguration Configuration { get; }
        private IWebHostEnvironment Env { get; }
        private ITokenProvider TokenProvider { get; }
        private PasswordHelper PasswordHelper { get; }
        private IHttpContextAccessor httpContextAccessor { get; }
        private IServiceProvider ServiceProvider { get; }

        public readonly string _myAllowSpecificOrigins = "_myAllowSpecificOrigins";

        public Startup(IConfiguration configuration,IWebHostEnvironment env,IServiceProvider serviceProvider)
        {
            ServiceProvider = serviceProvider;
            Configuration = configuration;
            Env = env;
            Cert = new X509Certificate2(Convert.FromBase64String(Configuration["Auth:Cert:Data"]),  Configuration["Auth:Cert:Password"], X509KeyStorageFlags.MachineKeySet);
            TokenProvider =new JwtTokenProvider(Cert, Configuration,env);
            PasswordHelper = new PasswordHelper();
            httpContextAccessor = ServiceProvider.GetService<IHttpContextAccessor>();
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(serviceProvider => new AuthManager(Configuration,TokenProvider));


            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
            {
                o.TokenValidationParameters = TokenProvider.GetValidationParameters();
                o.SecurityTokenValidators.Clear();
                o.SecurityTokenValidators.Add(new CustomTokenValidator(ServiceProvider));
            });
            services.AddAuthorization();
            services.AddResponseCompression(opts =>
            {
                opts.EnableForHttps = true;
                opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/octet-stream", "image/png", "font/otf", "image/gif", "image/x-icon", "image/jpeg", "application/pdf", "image/svg+xml", "font/ttf", "font/woff", "font/woff2", "application/xml", "text/csv" });
            });
            services.Configure<GzipCompressionProviderOptions>(o =>
            {
                o.Level = CompressionLevel.Optimal;
            });
            services.AddCors(options =>
            {
                options.AddPolicy(_myAllowSpecificOrigins,
                    builder =>
                    {
                        builder
                            .AllowAnyOrigin() 
                            .AllowAnyMethod()
                            .AllowAnyHeader();
                            //.AllowCredentials();
                    });
            });
            services.AddMvc().AddNewtonsoftJson();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
             app.UseAuthentication();
            app.UseAuthorization(); 
            app.UseResponseCompression();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBlazorDebugging();
            }

            app.UseCors(_myAllowSpecificOrigins);
            app.UseStaticFiles();
            app.UseClientSideBlazorFiles<Client.Startup>();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
                endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
            });
        }
    }
}

以及自定义验证类:

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;

namespace test.Server.Security
{
    public class CustomTokenValidator : ISecurityTokenValidator
    {
        private readonly JwtSecurityTokenHandler _tokenHandler;
        //private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly HttpContextAccessor _httpContextAccessor = new HttpContextAccessor();

        public CustomTokenValidator(IServiceProvider serviceProvider)
        {
            _tokenHandler = new JwtSecurityTokenHandler();
           // _httpContextAccessor = httpContextAccessor;
           // _httpContextAccessor = serviceProvider.GetService<IHttpContextAccessor>();

        }

        public bool CanValidateToken => true;

        public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;

        public bool CanReadToken(string securityToken)
        {
            return _tokenHandler.CanReadToken(securityToken);
        }

        public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters,
            out SecurityToken validatedToken)
        {
            //How to access HttpContext/IP address from here?
            var httpContext = _httpContextAccessor.HttpContext;

            var xx = httpContext.Connection.RemoteIpAddress;
//simple condition for testing
            var yy = xx.ToString()=="::1";
            if (yy)
            { 
                validatedToken=null;
                return  null;
            }
            var principal = _tokenHandler.ValidateToken(securityToken, validationParameters, out validatedToken);

            return principal;
        }
    }
}

以下是控制器代码:

 [HttpGet]
        [Authorize]
        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            var r= Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToList();
            r.Add(new WeatherForecast(){Date =DateTime.Now,TemperatureC =111111,Summary = $"{HttpContext.User.Identity.IsAuthenticated}"});

            return r;
        }

预期发送未被授权的结果,但实际上发送了数据。 HttpContext.User.Identity.IsAuthenticated 返回false,但仍然显示数据。

如果您正在使用身份验证,最好使用SignInManager类来检查已认证的用户。 - Jonathan Alfaro
他正在使用Jwt身份验证。 - enet
2个回答

1
自动认证中间件需要稍后进行配置:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  // not here 
  //   app.UseAuthentication();
  //  app.UseAuthorization(); 
    app.UseResponseCompression();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBlazorDebugging();
    }

    app.UseCors(_myAllowSpecificOrigins);
    app.UseStaticFiles();
    app.UseClientSideBlazorFiles<Client.Startup>();

    app.UseRouting();

  // but here 
    app.UseAuthentication();
    app.UseAuthorization(); 

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
        endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
    });
}

它们必须在路由之后添加(执行后)。

是的,这对的位置是错误的,但这绝不意味着问题就出在那里。 - enet
我没有测试OP的代码的其余部分,但将它们放在顶部可以解释我们所知道的一个症状。 - H H

0
也许你需要类似这样的东西:
services.AddMvcCore(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接