ITKeyword,专注技术干货聚合推荐

注册 | 登录

asp.net mvc 4 - MVC WebApi + Basic Authentication + JSONP cross-domain

itPublisher 分享于

2020腾讯云双十一活动,全年最低!!!(领取3500元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1073

【阿里云】双十一活动,全年抄底价,限时3天!(老用户也有),
入口地址https://www.aliyun.com/1111/home

推荐:使用asp.net mvc3的Filter模拟实现http basic Authentication

近段时间,在编写一个 淘宝家园的手机客户端,需要一个配套的api提供服务,使用http basic验证来确认用户。在iis7上架了一个api的测试站点,iis默认的http basic

I'm trying to create an MVC-based site providing some services via WebApi, to both local pages and external clients, thus requiring JSONP to avoid the same origin policy error. The problem is that the site is using Basic authentication, which as I learnt from other posts here cannot play with JSONP. I tried to inject user:pass in the URL, as suggested in the post How do I make a JSONP call with JQuery with Basic Authentication?, but this does not work and the server returns an unauthorized code. Also, I tried just making the call without injecting, as I would be fine in entering username and password in the browser: the browser asks me for the credentials as expected, but then for some reason they are refused, ending into an unauthorized code again. Yet the credentials are OK, as I can confirm by successfully running exactly the same code from the same domain. Could anyone tell me what's wrong with my code?

My MVC WebApi controller action is like:

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

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

where the BasicAuthorize attribute is a class I modified from http://kevin-junghans.blogspot.it/2013/02/mixing-forms-authentication-basic.html, as follows:

[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);
        } 
    }
}

My client code is a simple HTML page with some Javascript (using jQuery), like:

...
<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

Sorry for the late response... I'm trying with CORS as suggested, but I'm surely missing something obvious as the headers sent by my client do not include Origin. Here is what I did, by using the library from http://brockallen.com/2012/06/28/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel/ :

  1. I create a new MVC4 internet application to test this scenario, and add Thinktecture.IdentityModel using NuGet.

  2. in App_Start I create this CorsConfig class:

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

    // 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();
}

}

推荐:ASP.NET MVC的WebApi使用

不多说,我直接使用代码,过程如下 1.创建一个继承ApiController的控制器     <span style="font-size:14px;">using System.Collections.Generic;using System.

  1. in Global.asax.cs I call both methods of this class.

  2. in web.config add:

  3. I create a simple action method in a MVC controller returning some JSON. I plan to decorate with [Authorize] later, once the call is placed correctly on the client side, so I can test the authentication (and authorization, adding Roles).

  4. in a view, I call my method like:

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
});
Yet, examining the headers I see no origin. Also, is this the correct way of passing credentials (of course in real world this would be HTTPS) for a CORS call to MVC action/WebApi?

asp.net-mvc-4 asp.net-web-api jsonp basic-authentication
|
  this question
edited Mar 26 '13 at 20:30 asked Mar 8 '13 at 18:20 Naftis 1,505 3 33 61

 | 

1 Answers
1

Why dont you CORS enable your web api service instead of using JSONP? . This is a great post explaining how to enable CORS support in Web API


|
  this answer
answered Mar 10 '13 at 20:00 Whizkid747 2,778 2 12 20

 | 

推荐:ASP.NET MVC (一)

从零开始学习ASP.NET MVC:开天辟地入门篇(一) 2009-2-27 20:11:26 已被阅读:1151 发表评论 一.摘要 和自身水平有关, 我总喜欢写入门级别的文章.比如虽然做项目


相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。