有没有办法松散地耦合这个基本的身份验证过滤器
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
我有一个 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