ASP.NET MVC 5 中的基本身份验证

Posted

技术标签:

【中文标题】ASP.NET MVC 5 中的基本身份验证【英文标题】:Basic authentication in ASP.NET MVC 5 【发布时间】:2013-12-07 07:17:44 【问题描述】:

在ASP.NET MVC5 中实现基本认证必须做哪些步骤?

我了解到 OWIN 不支持无 cookie 身份验证,那么基本身份验证一般可以吗?

这里需要自定义属性吗?我不确定这些属性是如何工作的。

【问题讨论】:

有时解决方案是在另一个问题上,有人已经在***上做过了,有完整的代码:***.com/questions/9043831/… Cookie 和身份验证不相关。一个可以使用另一个,但两者都不依赖于另一个。 【参考方案1】:

您可以使用自定义 ActionFilter 属性来使用这种简单而有效的机制:

public class BasicAuthenticationAttribute : ActionFilterAttribute

    public string BasicRealm  get; set; 
    protected string Username  get; set; 
    protected string Password  get; set; 

    public BasicAuthenticationAttribute(string username, string password)
    
        this.Username = username;
        this.Password = password;
    

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    
        var req = filterContext.HttpContext.Request;
        var auth = req.Headers["Authorization"];
        if (!String.IsNullOrEmpty(auth))
        
            var cred = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
            var user = new  Name = cred[0], Pass = cred[1] ;
            if (user.Name == Username && user.Pass == Password) return;
        
        filterContext.HttpContext.Response.AddHeader("WWW-Authenticate", String.Format("Basic realm=\"0\"", BasicRealm ?? "Ryadel"));
        /// thanks to eismanpat for this line: http://www.ryadel.com/en/http-basic-authentication-asp-net-mvc-using-custom-actionfilter/#comment-2507605761
        filterContext.Result = new HttpUnauthorizedResult();
    

它可用于将整个控制器置于基本身份验证之下:

[BasicAuthenticationAttribute("your-username", "your-password", 
    BasicRealm = "your-realm")]
public class HomeController : BaseController

   ...

或特定的 ActionResult:

public class HomeController : BaseController

    [BasicAuthenticationAttribute("your-username", "your-password", 
        BasicRealm = "your-realm")]
    public ActionResult Index() 
    
        ...
    

如果您需要更多信息,请查看我在该主题上写的 this blog post。

【讨论】:

这种技术对我有用,即使对于混合身份验证,例如同一网站中的基本和表单身份验证。 请注意,此属性不适用于 Web API - 最好使用过滤器 - weblog.west-wind.com/posts/2013/Apr/18/… 太棒了。这正是我所需要的! @MacakM :所有发出质询的身份验证方案都需要领域属性(不区分大小写)。领域值(区分大小写)与被访问服务器的规范根 URL 相结合,定义了保护空间。这些领域允许将服务器上的受保护资源划分为一组保护空间,每个保护空间都有自己的身份验证方案和/或授权数据库。 [来自 RFC 1945 (HTTP/1.0) 和 RFC 2617] 将此与表单身份验证相结合会导致问题:基本身份验证的 401 重定向到表单身份验证登录页面。为避免这种情况,只需在 filterContext.Result 行之前添加行:filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;。您需要 .NET 4.5 或更高版本。【参考方案2】:

您可以使用自定义属性来做到这一点。在开源项目SimpleSecurity 中有一个支持基础认证的自定义属性的实现,你可以download here。有一个参考应用程序来演示它是如何使用的。它最初是为与 MVC 4 中的 SimpleMembership 一起工作而开发的,最近是ported to use ASP.NET Identity in MVC 5。

【讨论】:

【参考方案3】:

我想修改 Darkseal 分享的答案,因为该代码存在重大安全漏洞。如前所述,当调用 res.End() 时,该操作过滤器实际上并没有终止请求。提示用户输入凭据,如果凭据不匹配,则返回 401 响应,但控制器操作仍在服务器端执行。您需要将 filterContext.Result 属性设置为某个值,以使请求正确终止而不继续执行操作方法。

这对我的情况尤其不利,因为我试图保护从第三方接收数据馈送的 Web 服务端点。正如所写,这个动作过滤器没有保护任何东西,因为数据仍在通过我的动作方法推送。

我的“快速修复”如下:

public class BasicAuthenticationAttribute : ActionFilterAttribute

    public string BasicRealm  get; set; 
    protected string Username  get; set; 
    protected string Password  get; set; 

    public BasicAuthenticationAttribute(string username, string password)
    
        this.Username = username;
        this.Password = password;
    

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    
        var req = filterContext.HttpContext.Request;
        var auth = req.Headers["Authorization"];
        if (!String.IsNullOrEmpty(auth))
        
            var cred = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
            var user = new  Name = cred[0], Pass = cred[1] ;
            if (user.Name == Username && user.Pass == Password) return;
        
        var res = filterContext.HttpContext.Response;
        res.AddHeader("WWW-Authenticate", String.Format("Basic realm=\"0\"", BasicRealm ?? "Ryadel"));
        filterContext.Result = new HttpUnauthorizedResult();
    

【讨论】:

@Darkseal 已经在主要答案中应用了“快速修复”,res.end() 的问题已被删除,替换为HttpUnauthorizedResult() 似乎添加 filterContext.Result = new HttpUnauthorizedResult(); 会导致 asp 将用户重定向到默认登录页面,而不是让身份验证弹出窗口出现。【参考方案4】:

HTTP 基本身份验证不需要 cookie。它基于 HTTP 请求中的 HEADER。标头名为Authorization,其值应该是用户名和密码组合成一个字符串“username:password”(全部采用base64编码)。

真诚地,我从未对 ASP.NET MVC 使用基本身份验证,但我使用 Web API 创建自定义属性(对于 WebAPI,您可以从 here 开始,对于 MVC,您可以从 here 开始)。

【讨论】:

但是对于 Mvc 应用程序,您需要存储在 Cookie 中。例如,每个请求都不会自动附加标头。客户端浏览器需要添加授权标头,但不会。 WebApi 不同,我们可以控制 HttpClient。 @harsimranb 自 90 年代中期以来,没有一个主流浏览器不继续添加授权标头。您可能在服务器端有错误。 @harsimranb WebApi 没有什么不同;客户端浏览器总是添加授权头,存储在cookie中与MVC无关,是一个选择【参考方案5】:

来自@Darkseal 的精彩回答。这是重新用于 ASP.NET Web API 的相同代码(MVC 的近亲)。相同的想法,命名空间和上下文类略有不同。以完全相同的方式将其添加到您的类和方法中。

using System.Web.Http.Controllers;
using System.Web.Http.Filters;

public class BasicAuthenticationAttribute : ActionFilterAttribute

    public string BasicRealm  get; set; 
    protected string Username  get; set; 
    protected string Password  get; set; 

    public BasicAuthenticationAttribute(string username, string password)
    
        Username = username;
        Password = password;
    

    public override void OnActionExecuting(HttpActionContext filterContext)
    
        var req = filterContext.Request;
        var auth = req.Headers.Authorization;
        if (auth?.Scheme == "Basic")
        
            var cred = Encoding.ASCII.GetString(Convert.FromBase64String(auth.Parameter)).Split(':');
            var user = new  Name = cred[0], Pass = cred[1] ;
            if (user.Name == Username && user.Pass == Password) return;
        
        filterContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        filterContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"0\"", BasicRealm ?? "YourRealmName"));
    

【讨论】:

在***.com/questions/23336204/…查看类似的答案【参考方案6】:

你可以在Nuget (AuthPackage) 上试用这个包 它使您能够轻松地将身份验证添加到您的 asp.net mvc。

    使用包管理器控制台安装包:

    Install-Package AuthPackage

    在 (appSettings) 中将连接字符串添加到您的 Web.config:

     <add key="connectionString" value="connectionStringHere" />
    

    您已准备好注册用户、登录、注销

示例:

 public async Task<ActionResult> SignIn()
    
        var context = System.Web.HttpContext.Current;
        AuthUser authUser = new AuthUser(context);
        await authUser.SignIn("waleedchayeb2@gmail.com", "123456");
        return RedirectToAction("Index", "Home");
    

您可以阅读文档here

【讨论】:

你应该提到你是包的作者【参考方案7】:

由于 Web.config 中的以下代码,我们的一个应用程序“意外”使用了基本身份验证:

<system.webServer>
    <modules>
        <remove name="FormsAuthentication" />
    </modules>
    ... other stuff
</system.webServer>

应用程序被配置为使用表单身份验证。 只要使用普通表单身份验证,就会弹出浏览器身份验证窗口。

【讨论】:

【参考方案8】:

Darkseal’s answer

[BasicAuthenticationAttribute("your-username", "your-password", 
    BasicRealm = "your-realm")]

有两个缺点: 名称和密码是硬编码的,它们只支持单个用户。

更灵活的解决方案应该支持存储在配置中的多个用户名/密码对。

Microsoft 描述了一个示例 https://gm/aspnet/samples/tree/main/samples/aspnet/WebApi/BasicAuthentication。

public abstract class BasicAuthenticationAttribute : Attribute, IAuthenticationFilter

在重载中

abstract Task<IPrincipal> AuthenticateAsync(string userName, string password,   
CancellationToken cancellationToken);   

您可以执行检查以查找标头中的用户名/密码是否存在于用户名/密码对的配置/秘密列表中

还可以创建执行基本身份验证的 HTTP 模块。您可以通过替换 CheckPassword 方法轻松插入 ASP.NET 成员资格提供程序。 https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/basic-authentication#basic-authentication-with-custom-membership

OWIN 实现示例https://github.com/scottbrady91/Blog-Example-Classes/tree/master/OwinBasicAuthentication/WebApi

.Net 核心中的可能实现在 https://github.com/mihirdilip/aspnetcore-authentication-basic

【讨论】:

以上是关于ASP.NET MVC 5 中的基本身份验证的主要内容,如果未能解决你的问题,请参考以下文章

使用 ASP.NET 5 MVC 6 Web API 进行 Cookie 身份验证

将特定域的 ASP.NET 模拟用户添加到 windows 身份验证

IIS 上 asp.net mvc Intranet Web 应用程序的基本身份验证与 Windows 身份验证

表单身份验证、ASP.NET MVC 和 WCF RESTful 服务

我们可以在不使用 ASP.Net Identity 的情况下使用 MVC 5 提供的 cookie 身份验证吗?

使用身份验证 ASP.NET MVC 中的用户