简体   繁体   English

授权属性不适用于 IdentityServer4 和 .NET Core 3.1

[英]Authorize attribute not working with IdentityServer4 and .NET Core 3.1

I have a .NET Core 3.1 project using Identity and IdentityServer4 to implement the Resource Owner Password grant type.我有一个 .NET Core 3.1 项目,使用 Identity 和 IdentityServer4 来实现资源所有者密码授予类型。 I can get the tokens no problem but the [Authorize] attribute isn't working, it just lets everything through.我可以毫无问题地获得令牌,但 [Authorize] 属性不起作用,它只是让一切都通过。 An important note is that my API and Identity server are in the same project.一个重要的注意事项是我的 API 和身份服务器在同一个项目中。 From comments online it seems like it might be a middleware order issue but I can't seem to find a combination that works.从在线评论看来,这可能是中间件订单问题,但我似乎找不到有效的组合。 I've double checked that when no Authorization header is attached, the endpoint code is still hit.我已经仔细检查过,当没有附加授权标头时,端点代码仍然被命中。

Here's my Startup.cs file:这是我的 Startup.cs 文件:

using System;
using System.Collections.Generic;
using IdentityServer4.Models;
using LaunchpadSept2020.App;
using LaunchpadSept2020.App.Repositories;
using LaunchpadSept2020.App.Repositories.Interfaces;
using LaunchpadSept2020.App.Seeds;
using LaunchpadSept2020.Models.Entities;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace LaunchpadSept2020.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Set up the database
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"),
                b =>
                {
                    b.MigrationsAssembly("LaunchpadSept2020.App");
                })
            );

            services.AddIdentity<User, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.Configure<IdentityOptions>(options =>
            {
                options.Password.RequiredLength = 6;
                options.Password.RequireLowercase = true;
                options.Password.RequireUppercase = true;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireDigit = true;
            });

            services.AddAuthentication("Bearer")
                .AddIdentityServerAuthentication(options =>
                {
                    options.ApiName = "launchpadapi";
                    options.Authority = "http://localhost:25000";
                    options.RequireHttpsMetadata = false;
                });

            services.AddIdentityServer()
                .AddOperationalStore(options =>
                {
                    options.ConfigureDbContext = builder => builder.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"),
                        npgSqlOptions =>
                        {
                            npgSqlOptions.MigrationsAssembly("LaunchpadSept2020.App");
                        });
                })
                .AddInMemoryClients(Clients.Get())
                .AddAspNetIdentity<User>()
                .AddInMemoryIdentityResources(Resources.GetIdentityResources())
                .AddInMemoryApiResources(Resources.GetApiResources())
                .AddInMemoryApiScopes(Resources.GetApiScopes())
                .AddDeveloperSigningCredential();

            services.AddControllers();

            // Add Repositories to dependency injection
            services.AddScoped<ICompanyRepository, CompanyRepository>();
            services.AddScoped<IUserRepository, UserRepository>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, UserManager<User> userManager, RoleManager<IdentityRole> roleManager)
        {
            // Initialize the database
            UpdateDatabase(app);

            // Seed data
            UserAndRoleSeeder.SeedUsersAndRoles(roleManager, userManager);

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

            //app.UseHttpsRedirection();
            app.UseRouting();

            app.UseIdentityServer(); // Includes UseAuthentication
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

        // Update the database to the latest migrations
        private static void UpdateDatabase(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices
                 .GetRequiredService<IServiceScopeFactory>()
                 .CreateScope())
            {
                using (var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>())
                {
                    context.Database.Migrate();
                }
            }
        }
    }

    internal class Clients
    {
        public static IEnumerable<Client> Get()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "mobile",
                    ClientName = "Mobile Client",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                    ClientSecrets = { new Secret("MySecret".Sha256()) },
                    AllowedScopes = new List<String> { "launchpadapi.read" }
                    //AllowAccessTokensViaBrowser = true,
                    //RedirectUris = { "http://localhost:25000/signin-oidc" },
                    //PostLogoutRedirectUris = { "http://localhost:25000/signout-callback-oidc" },
                    //AllowOfflineAccess = true
                }
            };
        }
    }

    internal class Resources
    {
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new[]
            {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
            new IdentityResources.Email(),
            new IdentityResource
            {
                Name = "role",
                UserClaims = new List<string> {"role"}
            }
        };
        }

        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new[]
            {
            new ApiResource
            {
                Name = "launchpadapi",
                DisplayName = "Launchpad API",
                Description = "Allow the application to access the Launchpad API on your behalf",
                Scopes = new List<string> { "launchpadapi.read", "launchpadapi.write"},
                ApiSecrets = new List<Secret> {new Secret("ScopeSecret".Sha256())},
                UserClaims = new List<string> {"role"}
            }
        };
        }

        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new[]
            {
                new ApiScope("launchpadapi.read", "Read Access to Launchpad API"),
                new ApiScope("launchpadapi.write", "Write Access to Launchpad API")
            };
        }
    }
}

And my controller:还有我的控制器:

using System.Collections.Generic;
using System.Threading.Tasks;
using LaunchpadSept2020.App.Repositories.Interfaces;
using LaunchpadSept2020.Models.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace LaunchpadSept2020.Api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class CompanyController : ControllerBase
    {
        private readonly ICompanyRepository _companyRepository;

        public CompanyController(ICompanyRepository companyRepository)
        {
            _companyRepository = companyRepository;
        }

        [HttpPost]
        [Authorize]
        public async Task<ActionResult<CompanyVM>> Create([FromBody] CompanyCreateVM data)
        {
            // Make sure model has all required fields
            if (!ModelState.IsValid)
                return BadRequest("Invalid data");

            try
            {
                var result = await _companyRepository.Create(data);
                return Ok(result);
            }
            catch
            {
                return StatusCode(500);
            }
        }

        [HttpGet]
        [Authorize]
        public async Task<ActionResult<List<CompanyVM>>> GetAll()
        {
            try
            {
                var results = await _companyRepository.GetAll();
                return Ok(results);
            }
            catch
            {
                return StatusCode(500);
            }
        }
    }
}

I think a general issue is that you mix IdentityServer in the same app as ASP.NET Identity, in general my experience is that it gets hard to know who is doing what and its hard to fully understand.我认为一个普遍的问题是你将 IdentityServer 与 ASP.NET Identity 混合在同一个应用程序中,总的来说,我的经验是很难知道谁在做什么,也很难完全理解。 I always recommend putting IdentityServer and the API in independent services.我总是建议将 IdentityServer 和 API 放在独立的服务中。 Just to get a clean separation of concerns.只是为了得到一个干净的关注点分离。

For local API authentication you need the following additional configuration in Startup:对于本地 API 身份验证,您需要在 Startup 中进行以下附加配置:

public void ConfigureServices(IServiceCollection services)
{
  ....
  // After services.AddIdentityServer()
  services.AddLocalApiAuthentication();
}

For reference see the docs .有关参考,请参阅 文档

And then you need to specificy the local API policy as part of the Authorize attribute on your API:然后您需要将本地 API 策略指定为 API 上的Authorize属性的一部分:

[Authorize(LocalApi.PolicyName)]

See a local API example .请参阅本地 API 示例

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

相关问题 Razor .net 核心 3.1 与 IdentityServer4 - Razor .net core 3.1 with IdentityServer4 我想更新 IdentityServer4 (.NET Core 3.1) 中的用户声明 - I want to Update User Claims in IdentityServer4 (.NET Core 3.1) IdentityServer4 LocalApi 与 .NET Core 3.1 中的自定义策略 - IdentityServer4 LocalApi with custom policy in .NET Core 3.1 ASP.Net Core-IdentityServer4客户端凭据授权不起作用 - ASP.Net Core - IdentityServer4 client credentials authorization not working 如何使用托管在 .Net framework 4.6 应用程序中的登录页面对 .Net Core 3.1 上的 IdentityServer4 应用程序进行身份验证? - How to use login page hosted in .Net framework 4.6 application to authenticate for IdentityServer4 application on .Net Core 3.1? 如何使用IdentityServer4访问令牌和[Authorize]属性 - How to use IdentityServer4 Access Token and [Authorize] attribute 如何在 .net 核心 web api 3.1 中创建 Customtom 授权属性? - How to create Customtom Authorize Attribute in .net core web api 3.1? 升级到 NET Core 3.1 后,Authorize 属性始终返回 401 - Authorize attribute always returns 401 after upgrading to NET Core 3.1 asp.net core 3 和 identityserver4 - asp.net core 3 and identityserver4 IdentityServer4 Asp.Net 核心身份 - IdentityServer4 Asp.Net Core Identity
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM