无法使[Authorize(Role =“ Administrator”)]属性起作用

问题描述 投票:0回答:1

自几天以来,我一直在解决这个问题,但没有找到任何解决方案。

首先,我使用IdentyServer4创建了一个SSO服务器,这一部分有效,我可以进行身份​​验证

其次,我将Asp.Net.Identy添加到SSO服务器中,以管理用户和角色,并将所有这些都存储到持久性存储中。这部分也起作用。我已经创建了用户和角色,并将用户附加到角色。

第三,我创建了一个APS.NET MVC Web应用程序,该应用程序由SSO服务器保护并使用OWIN。我使用Authorize属性来保护控制器,并且该控制器中的一种方法要求是Administrator。

问题是,当我尝试访问此方法时,系统进入循环,从SSO服务器来回查询ProfileService。

现在有一些代码:

ProfileService进入SSO服务器:

using IdentityServer.Models;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace IdentityServer.Services
{
    public class ProfileService : IProfileService
    {
        private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly RoleManager<IdentityRole> _roleManager;

        public ProfileService(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory)
        {
            _userManager = userManager;
            _claimsFactory = claimsFactory;
            _roleManager = roleManager;
        }

        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            var sub = context.Subject.GetSubjectId();
            var user = await _userManager.FindByIdAsync(sub);
            var principal = await _claimsFactory.CreateAsync(user);

            var claims = await _userManager.GetClaimsAsync(user);

            claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();

            // Add custom claims in token here based on user properties or any other source
            claims.Add(new Claim("employee_id", user.EmployeeId ?? string.Empty));

            context.IssuedClaims = (List<Claim>)claims;
        }

        public async Task IsActiveAsync(IsActiveContext context)
        {
            var sub = context.Subject.GetSubjectId();
            var user = await _userManager.FindByIdAsync(sub);
            context.IsActive = user != null;
        }
    }
}

Startup.cs到Web MVC应用程序中

using Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using System;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.Owin.Security.OpenIdConnect;
using System.Security.Claims;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Linq;
using Microsoft.Owin.Extensions;
using Microsoft.Owin.Security.Authorization.Infrastructure;
using IdentityModel;
using System.Threading.Tasks;
using System.Collections.Generic;

[assembly: OwinStartup(typeof(WebApplication1.Startup))]

namespace WebApplication1
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Pour plus d'informations sur la configuration de votre application, visitez https://go.microsoft.com/fwlink/?LinkID=316888
            app.UseAuthorization();

            app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                AuthenticationType = "Cookies",
                ExpireTimeSpan = TimeSpan.FromSeconds(10),
                SlidingExpiration = true
            });


            var tokenHandler = new JwtSecurityTokenHandler();
            tokenHandler.InboundClaimFilter.Clear();

            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                AuthenticationType = "oidc",               
                SignInAsAuthenticationType = "Cookies",
                Authority = "https://localhost:5001", //adresse du serveur IdentityServer
                ClientId = "mvc", //nom du client au sens IdentityServer, doit être configuré dans la liste des client du fichier config.cs du serveur
                RedirectUri = "https://localhost:44354/Home/About", //redirection si login ok doit -être dans la liste des RefirectUris du client configuré sur le serveur
                PostLogoutRedirectUri = "https://localhost:44354/Home",
                ResponseType = "id_token token",
                Scope = "openid profile web_api",
                UseTokenLifetime = true,
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    SecurityTokenValidated = async n =>
                    {
                        var claims_to_exclude = new[]
                        {
                        "aud", "iss", "nbf", "exp", "nonce", "iat", "at_hash"
                        };

                        List<Claim> claims_to_keep =
                            n.AuthenticationTicket.Identity.Claims.Where(x => false == claims_to_exclude.Contains(x.Type)).ToList();
                        claims_to_keep.Add(new Claim("id_token", n.ProtocolMessage.IdToken));
                        if (n.ProtocolMessage.AccessToken != null)
                        {
                            claims_to_keep.Add(new Claim("access_token", n.ProtocolMessage.AccessToken));

                            //var userInfoClient = new UserInfoClient(EP_Configuration.epIdpUserInfoAccessPoint);
                            //var userInfoResponse = await userInfoClient.GetAsync(n.ProtocolMessage.AccessToken);
                            //var userInfoClaims = userInfoResponse.Claims; // filter sub since we're already getting it from id_token
                            //claims_to_keep.AddRange(userInfoClaims);
                        }

                        var ci = new ClaimsIdentity(
                            n.AuthenticationTicket.Identity.AuthenticationType);
                        ci.AddClaims(claims_to_keep);
                        n.AuthenticationTicket = new Microsoft.Owin.Security.AuthenticationTicket(
                            ci, n.AuthenticationTicket.Properties
                        );
                    },
                    RedirectToIdentityProvider = n =>
                    {
                        if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                        {
                            var id_token = n.OwinContext.Authentication.User.FindFirst("id_token")?.Value;
                            n.ProtocolMessage.IdTokenHint = id_token;
                        }

                        return Task.FromResult(0);
                    }
                }
            });
            app.UseStageMarker(PipelineStage.Authenticate);
        }
    }
}

安全方法:

 [Authorize(Roles = "Administrator")]
 public ActionResult About()
 {
    ViewBag.Message = "Your application description page.";
    return View();
 }

当我单击调用此方法的页面中的链接时,上下文传递到服务器不包含要声明的角色:

enter image description here

您可以看到,TequestedClaimType为空。

另一件事,当我得到我的用户时,它没有附加任何角色列表:

enter image description here

同时,角色进入了Pincipal对象的标识:

enter image description here

我在这里和其他站点上阅读了很多文章,但到目前为止,我没有尝试解决问题。

欢迎任何帮助。

更新1:

我稍微更改了我的MVC客户端配置服务器端,以如下方式将作用域添加到客户端:首先,我添加了这个:

 public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResource("roles", new[] { "role" })
            };
        }

我向Alice用户添加了管理员角色。当我获得用户令牌时,token_Id为:

{
  "nbf": 1580127045,
  "exp": 1580127345,
  "iss": "https://localhost:5001",
  "aud": "mvc",
  "nonce": "637157238434573534.ZWU0MjNhYTItZDU5Ni00YjQxLThlNzYtYTRkYjAyNGYxZDA5NDZiZmZmZTktMzYwZS00NzliLThkN2UtZTJlZDNjYzRkZGUx",
  "iat": 1580127045,
  "at_hash": "JaDEsHC7VCXRuLghvo-pZQ",
  "sid": "47c344d69e4f20899c8c9f8594f5c47f",
  "sub": "99de568f-49d8-4dd1-b256-14c2647504cd",
  "auth_time": 1580121815,
  "idp": "local",
  "amr": [
    "pwd"
  ]
}

{
  "nbf": 1580127045,
  "exp": 1580130645,
  "iss": "https://localhost:5001",
  "aud": [
    "https://localhost:5001/resources",
    "web_api"
  ],
  "client_id": "mvc",
  "sub": "99de568f-49d8-4dd1-b256-14c2647504cd",
  "auth_time": 1580121815,
  "idp": "local",
  "name": "Alice Smith",
  "given_name": "Alice",
  "family_name": "Smith",
  "email": "[email protected]",
  "email_verified": "true",
  "website": "http://alice.com",
  "address": "{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }",
  "employee_id": "",
  "role": "Administrator",
  "scope": [
    "openid",
    "profile",
    "roles",
    "web_api"
  ],
  "amr": [
    "pwd"
  ]
}

在我的家庭控制器中,此代码始终返回false:

    if (!User.IsInRole("Administrator"))
        throw new SecurityException("User is not an admin.");

并且此调用循环在客户端和服务器之间:

[Authorize(Roles = "Administrator")]
public ActionResult About()
{
    ViewBag.Message = "Your application description page.";
    return View();
}
asp.net-mvc asp.net-identity owin identityserver4 identity
1个回答
0
投票

我最终通过从网络上收集不同的资源来使它正常工作。

我战斗时的主要问题是在startup.cs的MVC客户端中。

var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");

var userInfoResponse = await client.GetUserInfoAsync( new UserInfoRequest {Address = disco.UserInfoEndpoint, Token= n.ProtocolMessage.AccessToken });

claims_to_keep.AddRange(userInfoClaims);

以及完整的内存类:

using Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using System;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.Owin.Security.OpenIdConnect;
using System.Security.Claims;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Linq;
using Microsoft.Owin.Extensions;
using Microsoft.Owin.Security.Authorization.Infrastructure;
using IdentityModel;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net.Http;
using IdentityModel.Client;

[assembly: OwinStartup(typeof(WebApplication1.Startup))]

namespace WebApplication1
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Pour plus d'informations sur la configuration de votre application, visitez https://go.microsoft.com/fwlink/?LinkID=316888
            app.UseAuthorization();

            app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                AuthenticationType = "Cookies",
                ExpireTimeSpan = TimeSpan.FromSeconds(10),
                SlidingExpiration = true
            });

            var tokenHandler = new JwtSecurityTokenHandler();
            tokenHandler.InboundClaimFilter.Clear();

            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {

                AuthenticationType = "oidc",
                SignInAsAuthenticationType = "Cookies",
                Authority = "https://localhost:5001", //adresse du serveur IdentityServer
                ClientId = "mvc", //nom du client au sens IdentityServer, doit être configuré dans la liste des client du fichier config.cs du serveur
                RedirectUri = "https://localhost:44354/Home", //redirection si login ok doit -être dans la liste des RefirectUris du client configuré sur le serveur
                PostLogoutRedirectUri = "https://localhost:44354/Home",
                ResponseType = "id_token token",
                Resource = "Role",
                Scope = "openid profile web_api roles",
                UseTokenLifetime = true,
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    SecurityTokenValidated = async n =>
                    {
                        var claims_to_exclude = new[]
                        {
                        "test"//"aud", "iss", "nbf", "exp", "nonce", "iat", "at_hash"
                        };

                        List<Claim> claims_to_keep =
                            n.AuthenticationTicket.Identity.Claims.Where(x => false == claims_to_exclude.Contains(x.Type)).ToList();
                        claims_to_keep.Add(new Claim("id_token", n.ProtocolMessage.IdToken));
                        if (n.ProtocolMessage.AccessToken != null)
                        {
                            claims_to_keep.Add(new Claim("access_token", n.ProtocolMessage.AccessToken));

                            var client = new HttpClient();
                            var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");

                            var userInfoResponse = await client.GetUserInfoAsync( new UserInfoRequest {Address = disco.UserInfoEndpoint, Token= n.ProtocolMessage.AccessToken });

                            var userInfoClaims = userInfoResponse.Claims; // filter sub since we're already getting it from id_token

                            claims_to_keep.AddRange(userInfoClaims);
                        }

                        var ci = new ClaimsIdentity(
                            n.AuthenticationTicket.Identity.AuthenticationType);
                        ci.AddClaims(claims_to_keep);
                        n.AuthenticationTicket = new Microsoft.Owin.Security.AuthenticationTicket(
                            ci, n.AuthenticationTicket.Properties
                        );
                    },
                    RedirectToIdentityProvider = n =>
                    {
                        if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                        {
                            var id_token = n.OwinContext.Authentication.User.FindFirst("id_token")?.Value;
                            n.ProtocolMessage.IdTokenHint = id_token;
                        }

                        return Task.FromResult(0);
                    }
                }
            });
            app.UseStageMarker(PipelineStage.Authenticate);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.