基于请求参数的 OWIN 中 JWT 的用户特定秘密
User specific secret for JWT in OWIN based on request parameter
我创建了一个示例身份服务。它从配置文件中读取一个密钥。
ConfigurationManager.AppSettings["as:AudienceSecret"]
我需要修改它以根据传入请求的正文参数 (form["CurrentUser"]
) 从数据库中读取秘密。我们该怎么做?
Startup.cs 配置
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
string issuer = ConfigurationManager.AppSettings["Issuer"];
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(issuer)
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
string issuer = ConfigurationManager.AppSettings["Issuer"];
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
CustomOAuthProvider 中的 GrantResourceOwnerCredentials
public override async Task
GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = ConfigurationManager.AppSettings["AllowedOrigin"];
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = null;
try
{
user = await userManager.FindAsync(context.UserName, context.Password);
}
catch (Exception ex)
{
string result = ex.Message;
string innerText = ex.InnerException.ToString();
}
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var form = await context.Request.ReadFormAsync();
var loggedinUserName = form["CurrentUser"];
string practice = null;
if (!String.IsNullOrWhiteSpace(loggedinUserName))
{
ApplicationUser loggedinUserObj = userManager.FindByName(loggedinUserName);
string loggedinUserID = loggedinUserObj == null ? "" : loggedinUserObj.Id;
if (loggedinUserID != null)
{
ProvidersBL providersBL = new ProvidersBL();
practice = providersBL.GetPracticeForUser(loggedinUserID);
}
}
practice = practice ?? "Undefined";
loggedinUserName = loggedinUserName ?? "Undefined";
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, loggedinUserName));
claims.Add(new Claim("Practice", practice));
var oAuthIdentity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
以下作品。不确定这是否是最佳解决方案。
注意:我后来决定不为不同的用户使用不同的密钥。所有用户的密钥都相同。但是作为声明,令牌中的每个用户都会插入一个用户特定的密钥。一旦用户使用一次,该密钥将在数据库中刷新。下一次,对于同一用户,令牌中的用户密钥将不同。
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer = string.Empty;
private ProvidersBL providerBL;
public CustomJwtFormat(string issuer)
{
_issuer = issuer;
providerBL = new ProvidersBL();
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string userName = String.Empty;
var userNameClaim = data.Identity.Claims.FirstOrDefault(claim => claim.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
if (userNameClaim != null)
{
userName = userNameClaim.Value;
}
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string symmetricKeyAsBase64 = string.Empty;
symmetricKeyAsBase64 = providerBL.GetKeyForUser(userName);
if (String.IsNullOrWhiteSpace(symmetricKeyAsBase64))
{
symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
}
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}
我创建了一个示例身份服务。它从配置文件中读取一个密钥。
ConfigurationManager.AppSettings["as:AudienceSecret"]
我需要修改它以根据传入请求的正文参数 (form["CurrentUser"]
) 从数据库中读取秘密。我们该怎么做?
Startup.cs 配置
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
string issuer = ConfigurationManager.AppSettings["Issuer"];
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(issuer)
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
string issuer = ConfigurationManager.AppSettings["Issuer"];
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
CustomOAuthProvider 中的 GrantResourceOwnerCredentials
public override async Task
GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = ConfigurationManager.AppSettings["AllowedOrigin"];
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = null;
try
{
user = await userManager.FindAsync(context.UserName, context.Password);
}
catch (Exception ex)
{
string result = ex.Message;
string innerText = ex.InnerException.ToString();
}
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var form = await context.Request.ReadFormAsync();
var loggedinUserName = form["CurrentUser"];
string practice = null;
if (!String.IsNullOrWhiteSpace(loggedinUserName))
{
ApplicationUser loggedinUserObj = userManager.FindByName(loggedinUserName);
string loggedinUserID = loggedinUserObj == null ? "" : loggedinUserObj.Id;
if (loggedinUserID != null)
{
ProvidersBL providersBL = new ProvidersBL();
practice = providersBL.GetPracticeForUser(loggedinUserID);
}
}
practice = practice ?? "Undefined";
loggedinUserName = loggedinUserName ?? "Undefined";
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, loggedinUserName));
claims.Add(new Claim("Practice", practice));
var oAuthIdentity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
以下作品。不确定这是否是最佳解决方案。
注意:我后来决定不为不同的用户使用不同的密钥。所有用户的密钥都相同。但是作为声明,令牌中的每个用户都会插入一个用户特定的密钥。一旦用户使用一次,该密钥将在数据库中刷新。下一次,对于同一用户,令牌中的用户密钥将不同。
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer = string.Empty;
private ProvidersBL providerBL;
public CustomJwtFormat(string issuer)
{
_issuer = issuer;
providerBL = new ProvidersBL();
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string userName = String.Empty;
var userNameClaim = data.Identity.Claims.FirstOrDefault(claim => claim.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
if (userNameClaim != null)
{
userName = userNameClaim.Value;
}
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string symmetricKeyAsBase64 = string.Empty;
symmetricKeyAsBase64 = providerBL.GetKeyForUser(userName);
if (String.IsNullOrWhiteSpace(symmetricKeyAsBase64))
{
symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
}
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}