首页 > 解决方案 > 使用 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());
    }
}

标签: c#asp.net-mvcoauthjwtaccess-token

解决方案


推荐阅读