Asp.net mvc 无法使[Authorize(Role=";Administrator";)]属性正常工作
这问题我坚持了好几天,没有找到任何解决办法 首先,我用IdentityServer4创建了一个SSO服务器,这部分工作正常,我可以进行身份验证 其次,我将Asp.Net.Identy添加到SSO服务器,以管理用户和角色,并将所有这些都添加到持久存储中。这部分也行。我创建了用户和角色,并将用户附加到角色 第三,我创建了一个APS.NET MVC web应用程序,由我的SSO服务器保护,并使用OWIN。 我使用Authorize属性保护控制器,该控制器中的一个方法需要是管理员 问题是,当我尝试访问此方法时,系统进入一个循环,从SSO服务器来回查询ProfileService 现在,一些代码: 将ProfileService插入SSO服务器:Asp.net mvc 无法使[Authorize(Role=";Administrator";)]属性正常工作,asp.net-mvc,asp.net-identity,owin,identityserver4,identity,Asp.net Mvc,Asp.net Identity,Owin,Identityserver4,Identity,这问题我坚持了好几天,没有找到任何解决办法 首先,我用IdentityServer4创建了一个SSO服务器,这部分工作正常,我可以进行身份验证 其次,我将Asp.Net.Identy添加到SSO服务器,以管理用户和角色,并将所有这些都添加到持久存储中。这部分也行。我创建了用户和角色,并将用户附加到角色 第三,我创建了一个APS.NET MVC web应用程序,由我的SSO服务器保护,并使用OWIN。 我使用Authorize属性保护控制器,该控制器中的一个方法需要是管理员 问题是,当我尝试访问此
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;
}
}
}
当我单击调用此方法的页面中的链接时,上下文传递到服务器,但不包含要声明的角色:
如您所见,TequestedClaimType为空
另外,当我获得我的用户时,它没有附加角色列表:
同时,角色进入Pincipal对象的标识中:
我在这里和其他网站上读了很多帖子,但到目前为止,我还没有尝试解决我的问题
欢迎任何帮助
更新1:
我稍微更改了MVC客户端配置服务器端,向客户端添加角色,如下所示:
首先,我补充说:
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResource("roles", new[] { "role" })
};
}
在我的家庭控制器中,此代码始终返回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();
}
我最终通过从网络上收集不同的资源来实现它 当我与之斗争时,主要问题是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);
}
}
}
使用Owin;
使用Microsoft.Owin;
使用Microsoft.Owin.Security.Cookies;
使用制度;
使用System.IdentityModel.Tokens.Jwt;
使用Microsoft.Owin.Security.OpenIdConnect;
使用System.Security.Claims;
使用Microsoft.IdentityModel.Protocols.OpenIdConnect;
使用System.Linq;
使用Microsoft.Owin.Extensions;
使用Microsoft.Owin.Security.Authorization.Infrastructure;
使用IdentityModel;
使用System.Threading.Tasks;
使用System.Collections.Generic;
使用System.Net.Http;
使用IdentityModel.Client;
[程序集:OwinStartup(typeof(WebApplication1.Startup))]
命名空间WebApplication1
{
公营创业
{
公共无效配置(IAppBuilder应用程序)
{
//在visitez的votre应用程序配置上提供更多信息https://go.microsoft.com/fwlink/?LinkID=316888
app.UseAuthorization();
app.UseCookieAuthentication(新的CookieAuthenticationOptions()
{
AuthenticationType=“Cookies”,
ExpireTimeSpan=从秒开始的时间跨度(10),
slidengexpiration=true
});
var tokenHandler=new JwtSecurityTokenHandler();
tokenHandler.InboundClaimFilter.Clear();
app.UseOpenIdConnectAuthentication(新的OpenIdConnectAuthenticationOptions
{
AuthenticationType=“oidc”,
SignInAsAuthenticationType=“Cookies”,
权威=”https://localhost:5001“,//服务器地址识别服务器”
ClientId=“mvc”//nom du client au sens IdentityServer,doitêtre configural dans la liste des client du fichier config.cs du serveur
重定向URI=”https://localhost:44354/Home“,//重定向si登录确定是否在客户端服务器配置中列出重定向
PostLogoutRedirectUri=”https://localhost:44354/Home",
ResponseType=“id\u令牌”,
Resource=“Role”,
Scope=“openid profile web\u api角色”,
UseTokenLifetime=true,
通知=新的OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated=异步n=>
{
var索赔_至_排除=新[]
{
测试“/”aud“、”iss“、”nbf“、”exp“、”nonce“、”iat“、”at_hash”
};
列出要保留的索赔=
n、 AuthenticationTicket.Identity.Claims.Where(x=>false==Claims_to_exclude.Contains(x.Type)).ToList();
claims_to_keep.Add(新的claims(“id_token”,n.ProtocolMessage.IdToken));
if(n.ProtocolMessage.AccessToken!=null)
{
claims_to_keep.Add(新的claims(“access_token”,n.ProtocolMessage.AccessToken));
var client=新的HttpClient();
var disco=await client.GetDiscoveryDocumentAsync(“https://localhost:5001");
var userInfoResponse=wait client.GetUserInfoAsync(新UserInfoRequest{Address=disco.UserInfoEndpoint,Token=n.ProtocolMessage.AccessToken});
var userInfoClaims=userInfoResponse.Claims;//筛选子项,因为我们已经从id_令牌获取了它
claims_to_keep.AddRange(userInfoClaims);
}
var ci=新的索赔实体(
n、 AuthenticationTicket.Identity.AuthenticationType);
ci.添加索赔(索赔保留);
n、 AuthenticationTicket=新的Microsoft.Owin.Security.AuthenticationTicket(
ci,n.AuthenticationTicket.Properties
);
},
RedirectToIdentityProvider=n=>
{
if(n.ProtocolMessage.RequestType==OpenIdConnectRequestType.Logout)
{
var id_token=n.OwinContext。
[Authorize(Roles = "Administrator")]
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
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);
}
}
}
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = $"https://login.microsoftonline.com/{tenant}/v2.0",
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
NameClaimType = "name",
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}