asp.net-mvc-4 - MVC WebApi + 基础认证 + JSONP跨域

标签 asp.net-mvc-4 asp.net-web-api jsonp basic-authentication

我正在尝试创建一个基于 MVC 的网站,通过 WebApi 向本地页面和外部客户端提供一些服务,因此需要 JSONP 来避免同源策略错误。问题是该网站正在使用基本身份验证,据我从其他帖子中了解到,该身份验证无法使用 JSONP。我尝试按照帖子 How do I make a JSONP call with JQuery with Basic Authentication? 中的建议在 URL 中注入(inject) user:pass ,但这不起作用,服务器返回未经授权的代码。另外,我尝试在不注入(inject)的情况下进行调用,因为我可以在浏览器中输入用户名和密码:浏览器按预期要求我提供凭据,但随后由于某种原因它们被拒绝,再次以未经授权的代码结束。然而,凭据是好的,因为我可以通过从同一域成功运行完全相同的代码来确认。谁能告诉我我的代码有什么问题吗?

我的 MVC WebApi Controller 操作如下:

[BasicAuthorize(Roles = "administrator,customer,trial")]
public class TextApiController : ApiController
{
      // ...

    public SomeResult Get([FromUri] SomeParams p)
    {
          // some processing which returns a SomeResult object
          //...
    }
}

其中BasicAuthorize属性是我从http://kevin-junghans.blogspot.it/2013/02/mixing-forms-authentication-basic.html修改而来的类,如下:

[AttributeUsageAttribute(AttributeTargets.Class |
    AttributeTargets.Method, Inherited = true,
    AllowMultiple = true)]
public sealed class BasicAuthorizeAttribute : AuthorizeAttribute
{
    static private string DecodeFrom64(string sEncodedData)
    {
        byte[] encodedDataAsBytes = Convert.FromBase64String(sEncodedData);
        return Encoding.ASCII.GetString(encodedDataAsBytes);
    }

    static private bool GetUserNameAndPassword(HttpActionContext context,
        out string sUserName,
        out string sPassword,
        out bool bCookieAuthorization)
    {
        bCookieAuthorization = false;
        bool bSuccess = false;
        sUserName = sPassword = "";
        IEnumerable<string> headerVals;

        if (context.Request.Headers.TryGetValues("Authorization", out headerVals))
        {
            try
            {
                string sAuthHeader = headerVals.First();
                string[] authHeaderTokens = sAuthHeader.Split();

                if (authHeaderTokens[0].Contains("Basic"))
                {
                    string sDecoded = DecodeFrom64(authHeaderTokens[1]);
                    string[] aPairMembers = sDecoded.Split(new[] { ':' });
                    sUserName = aPairMembers[0];
                    sPassword = aPairMembers[1];
                } 
                else
                {
                    if (authHeaderTokens.Length > 1)
                        sUserName = DecodeFrom64(authHeaderTokens[1]);
                    bCookieAuthorization = true;
                } 

                bSuccess = true;
            }
            catch
            {
                bSuccess = false;
            }
        } 

        return bSuccess;
    }

    static private bool Authenticate(HttpActionContext actionContext,
        out string sUserName)
    {
        bool bIsAuthenticated = false;
        string sPassword;
        bool bCookieAuthorization;

        if (GetUserNameAndPassword(actionContext,
            out sUserName, out sPassword, out bCookieAuthorization))
        {
            // if the header tells us we're using Basic auth then log the user in
            if (!bCookieAuthorization)
            {
                if (WebSecurity.Login(sUserName, sPassword, true))
                    bIsAuthenticated = true;
                else
                    WebSecurity.Logout();
            } 
            // else get authentication from web security
            else
            {
                if (WebSecurity.IsAuthenticated) bIsAuthenticated = true;
                sUserName = WebSecurity.CurrentUserName;
            } 
        } 
        else actionContext.Response =
            new HttpResponseMessage(HttpStatusCode.BadRequest);

        return bIsAuthenticated;
    }

    private bool IsAuthorized(string sUserName)
    {
        SimpleRoleProvider roles =
            (SimpleRoleProvider)System.Web.Security.Roles.Provider;
          string[] aRoles = Roles.Split(new[] {','});

        return (aRoles.Any(sRole => roles.IsUserInRole(sUserName, sRole)));
    }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        string sUserName;

        if (Authenticate(actionContext, out sUserName))
        {
            if (!IsAuthorized(sUserName))
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
        } 
        else
        {
            actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        } 
    }
}

我的客户端代码是一个简单的 HTML 页面,带有一些 Javascript(使用 jQuery),例如:

...
<form>
    <fieldset>
        <legend></legend>
        <ol>
            <li>
                input text
                <input type="text" id="input"/>
            </li>
            <li>
                username
                <input type="text" id="user"/>
            </li>
            <li>
                password
                <input type="password" id="password"/>
            </li>
            <li><a href="#" id="apip">API: JSONP</a></li>
        </ol>
    </fieldset>
</form>
<div id="result"></div>
<script>
    function getAuthorizationHeader(username, password) {
        "use strict";
        var authType;

        if (password == "") {
            authType = "Cookie " + $.base64.encode(username);
        }
        else {
            var up = $.base64.encode(username + ":" + password);
            authType = "Basic " + up;
        };
        return authType;
    };

    function ajaxSuccessHandler(data) {
        "use strict";
        $("#result").text(data);
    };

    function ajaxErrHandler(jqXHR, textStatus, errorThrown) {
        "use strict";
        $("#result").text(errorThrown + " : " + textStatus);
    }

    $(function () {
        "use strict";

        $("#apip").click(function () {
            "use strict";
            var text = $("#input").val();
            $.ajax({
                url: "https://somesiteurl.com/api/wordapi?Text=" + encodeURIComponent(text),
                dataType: "jsonp",
                type: "GET",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("Authorization", getAuthorizationHeader($("#user").val(), $("#password").val()));
                },
                success: ajaxSuccessHandler,
                error: ajaxErrHandler
            });
        });
    });
</script>

跨域资源共享

抱歉回复晚了...我正在按照建议尝试使用 CORS,但我肯定错过了一些明显的东西,因为我的客户发送的 header 不包含 Origin。这是我所做的,使用 http://brockallen.com/2012/06/28/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel/ 中的库:

  1. 我创建了一个新的 MVC4 互联网应用程序来测试此场景,并使用 NuGet 添加 Thinktecture.IdentityModel。

  2. 在 App_Start 中我创建了这个 CorsConfig 类:

static public class CorsConfig
{
    public static void RegisterCorsForWebApi(HttpConfiguration httpConfig)
    {
        WebApiCorsConfiguration corsConfig = new WebApiCorsConfiguration();<p></p>

<pre><code>    // this adds the CorsMessageHandler to the HttpConfiguration’s 
    // MessageHandlers collection
    corsConfig.RegisterGlobal(httpConfig);

    corsConfig
        .ForResources("Products")
        .ForOrigins("http://hello.net")
        .AllowAll();
}

public static void RegisterCorsForMvc(MvcCorsConfiguration corsConfig)
{
    corsConfig
        .ForResources("Products.GetProducts")
        .ForOrigins("http://hello.net")
        .AllowAll();
}
</code></pre>

<p>}
</p>

  • 在 Global.asax.cs 中我调用了该类的两个方法。

  • 在 web.config 中添加:

  • 我在 MVC Controller 中创建一个简单的操作方法,返回一些 JSON。我计划稍后在客户端正确调用调用后用 [Authorize] 进行装饰,这样我就可以测试身份验证(和授权、添加角色)。

  • 在 View 中,我将我的方法称为:

  • var text = $("#input").val();
    var json = "{'text': " + JSON.stringify(text) + "}";
    $.ajax({
        url: "/Home/GetSomeJson",
        dataType: "json",
        data: json,
        type: "GET",
        beforeSend: function (xhr) {
            xhr.withCredentials = true;
        },
        crossDomain: true,
        username: $("#user").val(),
        password: $("#password").val(),
        success: ajaxSuccessHandler,
        error: ajaxErrHandler
    });
    
    然而,检查标题我发现没有来源。另外,这是为 MVC 操作/WebApi 的 CORS 调用传递凭据(当然在现实世界中这将是 HTTPS)的正确方法吗?

    最佳答案

    你为什么不CORS启用您的 Web api 服务而不是使用 JSONP? 。 This是一篇很棒的文章,解释了如何在 Web API 中启用 CORS 支持

    关于asp.net-mvc-4 - MVC WebApi + 基础认证 + JSONP跨域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15300696/

    相关文章:

    c# - 如何从 Web API 传递 pdf 并从 MVC Controller 读取 pdf?

    javascript - 在客户端 js 上使用来自第三方的跨源 JSON?

    c# - 使用 FormCollection 中的数据发出异步 HttpClient 发布请求

    c# - MVC 图表仅显示文本流

    c# - Entity Framework 中的 NoLock

    javascript - Firefox 无法读取获取响应 header

    c# - 在 Entity Framework 中使用数学函数和 mysql

    c# - Web API Controller 返回任务并不总是等待任务完成(puppeteer-sharp)

    javascript - 使用来自 angularjs 的 header 调用 JSONP

    javascript - 如何在等待多个异步 JSONP 回调时显示加载消息?