.NET Core 在 AzuerAD 身份验证后添加声明
.NET Core add Claim after AzuerAD Authentication
我的应用程序通过 AzureAD 登录,但现在我需要从数据库中获取信息,然后将角色存储为声明。
所以我的问题是:如何在身份验证后将角色存储为声明?
这是我试过的:
var user = User as ClaimsPrincipal;
var identity = user.Identity as ClaimsIdentity;
identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
但是当我转到另一个控制器时,声明不再存在了?
谢谢
你可以在认证的时候实现,在OIDC中间件中,OnTokenValidated
给你机会修改传入的token中获取的ClaimsIdentity,下面的代码供你参考:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = ctx =>
{
//query the database to get the role
// add claims
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "Admin")
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
return Task.CompletedTask;
},
};
});
然后在 controller 中,你可以得到这样的声明:
var role = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
在OnTokenValidated
事件中,当用户不存在你的数据库时,你可以抛出特定的异常。然后 OnRemoteFailure
事件将用户重定向到该特定异常的特定操作方法
options.Events = new OpenIdConnectEvents()
{
OnTokenValidated = async context =>
{
// get email claim
var emailClaim = context.Principal.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Email);
UserEntity cu = null;
using (var accountService = context.HttpContext.RequestServices.GetService<IAccountService>())
{
cu = await accountService.Authorize(emailClaim.Value);
}
if (cu == null)
{
throw new UnauthorizedAccessException(string.Format("Could not find user for login '{0}' ", emailClaim.Value));
}
var newIdentity = new ClaimsIdentity(context.Principal.Identity.AuthenticationType);
// keep the id_token for logout
newIdentity.AddClaim(new Claim("id_token", context.ProtocolMessage.IdToken));
// add email claim
newIdentity.AddClaim(emailClaim);
// add email value as name claim
newIdentity.AddClaim(new Claim(ClaimTypes.Name, emailClaim.Value));
// add other claims here like roles
context.Properties.IsPersistent = true;
context.Properties.ExpiresUtc = DateTime.UtcNow.AddHours(3);
// overwrite existing authentication ticket
context.Principal = new ClaimsPrincipal(newIdentity);
},
OnRedirectToIdentityProviderForSignOut = async context =>
{
var idTokenHint = context.HttpContext?.User?.FindFirst("id_token");
if (idTokenHint != null)
context.ProtocolMessage.IdTokenHint = idTokenHint.Value;
await Task.FromResult(0);
},
OnRemoteFailure = async context =>
{
if (context.Failure is UnauthorizedAccessException)
{
context.Response.Redirect("/Account/UnAuthorized");
}
else
{
context.Response.Redirect("/Account/Error");
}
context.HandleResponse();
await Task.FromResult(0);
}
};
AccontController.cs
public class AccountController : Controller
{
[AllowAnonymous]
public IActionResult UnAuthorized()
{
HttpContext.Session.Clear();
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(HttpContext, CookieAuthenticationDefaults.AuthenticationScheme);
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(HttpContext, OpenIdConnectDefaults.AuthenticationScheme);
}
}
我的应用程序通过 AzureAD 登录,但现在我需要从数据库中获取信息,然后将角色存储为声明。
所以我的问题是:如何在身份验证后将角色存储为声明?
这是我试过的:
var user = User as ClaimsPrincipal;
var identity = user.Identity as ClaimsIdentity;
identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
但是当我转到另一个控制器时,声明不再存在了?
谢谢
你可以在认证的时候实现,在OIDC中间件中,OnTokenValidated
给你机会修改传入的token中获取的ClaimsIdentity,下面的代码供你参考:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = ctx =>
{
//query the database to get the role
// add claims
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "Admin")
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
return Task.CompletedTask;
},
};
});
然后在 controller 中,你可以得到这样的声明:
var role = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
在OnTokenValidated
事件中,当用户不存在你的数据库时,你可以抛出特定的异常。然后 OnRemoteFailure
事件将用户重定向到该特定异常的特定操作方法
options.Events = new OpenIdConnectEvents()
{
OnTokenValidated = async context =>
{
// get email claim
var emailClaim = context.Principal.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Email);
UserEntity cu = null;
using (var accountService = context.HttpContext.RequestServices.GetService<IAccountService>())
{
cu = await accountService.Authorize(emailClaim.Value);
}
if (cu == null)
{
throw new UnauthorizedAccessException(string.Format("Could not find user for login '{0}' ", emailClaim.Value));
}
var newIdentity = new ClaimsIdentity(context.Principal.Identity.AuthenticationType);
// keep the id_token for logout
newIdentity.AddClaim(new Claim("id_token", context.ProtocolMessage.IdToken));
// add email claim
newIdentity.AddClaim(emailClaim);
// add email value as name claim
newIdentity.AddClaim(new Claim(ClaimTypes.Name, emailClaim.Value));
// add other claims here like roles
context.Properties.IsPersistent = true;
context.Properties.ExpiresUtc = DateTime.UtcNow.AddHours(3);
// overwrite existing authentication ticket
context.Principal = new ClaimsPrincipal(newIdentity);
},
OnRedirectToIdentityProviderForSignOut = async context =>
{
var idTokenHint = context.HttpContext?.User?.FindFirst("id_token");
if (idTokenHint != null)
context.ProtocolMessage.IdTokenHint = idTokenHint.Value;
await Task.FromResult(0);
},
OnRemoteFailure = async context =>
{
if (context.Failure is UnauthorizedAccessException)
{
context.Response.Redirect("/Account/UnAuthorized");
}
else
{
context.Response.Redirect("/Account/Error");
}
context.HandleResponse();
await Task.FromResult(0);
}
};
AccontController.cs
public class AccountController : Controller
{
[AllowAnonymous]
public IActionResult UnAuthorized()
{
HttpContext.Session.Clear();
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(HttpContext, CookieAuthenticationDefaults.AuthenticationScheme);
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignOutAsync(HttpContext, OpenIdConnectDefaults.AuthenticationScheme);
}
}