c# - 使用 Windows 身份验证生成 JWT 时授权属性不起作用
问题描述
背景
我有一个继承的 .NET 框架 v4.6.1 MVC Web 应用程序。身份验证是 Windows 身份验证,我们无权添加 IdentityServer 或 Azure AD。
我们将在 6 个月内重写整个应用程序,但现在我们需要实现一个需要生成 JWT 并将其应用于特定控制器/操作的功能。
问题
我已经实现了自己的 OAuth 授权服务器,但是当我使用该[Authorize]
属性装饰控制器的操作时,HTTP 请求会在没有任何拦截的情况下执行该操作。
代码
Startup.cs
[assembly: OwinStartup(typeof(SomeNamespace.Startup))]
namespace SomeNamespace
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
//removed for brevity...
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new []
{
ConfigurationManager.AppSettings["Jwt-Audience"]
},
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = ConfigurationManager.AppSettings["Jwt-Issuer"],
ValidAudience = ConfigurationManager.AppSettings["Jwt-Audience"],
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(ConfigurationManager.AppSettings["Jwt-SigningKey"]))
}
});
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth2/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(30),
Provider = new MyOAuthProvider(),
AccessTokenFormat = new MyJwtFormat(ConfigurationManager.AppSettings["Jwt-Issuer"])
});
}
}
public class MyJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private static readonly byte[] _secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["Jwt-SigningKey"]);
private readonly string _issuer;
public MyJwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var securityKey = new SymmetricSecurityKey(_secret);
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(_issuer, ConfigurationManager.AppSettings["Jwt-Audience"], data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingCredentials));
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}
public class MyOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
//Owin doesn't seem to have any knowledge of my DbContext
var user = context.OwinContext.Get<MyContext>(null).StaffMembers.AsNoTracking().FirstOrDefault(x => x.Username == context.OwinContext.Authentication.User.Identity.Name);
//temp hack to get the user from the DB
user = new MyContext().StaffMembers.AsNoTracking().FirstOrDefault(x => x.Username == "someuser");
if (user == null)
{
context.SetError("invalid_grant", "The user is not a registered");
context.Rejected();
return Task.FromResult<object>(null);
}
var ticket = new AuthenticationTicket(SetClaimsIdentity(context, user), new AuthenticationProperties());
context.Validated(ticket);
return Task.FromResult<object>(null);
}
private static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context, StaffMember model)
{
var identity = new ClaimsIdentity("JWT");
var claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, model.StaffId.ToString()),
new Claim(ClaimTypes.Name, model.FullName),
new Claim(ClaimTypes.WindowsAccountName, model.Username),
//new Claim(ClaimTypes.Expiration, newExpiry.ToString("yyyy-MM-dd HH:mm:ss")),
};
identity.AddClaims(claims);
return identity;
}
}
Sample controller action
[System.Web.Mvc.HttpGet]
[System.Web.Mvc.Authorize]
public async Task<ActionResult> Something()
{
var result = await _service.SomeMethod();
return new JsonStringResult(result);
}
FilterConfig.cs
(尝试使用和不使用此过滤器注册)
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new ApplicationInsightsHandleErrorAtrribute());
//filters.Add(new AuthorizeAttribute());
}
}
解决方案
推荐阅读
- testing - 基于 API 的测试是黑盒测试还是灰盒测试?
- google-ads-api - Google Ads - 类别错误
- python - 在堆栈图的弹出窗口中打印图例
- python - 如何在 np.where 中使用表示条件的变量作为 pandas 中具有列表值的列?
- powershell - 暂停 Windows 更新长达 35 天,并通过 powershell 找出暂停更新的日期
- javascript - 如何使用字符串类型对象的键值对对象数组进行排序
- postgresql - 升级 postgres 10.6 到 12.3
- python - 在数据框python中解压字典值
- c# - 为什么 Grid 的 TapGesture 识别器在 UWP 的情况下响应缓慢,但在 Android 和 iOS 中运行良好?
- nestjs - NestJS/TypeORM 项目的物化视图