跨请求保留新声明
Persisting new claims across requests
如何添加新声明,使它们在 cookie 过期之前通过请求持续存在?
我正在使用 OWIN 中间件,本地身份验证对登录系统的用户进行身份验证。
登录部分成功,我在ws-federation提供的user claims中添加了Roles,帮助授权用户进行某些操作方法。
在登录时,在控制器中,我写了以下内容来添加角色:
string[] roles = { "Role1", "Role2" };
var identity = new ClaimsIdentity(User.Identity);
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
var authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant
(new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true });
但是当我在下一个请求中检查声明时,我没有看到角色声明。
我相信您在成功验证后添加了自定义声明(通常在成功验证后添加到某些事件处理程序)。现在,为了在后续请求中保留该信息,您需要在管道中进行身份验证之前使用 CookieAuthentication 中间件。
工作原理:
在第一次成功验证并添加自定义声明后,声明将转换为某种身份验证 cookie 并发送回客户端。后续请求将携带此 auth cookie。 CookieAuthentication 中间件在查找身份验证 cookie 时会设置您的 Thread.CurrentPriciple 从 cookie 获得的声明。
在第一次请求期间,当 cookie 中间件确实看到任何 auth cookie 时,它会将请求传递给管道中的下一个中间件(在您的情况下为 Authentication owin)以挑战用户登录。
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies",
AuthenticationMode= AuthenticationMode.Active,
CookieName="XXXXX",
CookieDomain= _cookiedomain,
/* you can go with default cookie encryption also */
TicketDataFormat = new TicketDataFormat(_x509DataProtector),
SlidingExpiration = true,
CookieSecure = CookieSecureOption.Always,
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = _clientID,
Authority = _authority,
RedirectUri = _redirectUri,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = SecurityTokenValidated,
AuthenticationFailed = (context) =>
{
/* your logic to handle failure*/
}
},
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidIssuers = _validIssuers,
ValidateIssuer = _isValidIssuers,
}
});
编辑:(附加信息)
几乎与上面完全相同的代码也适用于 ws federation,具有相同的逻辑和所有内容。
SecurityTokenValidated = notification =>
{
ClaimsIdentity identity = notification.AuthenticationTicket.Identity;
string[] roles = { "Role1", "Role2" };
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
return Task.FromResult(0);
}
您需要使用与 Startup.ConfigureAuth
相同的 AuthenticationType
。例如:
在Startup.ConfigureAuth
中:
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
//....
});
并在您的登录代码中(在问题中提供):
var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
或者确保 User.Identity
具有相同的 AuthenticationType
,并且您可以像以前那样使用它:
var identity = new ClaimsIdentity(User.Identity);
现在重要的部分是对于登录,您应该在使用之前添加声明,而不是之后。像这样:
HttpContext.GetOwinContext().Authentication.SignIn(identity);
您可以在登录后添加声明,但是您将在创建cookie后立即修改它,效率不高。如果在其他一些代码中您需要修改声明,那么您可以使用与您的代码类似的东西,但您必须从 Current
:
获取上下文
HttpContext.Current.GetOwinContext().Authentication.AuthenticationResponseGrant =
new AuthenticationResponseGrant(new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true });
所以你可以通过像上面那样简单地添加 Current
来修复你的代码,但这对于登录代码来说效率不高,最好将声明传递给 SignIn
函数。
你可以在WEB中进行如下操作API C#(SOAP),(STORED PROCEDURES)
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
LoginModel model = new LoginModel();
//validate user credentials and obtain user roles (return List Roles)
//validar las credenciales de usuario y obtener roles de usuario
var user = model.User = _serviceUsuario.ObtenerUsuario(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "El nombre de usuario o la contraseña no son correctos.cod 01");
return;
}
var stringRoles = user.Roles.Replace(" ", "");//It depends on how you bring them from your DB
string[] roles = stringRoles.Split(',');//It depends on how you bring them from your DB
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
foreach(var Rol in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, Rol));
}
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Email, user.Correo));
identity.AddClaim(new Claim(ClaimTypes.MobilePhone, user.Celular));
identity.AddClaim(new Claim("FullName", user.FullName));//new ClaimTypes
identity.AddClaim(new Claim("Empresa", user.Empresa));//new ClaimTypes
identity.AddClaim(new Claim("ConnectionStringsName", user.ConnectionStringsName));//new ClaimTypes
//add user information for the client
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
{ "userName",user.NombreUsuario },
{ "FullName",user.FullName },
{ "EmpresaName",user.Empresa }
});
//end
var ticket = new AuthenticationTicket(identity, properties);
context.Validated(ticket);
}
如何添加新声明,使它们在 cookie 过期之前通过请求持续存在?
我正在使用 OWIN 中间件,本地身份验证对登录系统的用户进行身份验证。
登录部分成功,我在ws-federation提供的user claims中添加了Roles,帮助授权用户进行某些操作方法。 在登录时,在控制器中,我写了以下内容来添加角色:
string[] roles = { "Role1", "Role2" };
var identity = new ClaimsIdentity(User.Identity);
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
var authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant
(new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true });
但是当我在下一个请求中检查声明时,我没有看到角色声明。
我相信您在成功验证后添加了自定义声明(通常在成功验证后添加到某些事件处理程序)。现在,为了在后续请求中保留该信息,您需要在管道中进行身份验证之前使用 CookieAuthentication 中间件。
工作原理:
在第一次成功验证并添加自定义声明后,声明将转换为某种身份验证 cookie 并发送回客户端。后续请求将携带此 auth cookie。 CookieAuthentication 中间件在查找身份验证 cookie 时会设置您的 Thread.CurrentPriciple 从 cookie 获得的声明。
在第一次请求期间,当 cookie 中间件确实看到任何 auth cookie 时,它会将请求传递给管道中的下一个中间件(在您的情况下为 Authentication owin)以挑战用户登录。
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies",
AuthenticationMode= AuthenticationMode.Active,
CookieName="XXXXX",
CookieDomain= _cookiedomain,
/* you can go with default cookie encryption also */
TicketDataFormat = new TicketDataFormat(_x509DataProtector),
SlidingExpiration = true,
CookieSecure = CookieSecureOption.Always,
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = _clientID,
Authority = _authority,
RedirectUri = _redirectUri,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = SecurityTokenValidated,
AuthenticationFailed = (context) =>
{
/* your logic to handle failure*/
}
},
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidIssuers = _validIssuers,
ValidateIssuer = _isValidIssuers,
}
});
编辑:(附加信息)
几乎与上面完全相同的代码也适用于 ws federation,具有相同的逻辑和所有内容。
SecurityTokenValidated = notification =>
{
ClaimsIdentity identity = notification.AuthenticationTicket.Identity;
string[] roles = { "Role1", "Role2" };
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
return Task.FromResult(0);
}
您需要使用与 Startup.ConfigureAuth
相同的 AuthenticationType
。例如:
在Startup.ConfigureAuth
中:
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
//....
});
并在您的登录代码中(在问题中提供):
var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
或者确保 User.Identity
具有相同的 AuthenticationType
,并且您可以像以前那样使用它:
var identity = new ClaimsIdentity(User.Identity);
现在重要的部分是对于登录,您应该在使用之前添加声明,而不是之后。像这样:
HttpContext.GetOwinContext().Authentication.SignIn(identity);
您可以在登录后添加声明,但是您将在创建cookie后立即修改它,效率不高。如果在其他一些代码中您需要修改声明,那么您可以使用与您的代码类似的东西,但您必须从 Current
:
HttpContext.Current.GetOwinContext().Authentication.AuthenticationResponseGrant =
new AuthenticationResponseGrant(new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true });
所以你可以通过像上面那样简单地添加 Current
来修复你的代码,但这对于登录代码来说效率不高,最好将声明传递给 SignIn
函数。
你可以在WEB中进行如下操作API C#(SOAP),(STORED PROCEDURES)
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
LoginModel model = new LoginModel();
//validate user credentials and obtain user roles (return List Roles)
//validar las credenciales de usuario y obtener roles de usuario
var user = model.User = _serviceUsuario.ObtenerUsuario(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "El nombre de usuario o la contraseña no son correctos.cod 01");
return;
}
var stringRoles = user.Roles.Replace(" ", "");//It depends on how you bring them from your DB
string[] roles = stringRoles.Split(',');//It depends on how you bring them from your DB
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
foreach(var Rol in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, Rol));
}
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Email, user.Correo));
identity.AddClaim(new Claim(ClaimTypes.MobilePhone, user.Celular));
identity.AddClaim(new Claim("FullName", user.FullName));//new ClaimTypes
identity.AddClaim(new Claim("Empresa", user.Empresa));//new ClaimTypes
identity.AddClaim(new Claim("ConnectionStringsName", user.ConnectionStringsName));//new ClaimTypes
//add user information for the client
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
{ "userName",user.NombreUsuario },
{ "FullName",user.FullName },
{ "EmpresaName",user.Empresa }
});
//end
var ticket = new AuthenticationTicket(identity, properties);
context.Validated(ticket);
}