Ролевая авторизация для веб-API с IdentityServer4 - PullRequest
0 голосов
/ 10 мая 2018

Я использую IdentityServer4 (v2.2.1) с .Net Core 2.0 и Asp.Net Core Identity. У меня есть три проекта в моем решении.

  1. IdentityServer
  2. Веб-API
  3. Веб-приложение MVC

Я пытаюсь внедрить Role Based Authorization в моем Web API, чтобы любой клиент передавал токен доступа в Web API для доступа к ресурсам.

В настоящее время я могу реализовать авторизацию на основе ролей на контроллере приложений MVC, но не могу передать / настроить то же самое для контроллера WEB API.

Ниже приведены файлы Identity Server: Config.cs

public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            //SCOPE - Resource to be protected by IDS 
            new ApiResource("TCSAPI", "TCS API")
            {
                UserClaims = { "role" }
            }
        };
    }


 public static IEnumerable<Client> GetClients()
    {
        return new List<Client>
        {
new Client
            {
                ClientId = "TCSIdentity",
                ClientName = "TCS Mvc Client Application .",
                AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
                RequireConsent = false,
                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },

                RedirectUris = { "http://localhost:5002/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

                AlwaysSendClientClaims= true,
                AlwaysIncludeUserClaimsInIdToken = true,

                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Email,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.OfflineAccess,
                    "TCSAPI",
                    "office",
                    "role",
                },
                AllowOfflineAccess = true
            }
  };
    }

public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new IdentityResource[]
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
            new IdentityResources.Email(),
            new IdentityResource
            {
                Name = "role",
                DisplayName="User Role",
                Description="The application can see your role.",
                UserClaims = new[]{JwtClaimTypes.Role,ClaimTypes.Role},
                ShowInDiscoveryDocument = true,
                Required=true,
                Emphasize = true
            }
        };
    }

Startup.cs

 public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

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

        // Add application services.
        services.AddTransient<IEmailSender, EmailSender>();

        services.AddMvc();

        // configure identity server with in-memory stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddDeveloperSigningCredential()
            .AddInMemoryPersistedGrants()
            .AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryClients(Config.GetClients())
            .AddAspNetIdentity<ApplicationUser>();
    }

MVC WEB APP (Авторизация на основе ролей хорошо работает для MVC WEB APP):

RoleClaimAction.cs использование этого файла для добавления ролей в личность.

internal class RoleClaimAction : ClaimAction
{
    public RoleClaimAction()
        : base("role", ClaimValueTypes.String)
    {
    }

    public override void Run(JObject userData, ClaimsIdentity identity, string issuer)
    {
        var tokens = userData.SelectTokens("role");
        IEnumerable<string> roles;

        foreach (var token in tokens)
        {
            if (token is JArray)
            {
                var jarray = token as JArray;
                roles = jarray.Values<string>();
            }
            else
                roles = new string[] { token.Value<string>() };

            foreach (var role in roles)
            {
                Claim claim = new Claim("role", role, ValueType, issuer);
                if (!identity.HasClaim(c => c.Subject == claim.Subject
                                         && c.Value == claim.Value))
                {
                    identity.AddClaim(claim);
                }
            }
        }
    }
} 

MVC WEB APP / Startup.cs

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

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        services.AddCors();
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.SignInScheme = "Cookies";
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;

                options.ClientId = "TCSIdentity";

                //HYBRID FLOW
                options.ClientSecret = "secret";

                options.ClaimActions.Add(new RoleClaimAction()); // <-- 

                options.ResponseType = "code id_token token";
                options.GetClaimsFromUserInfoEndpoint = true;
                options.Scope.Add("TCSAPI");
                options.Scope.Add("offline_access");
                //END HYBRID FLOW
                options.SaveTokens = true;
                options.Scope.Add("role");

                options.TokenValidationParameters.NameClaimType = "name";
                options.TokenValidationParameters.RoleClaimType = "role";
            });

    }

MVC WEB APP / HomeController.cs этот метод действий хорошо работает с авторизацией на основе ролей, но когда я пытаюсь передать токен в Web Api для доступа к чему-либо с помощью авторизации на основе ролей, он не может авторизоваться. например var content = await client.GetStringAsync ("http://localhost:5001/user");

[Authorize(Roles = "User")]
    [Route("user")]
    public async Task<IActionResult> UserAccess()
    {

        var tokenClient = new TokenClient("http://localhost:5000/connect/token", "RoleApi", "secret");
        var tokenResponse = await tokenClient.RequestClientCredentialsAsync("TCSAPI");

        var client = new HttpClient();
        client.SetBearerToken(tokenResponse.AccessToken);
        var content = await client.GetStringAsync("http://localhost:5001/user");

        ViewBag.Json = JArray.Parse(content).ToString();
        return View("json");
    }
    [Authorize(Roles = "Admin")]
    [Route("admin")]
    public async Task<IActionResult> AdminAccess()
    {
        var accessToken = await HttpContext.GetTokenAsync("id_token");

        var client = new HttpClient();
        client.SetBearerToken(accessToken);
        var content = await client.GetStringAsync("http://localhost:5001/admin");
        ViewBag.Json = JArray.Parse(content).ToString();
        return View("json");
    }

WebAPI / Startup.cs

 public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvcCore()
            .AddAuthorization()
            .AddJsonFormatters();
        services.AddAuthentication("Bearer")
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;
                options.ApiName = "TCSAPI";
            });

        services.AddCors(options =>
        {
            options.AddPolicy("default", policy =>
            {
                policy.WithOrigins("http://localhost:5002")
                .AllowAnyHeader()
                .AllowAnyMethod();
            });
        });
    }

WEB API / TestController.cs

[Route("admin")]
    [Authorize(Roles = "Admin")]
    public IActionResult AdminAccess()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
    [Route("user")]
    [Authorize(Roles = "User")]
    public IActionResult UserAccess()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
    [AllowAnonymous]
    [Route("public")]
    public IActionResult PublicAccess()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }

1 Ответ

0 голосов
/ 10 мая 2018

Ваш код не является авторизацией на основе политики. Ваша выглядит как .NET Framework Role Based Authorization.

Для авторизации на основе политик необходимо выполнить следующие действия:

1. В Startup.cs вашего проекта Web API вам нужно добавить что-то вроде:

// more code
.AddMvcCore()
            .AddAuthorization(options =>
            {
                options.AddPolicy("Policy1",
                    policy => policy.Requirements.Add(new Policy1Requirement()));
                options.AddPolicy("Policy2",
                    policy => policy.Requirements.Add(new Policy2Requirement()));
                .
                .
                .
                .
            })
 // more code

2. Тогда вам нужен класс для каждого Policy(X)Requirement:

public class Policy1Requirement : AuthorizationHandler<Policy1Requirement>, IAuthorizationRequirement
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AdminUserRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == "role" && c.Value == "<YOUR_ROLE_FOR_THIS_POLICY>"))
        {
            context.Fail();
        }
        else
        {
            context.Succeed(requirement);
        }
        return Task.FromResult(0);
    }
}

3. И затем в конце, где вы применяете политику, вам необходимо иметь:

[Authorize(Policy = "Policy1")]
public class MyController : Controller
{
.
.    
}

Удачи!

PS:

Имена Policy(X) и Policy(X)Requirement только для пояснения. Вы можете использовать любые имена, какие захотите, если вы реализуете правильный интерфейс IAuthorizationRequirement и наследуете класс AuthorizationHandler

...