我有一个使用IdentityServer4混合身份验证流的ASP.Net Core应用程序项目。设置如下,
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = IdentityServerUrl;
options.RequireHttpsMetadata = false;
options.ClientId = ClientId;
options.ClientSecret = ClientSecret;
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("offline_access");
options.Scope.Add("ApiAuthorizedBasedOnIdentity");
options.GetClaimsFromUserInfoEndpoint = true;
options.TokenValidationParameters.NameClaimType = JwtClaimTypes.Name;
options.TokenValidationParameters.RoleClaimType = JwtClaimTypes.Role;
});
//Setup Tenant Role based authorization
services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
services.AddProxy();
}
我能够进行身份验证,并且SaveTokens = true成功将访问令牌保存在ASP.Net身份验证cookie中。现在,我需要从ASP.Net Core客户端项目中的Controller Action(不通过中间件)中向同一身份验证cookie添加自定义声明。例如,假设HomeController的Index操作。
我还需要此声明保留在身份验证Cookie中,以便它可以在请求和控制器操作之间持续存在。
我进行了一些挖掘,发现我可以使用ASP.Net Identity来完成
if (User.Identity.IsAuthenticated)
{
var claimsIdentity = ((ClaimsIdentity)User.Identity);
if (!claimsIdentity.HasClaim(c => c.Type == "your-claim"))
{
((ClaimsIdentity)User.Identity).AddClaim(new Claim("your-claim", "your-value"));
var appUser = await userManager.GetUserAsync(User).ConfigureAwait(false);
await signInManager.RefreshSignInAsync(appUser).ConfigureAwait(false);
}
}
身份验证由IdentityServer使用在该项目中设置的ASP.Net Identity完成。但是,要在客户端项目中使用SignInManager,UserManager等,我需要将ASP.Net Identity引入其中。设置ASP.Net身份并将其存储在客户端项目中,只是为了通过附加声明更新身份验证cookie似乎有些过头。还有其他方法吗?
答案 0 :(得分:2)
您当然不需要在客户端项目中包括ASP.NET Core Identity,但是您可以将其用于启发如何实现所需的内容。让我们首先来看一下RefreshSignInAsync
的实现:
public virtual async Task RefreshSignInAsync(TUser user)
{
var auth = await Context.AuthenticateAsync(IdentityConstants.ApplicationScheme);
var authenticationMethod = auth?.Principal?.FindFirstValue(ClaimTypes.AuthenticationMethod);
await SignInAsync(user, auth?.Properties, authenticationMethod);
}
从上面可以看出,它也调用SignInAsync
,如下所示:
public virtual async Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null)
{
var userPrincipal = await CreateUserPrincipalAsync(user);
// Review: should we guard against CreateUserPrincipal returning null?
if (authenticationMethod != null)
{
userPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.AuthenticationMethod, authenticationMethod));
}
await Context.SignInAsync(IdentityConstants.ApplicationScheme,
userPrincipal,
authenticationProperties ?? new AuthenticationProperties());
}
我们最感兴趣的两个电话是:
Context.AuthenticateAsync
,它会创建一个AuthenticateResult
,其中包含从Cookie读取的ClaimsPrincipal
和AuthenticationProperties
。Context.SignInAsync
,最终使用ClaimsPrincipal
和关联的AuthenticationProperties
重写cookie。 ASP.NET Core Identity创建一个全新的ClaimsPrincipal
,通常从数据库中提取该ClaimsPrincipal
以便“刷新”它。您不需要这样做,因为您只是想将现有 var authenticateResult = await HttpContext.AuthenticateAsync();
if (authenticateResult.Succeeded)
{
var claimsIdentity = (ClaimsIdentity)authenticateResult.Principal.Identity;
if (!claimsIdentity.HasClaim(c => c.Type == "your-claim"))
{
claimsIdentity.AddClaim(new Claim("your-claim", "your-value"));
await HttpContext.SignInAsync(authenticateResult.Principal, authenticateResult.Properties);
}
}
与附加声明一起使用。这是满足您要求的完整解决方案:
HttpContext.AuthenticateAsync
对"Cookies"
的调用将使用您已在配置(ClaimsPrincipal
)中设置的默认方案来同时访问AuthenticationProperties
和HttpContext.SignInAsync
。之后,仅是添加新声明并执行对"Cookies"
的调用的情况,该调用还将使用默认方案(library(dplyr)
library(fuzzywuzzyR)
set.seed(42)
rm(list = ls())
options(scipen = 999)
init = FuzzMatcher$new()
data <- data.frame(string = c("hello world", "hello vorld", "hello world 1", "hello world", "hello world hello world"))
data$string <- as.character(data$string)
distance_function <- function(string_1, string_2) {
init$Token_set_ratio(string1 = string_1, string2 = string_2)
}
combinations <- combn(nrow(data), 2)
distances <- matrix(, nrow = 1, ncol = ncol(combinations))
distance_matrix <- matrix(NA, nrow = nrow(data), ncol = nrow(data), dimnames = list(data$string, data$string))
for (i in 1:ncol(combinations)) {
distance <- distance_function(data[combinations[1, i], 1], data[combinations[2, i], 1])
#print(data[combinations[1, i], 1])
#print(data[combinations[2, i], 1])
#print(distance)
distance_matrix[combinations[1, i], combinations[2, i]] <- distance
distance_matrix[combinations[2, i], combinations[1, i]] <- distance
}
distance_matrix
)。
答案 1 :(得分:0)
如果您的项目中没有UserStore,而我只是在使用UserManager,则可以通过继承UserManager并覆盖GetClaimsAsync方法来轻松实现
public override Task<IList<Claim>> GetClaimsAsync(T user)
{
// here you can return a list of claims and it will be in the cookie
return base.GetClaimsAsync(user);
}
通过base.GetClaimsAsync(user);的默认实现;就是调用相关的UserStore。因此,如果您没有UserManager的UserStore,则可以将其删除。
答案 2 :(得分:-1)
选项1
您将需要使用HttpContext.SignInAsync
方法。另外,向用户添加其他声明后,您需要用新的HttpContext.User
更新ClaimsPrincipal
。请参见下面的代码:
var identity = (ClaimsIdentity)User.Identity;
identity.AddClaim(new Claim("your-claim", "your-value"));
// genereate the new ClaimsPrincipal
var claimsPrincipal = new ClaimsPrincipal(identity);
// store the original tokens in the AuthenticationProperties
var props = new AuthenticationProperties();
// get the current tokens
var accessToken = await HttpContext.GetTokenAsync("access_token");
var refreshToken = await HttpContext.GetTokenAsync("refresh_token");
// create the enumerable list
var tokens = new List<AuthenticationToken>
{
new AuthenticationToken {Name = "access_token", Value = accessToken},
new AuthenticationToken {Name = "refresh_token", Value = refreshToken}
};
//store the tokens
props.StoreTokens(tokens);
// update the thread's current principal as it is changed, otherwise
// System.Security.Claims.ClaimsPrincipal.Current is referring to the
// ClaimsPrincipal created from the cookie on the initial request. This is required
// so that the next instance of HttpContext will be injected with the updated claims
HttpContext.User = claimsPrincipal;
Thread.CurrentPrincipal = claimsPrincipal;
// sign in using the built-in Authentication Manager and ClaimsPrincipal
// this will create a cookie as defined in CookieAuthentication middleware
await HttpContext.SignInAsync("your scheme", claimsPrincipal, props);
请确保使用您使用的方案名称替换“您的方案”。希望对您有所帮助。
选项2
@Ruard van Elburg正确指出,上述解决方案将覆盖访问令牌。 (已更新为允许存储原始令牌)
如果您是在用户登录后立即添加声明,则可以使用OnTokenValidated
事件
.AddOpenIdConnect("oidc", options =>
{
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = async ctx =>
{
var claim = new Claim("your-claim", "your-value");
var identity = new ClaimsIdentity(new[] { claim });
ctx.Principal.AddIdentity(identity);
await Task.CompletedTask;
}
};
}