有没有办法松散地耦合这个基本的身份验证过滤器

Is there a way to loosely couple this basic authentication filter

我有一个 asp.net 网络 api,我想使用基本身份验证。有什么办法可以使这种松散耦合吗?我尝试了构造函数 DI,但我无法弄清楚如何将 Dbcontext 传递到 WebApiConfig。任何帮助将不胜感激。

这是我的界面:

public interface IUserValidate
    {
        bool Login(string username, string password);
    }

这是我的 class:

public class UserValidate : IUserValidate
    {

        //This method is used to check the user credentials
        public bool Login(string username, string password)
        {
            using (var context = new EPINMiddleWareAPIContext())
            {
                return context.Companies.Any(user =>
                    user.userName.Equals(username, StringComparison.OrdinalIgnoreCase)
                    && user.password == password);
            }

        }
    }

这是我的基本身份验证过滤器:

public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
    {

        private const string Realm = "My Realm";


        public override void OnAuthorization(HttpActionContext actionContext)
        {
            //If the Authorization header is empty or null
            //then return Unauthorized
            if (actionContext.Request.Headers.Authorization == null)
            {
                actionContext.Response = actionContext.Request
                    .CreateResponse(HttpStatusCode.Unauthorized);
                // If the request was unauthorized, add the WWW-Authenticate header 
                // to the response which indicates that it require basic authentication
                if (actionContext.Response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    actionContext.Response.Headers.Add("WWW-Authenticate",
                        string.Format("Basic realm=\"{0}\"", Realm));
                }
            }
            else
            {
                //Get the authentication token from the request header
                string authenticationToken = actionContext.Request.Headers
                    .Authorization.Parameter;
                //Decode the string
                string decodedAuthenticationToken = Encoding.UTF8.GetString(
                    Convert.FromBase64String(authenticationToken));
                //Convert the string into an string array
                string[] usernamePasswordArray = decodedAuthenticationToken.Split(':');
                //First element of the array is the username
                string username = usernamePasswordArray[0];
                //Second element of the array is the password
                string password = usernamePasswordArray[1];
                //call the login method to check the username and password
                UserValidate uv = new UserValidate();
                if (uv.Login(username, password))
                {
                    var identity = new GenericIdentity(username);
                    IPrincipal principal = new GenericPrincipal(identity, null);
                    Thread.CurrentPrincipal = principal;
                    if (HttpContext.Current != null)
                    {
                        HttpContext.Current.User = principal;
                    }
                }
                else
                {
                    actionContext.Response = actionContext.Request
                        .CreateResponse(HttpStatusCode.Unauthorized);
                }
            }
        }

    }

这是我的 WebApiConfig:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            config.Filters.Add(new BasicAuthenticationAttribute());
            // Web API routes
            config.MapHttpAttributeRoutes();
            //Registering GlobalExceptionHandler
            config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
            //Registering UnhandledExceptionLogger
            config.Services.Replace(typeof(IExceptionLogger), new UnhandledExceptionLogger());
            //Registering RequestResponseHandler
            config.MessageHandlers.Add(new RequestResponseHandler());
            //Validate Token
            //config.MessageHandlers.Add(new TokenValidationHandler());

            //Registering CustomExceptionFilter
            config.Filters.Add(new CustomExceptionFilter());

        }
    }

这是我的 Dbcontext:

public class EPINMiddleWareAPIContext : DbContext
    {

        public EPINMiddleWareAPIContext() : base("name=EPINMiddleWareAPIContext")
        {
        }

        public DbSet<InitiateRequest> InitiateRequests { get; set; }
        public DbSet<InitiateResponse> InitiateResponses { get; set; }
        public DbSet<Company> Companies { get; set; }
        public DbSet<ConfirmRequest> ConfirmRequests { get; set; }
        public DbSet<ConfirmResponse> ConfirmResponses { get; set; }
        public DbSet<GameBank> GameBanks { get; set; }
        public DbSet<GameCouponBank> GameCouponBanks { get; set; }

    }

这是我的 Ninject Web Common:

using EPINMiddleWareAPI.Controllers;

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(EPINMiddleWareAPI.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(EPINMiddleWareAPI.App_Start.NinjectWebCommon), "Stop")]

namespace EPINMiddleWareAPI.App_Start
{
    using System;
    using System.Web;

    using Microsoft.Web.Infrastructure.DynamicModuleHelper;

    using Ninject;
    using Ninject.Web.Common;
    using Models;
    using Ninject.Web.Common.WebHost;

    public static class NinjectWebCommon 
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();

        /// <summary>
        /// Starts the application
        /// </summary>
        public static void Start() 
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }

        /// <summary>
        /// Stops the application.
        /// </summary>
        public static void Stop()
        {
            bootstrapper.ShutDown();
        }

        /// <summary>
        /// Creates the kernel that will manage your application.
        /// </summary>
        /// <returns>The created kernel.</returns>
        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            try
            {
                kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
                kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
                RegisterServices(kernel);
                return kernel;
            }
            catch
            {
                kernel.Dispose();
                throw;
            }
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<EPINMiddleWareAPIContext>().ToSelf().InRequestScope();


        }        
    }
}

编辑: 我尝试根据 Nkosi 的解决方案按如下方式实现提供程序:

public class AuthenticationFilterProvider : System.Web.Http.Filters.IFilterProvider
    {
        private readonly Func<BasicAuthenticationAttribute> _authorizeViewFilterFactory;

        public AuthenticationFilterProvider(Func<BasicAuthenticationAttribute> authorizeViewFilterFactory)
        {
            this._authorizeViewFilterFactory = authorizeViewFilterFactory;
        }

        public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
        {
            if (!actionDescriptor.GetCustomAttributes<BasicAuthenticationAttribute>().Any())
                return Enumerable.Empty<FilterInfo>();

            return new[]
            {
                new FilterInfo(this._authorizeViewFilterFactory(), FilterScope.Action)
            };
        }
    }

并且此绑定到 ninject:

kernel.Bind<System.Web.Http.Filters.IFilterProvider>().To<AuthenticationFilterProvider>();

但基本身份验证不会触发。

重构UserValidate

public class UserValidate : IUserValidate {
    private EPINMiddleWareAPIContext context;

    public UserValidate(EPINMiddleWareAPIContext context) {
        this.context = context;
    }

    //This method is used to check the user credentials
    public bool Login(string username, string password) {
        return context.Companies.Any(user =>
            user.userName.Equals(username, StringComparison.OrdinalIgnoreCase)
            && user.password == password
        );
    }
}

如果您不打算使用属性来装饰任何东西,而只是打算在配置中使用它,那么可以重构它以使用带有工厂的构造函数注入来获取服务。

public class BasicAuthenticationAttribute : AuthorizationFilterAttribute {

    private const string Realm = "My Realm";
    readonly Func<IUserValidate> factory;

    public BasicAuthenticationAttribute(Func<IUserValidate> factory) {
        this.factory = factory;
    }

    public override void OnAuthorization(HttpActionContext actionContext) {
        //If the Authorization header is empty or null
        //then return Unauthorized
        if (actionContext.Request.Headers.Authorization == null) {
            actionContext.Response = actionContext.Request
                .CreateResponse(HttpStatusCode.Unauthorized);
            // If the request was unauthorized, add the WWW-Authenticate header 
            // to the response which indicates that it require basic authentication
            if (actionContext.Response.StatusCode == HttpStatusCode.Unauthorized) {
                actionContext.Response.Headers.Add("WWW-Authenticate",
                    string.Format("Basic realm=\"{0}\"", Realm));
            }
        } else {
            //Get the authentication token from the request header
            string authenticationToken = actionContext.Request.Headers
                .Authorization.Parameter;
            //Decode the string
            string decodedAuthenticationToken = Encoding.UTF8.GetString(
                Convert.FromBase64String(authenticationToken));
            //Convert the string into an string array
            string[] usernamePasswordArray = decodedAuthenticationToken.Split(':');
            //First element of the array is the username
            string username = usernamePasswordArray[0];
            //Second element of the array is the password
            string password = usernamePasswordArray[1];
            //call the login method to check the username and password
            IUserValidate uv = factory(); 
            if (uv.Login(username, password)) {
                var identity = new GenericIdentity(username);
                IPrincipal principal = new GenericPrincipal(identity, null);
                Thread.CurrentPrincipal = principal;
                if (HttpContext.Current != null) {
                    HttpContext.Current.User = principal;
                }
            } else {
                actionContext.Response = actionContext.Request
                    .CreateResponse(HttpStatusCode.Unauthorized);
            }
        }
    }
}

相应地注册您的服务

private static void RegisterServices(IKernel kernel) {
    kernel.Bind<EPINMiddleWareAPIContext>().ToSelf().InRequestScope();
    kernel.Bind<IUserValidate>().To<UserValidate>();
    kernel.Bind<BasicAuthenticationAttribute>().ToSelf();
}

您在配置中解析属性并将其添加到过滤器

public static class WebApiConfig {
    public static void Register(HttpConfiguration config) {

        var resolver = config.DependencyResolver; //Assuming one is set.
        var basicAuth = resolver.GetService(typeof(BasicAuthenticationAttribute)) as BasicAuthenticationAttribute;
        // Web API configuration and services
        config.Filters.Add(basicAuth);

        //...omitted for brevity