Предположим, у вас есть пользовательский RequireRoleViewAttribute
:
[AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
public class RequireRoleViewAttribute : Attribute
{
public string Role;
public RequireRoleViewAttribute(string role){
this.Role = role;
}
}
Как передать значения, основанные на запросе, этому распознавателю?
Вы можете добавить IServiceProvider
в свой пользовательский распознаватель:
public class RoleBasedContractResolver : DefaultContractResolver
{
public IServiceProvider ServiceProvider { get; }
public RoleBasedContractResolver( IServiceProvider sp)
{
this.ServiceProvider = sp;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var contextAccessor = this.ServiceProvider.GetRequiredService<IHttpContextAccessor>() ;
var context = contextAccessor.HttpContext;
var user = context.User;
// if you're using the Identity, you can get the userManager :
var userManager = context.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
// ...
}
}
Таким образом, мы можем получить HttpContext
и User
, как нам нравится. Если вы используете Identity, вы также можете получить службу и роли UserManager
.
и теперь мы можем последовать совету @ dbc , чтобы контролировать ShouldSerialize
:
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var contextAccessor = this.ServiceProvider.GetRequiredService<IHttpContextAccessor>() ;
var context = contextAccessor.HttpContext;
var user = context.User;
// if you use the Identitiy, you can get the usermanager
//UserManager<IdentityUser>
var userManager = context.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
JsonProperty property = base.CreateProperty(member, memberSerialization);
// get the attributes
var attrs=member.GetCustomAttributes<RequireRoleViewAttribute>();
// if no [RequireResoveView] decorated, always serialize it
if(attrs.Count()==0) {
property.ShouldDeserialize = instance => true;
return property;
}
// custom your logic to dertermine wether should serialize the property
// I just use check if it can statisify any the condition :
var roles = this.GetIdentityUserRolesAsync(context,userManager).Result;
property.ShouldSerialize = instance => {
var resource = new { /* any you need */ };
return attrs.Any(attr => {
var rolename = attr.Role;
return roles.Any(r => r == rolename ) ;
}) ? true : false;
};
return property;
}
Функция GetIdentityUserRolesAsync
здесь является вспомогательным методом для получения ролей с использованием текущей HttpContext
и службы UserManger
:
private async Task<IList<string>> GetIdentityUserRolesAsync(HttpContext context, UserManager<IdentityUser> userManager)
{
var rolesCached= context.Items["__userRoles__"];
if( rolesCached != null){
return (IList<string>) rolesCached;
}
var identityUser = await userManager.GetUserAsync(context.User);
var roles = await userManager.GetRolesAsync(identityUser);
context.Items["__userRoles__"] = roles;
return roles;
}
Как ввести IServiceProvider
в деталях:
Хитрость в том, как настроить значение по умолчанию MvcJwtOptions
с IServiceProvider
.
Не настраивайте JsonOptions
:
services.AddMvc().
.AddJsonOptions(o =>{
// o.
});
, поскольку это не позволяет нам добавить параметр IServiceProvider
.
Мы можем создать подкласс MvcJsonOptions
:
public class MyMvcJsonOptionsWrapper : IConfigureOptions<MvcJsonOptions>
{
IServiceProvider ServiceProvider;
public MyMvcJsonOptionsWrapper(IServiceProvider serviceProvider)
{
this.ServiceProvider = serviceProvider;
}
public void Configure(MvcJsonOptions options)
{
options.SerializerSettings.ContractResolver =new RoleBasedContractResolver(ServiceProvider);
}
}
и зарегистрируйте услуги по:
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// don't forget to add the IHttpContextAccessor
services.AddTransient<IConfigureOptions<MvcJsonOptions>,MyMvcJsonOptionsWrapper>();
Контрольный пример:
Допустим, у вас есть пользовательское POCO:
public class TestObject
{
public string Field1 => "NoRestrictions";
[RequireRoleView("Admin")]
public string Field2 => "ViewRequiresAdmin";
[RequireRoleView("HR"),RequireRoleView("OP")]
public string Field3 => "ViewRequiresHROrOP";
[RequireRoleView("IT"), RequireRoleView("HR")]
public string Field4 => "ViewRequiresITOrHR";
[RequireRoleView("IT"), RequireRoleView("OP")]
public string Field5 => "ViewRequiresITOrOP";
}
А у текущего пользователя есть роли: Admin
и HR
:
Результат будет:
{"Field1":"NoRestrictions","Field2":"ViewRequiresAdmin","Field3":"ViewRequiresHROrOP","Field4":"ViewRequiresITOrHR"}
Скриншот тестирования методом действия: