การเรียก webapi ที่ไม่ได้รับอนุญาตส่งคืนหน้าเข้าสู่ระบบแทน 401


181

ฉันจะกำหนดค่าโครงการ mvc / webapi ของฉันอย่างไรเพื่อให้วิธีการ webapi ที่เรียกจากมุมมองมีดโกนไม่ส่งคืนหน้าล็อกอินเมื่อไม่ได้รับอนุญาต

มันเป็นแอปพลิเคชั่น MVC5 ซึ่งมีตัวควบคุม WebApi สำหรับการโทรผ่าน javascript

ทั้งสองวิธีด้านล่าง

[Route("api/home/LatestProblems")]      
[HttpGet()]
public List<vmLatestProblems> LatestProblems()
{
    // Something here
}

[Route("api/home/myLatestProblems")]
[HttpGet()]
[Authorize(Roles = "Member")]
public List<vmLatestProblems> mylatestproblems()
{
   // Something there
}

ถูกเรียกผ่านรหัสเชิงมุมดังต่อไปนี้:

angular.module('appWorship').controller('latest', 
    ['$scope', '$http', function ($scope,$http) {         
        var urlBase = baseurl + '/api/home/LatestProblems';
        $http.get(urlBase).success(function (data) {
            $scope.data = data;
        }).error(function (data) {
            console.log(data);
        });
        $http.get(baseurl + '/api/home/mylatestproblems')
          .success(function (data) {
            $scope.data2 = data;
        }).error(function (data) {
            console.log(data);
        });  
    }]
);

ดังนั้นฉันไม่ได้เข้าสู่ระบบและวิธีแรกที่ส่งกลับข้อมูลสำเร็จ วิธีที่สองส่งกลับข้อมูล (ในฟังก์ชั่นความสำเร็จ) ซึ่งมีข้อมูลเทียบเท่ากับหน้าเข้าสู่ระบบ นั่นคือสิ่งที่คุณจะได้รับใน mvc หากคุณร้องขอการกระทำของคอนโทรลเลอร์ที่ประทับด้วย [อนุญาต] และคุณไม่ได้ลงชื่อเข้าใช้

ฉันต้องการให้ส่งคืน 401 ที่ไม่ได้รับอนุญาตเพื่อให้ฉันสามารถแสดงข้อมูลที่แตกต่างกันสำหรับผู้ใช้โดยขึ้นอยู่กับว่าพวกเขาเข้าสู่ระบบหรือไม่ เป็นการดีถ้าผู้ใช้เข้าสู่ระบบฉันต้องการที่จะสามารถเข้าถึงคุณสมบัติผู้ใช้ของตัวควบคุมเพื่อให้ฉันสามารถส่งกลับข้อมูลเฉพาะกับสมาชิกนั้น

ปรับปรุง: เนื่องจากไม่มีคำแนะนำด้านล่างดูเหมือนว่าจะทำงานได้อีกต่อไป (การเปลี่ยนแปลง Identity หรือ WebAPI) ive สร้างตัวอย่างดิบบนgithubซึ่งควรจะแสดงให้เห็นปัญหา

คำตอบ:


78

มีการใช้งาน AuthorizeAttribute สองรายการและคุณต้องตรวจสอบให้แน่ใจว่าคุณอ้างอิงสิ่งที่ถูกต้องสำหรับ Web API มีSystem.Web.Http.AuthorizeAttributeซึ่งใช้สำหรับ Web API's และSystem.Web.Mvc.AuthorizeAttributeซึ่งใช้สำหรับตัวควบคุมที่มีมุมมอง Http.AuthorizeAttributeจะส่งคืนข้อผิดพลาด 401 หากการอนุญาตล้มเหลวและMvc.AuthorizeAttributeจะเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ

อัปเดตเมื่อ 11/26/2013

ดังนั้นจึงปรากฏสิ่งที่มีการเปลี่ยนแปลงอย่างมากกับ MVC 5 เป็นบร็อคอัลเลนชี้ให้เห็นในบทความของเขา ฉันเดาว่าไปป์ไลน์ OWIN จะเข้ามาแทนที่และแนะนำพฤติกรรมใหม่บางอย่าง ตอนนี้เมื่อผู้ใช้ไม่ได้รับอนุญาตสถานะ 200 จะถูกส่งกลับด้วยข้อมูลต่อไปนี้ในส่วนหัว HTTP

X-Responded-JSON: {"status":401,"headers":{"location":"http:\/\/localhost:59540\/Account\/Login?ReturnUrl=%2Fapi%2FTestBasic"}}

คุณสามารถเปลี่ยนลอจิกที่ฝั่งไคลเอ็นต์เพื่อตรวจสอบข้อมูลนี้ในส่วนหัวเพื่อกำหนดวิธีจัดการกับปัญหานี้แทนที่จะมองหาสถานะ 401 ในสาขาข้อผิดพลาด

ผมพยายามที่จะแทนที่ลักษณะการทำงานนี้เองAuthorizeAttributeโดยการตั้งค่าสถานะในการตอบสนองในการOnAuthorizationและHandleUnauthorizedRequestวิธี

actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

แต่สิ่งนี้ไม่ได้ผล ไปป์ไลน์ใหม่ต้องคว้าการตอบสนองนี้ในภายหลังและแก้ไขเป็นคำตอบเดียวกับที่ฉันได้รับมาก่อน การขว้าง HttpException ไม่ได้ผลเนื่องจากมันเพิ่งเปลี่ยนเป็นสถานะข้อผิดพลาด 500

ฉันทดสอบวิธีแก้ปัญหาของ Brock Allen และใช้งานได้เมื่อฉันใช้การโทร jQuery ajax ถ้ามันไม่ได้ผลสำหรับคุณฉันคิดว่ามันเป็นเพราะคุณใช้มุม รันการทดสอบของคุณด้วยพู้ทำเล่นและดูว่าต่อไปนี้อยู่ในส่วนหัวของคุณ

X-Requested-With: XMLHttpRequest

หากไม่เป็นเช่นนั้นก็เป็นปัญหา ฉันไม่คุ้นเคยกับเชิงมุม แต่ถ้ามันช่วยให้คุณแทรกค่าส่วนหัวของคุณเองแล้วเพิ่มสิ่งนี้ลงในคำขอ ajax ของคุณและมันอาจจะเริ่มทำงานได้


ฉันคิดว่าฉันใช้ System.web.http.authorizeattribute อย่างน้อย webapicontroller นี้ไม่มีการใช้สำหรับ system.web.mvc และไปที่นิยามของคุณสมบัติการอนุญาตส่งฉันไปที่ system.web.http
Tim

สวัสดี @ kevin-junghans สับสนอย่างมากที่นี่ ตัวอย่างข้างต้นจาก shiva ใช้แอ็ตทริบิวต์การอนุญาต mvc ซึ่งแน่นอนว่าฉันไม่ควรใช้กับการกระทำของ webapi ตัวอย่างจาก Brock allen ไม่ปรากฏว่าทำงานได้ไม่คิดว่าจะเป็นการร้องขอ ajax เมื่อฉันผ่านไป
ทิม

1
เพียงแค่เห็นคำตอบนี้ (คิดว่า stackoverflow ไม่ได้ส่งการแจ้งเตือน) ฉันได้เพิ่มตัวอย่าง GitHub เพื่ออธิบายปัญหาและตอนนี้ก็เพิ่มการแก้ไขของคุณไปที่ส่วนหัวเชิงมุม ขอบคุณ ดูเหมือนจะไม่ถูกต้อง แต่ไม่มีคุณสมบัติในคุณสมบัติการอนุญาตที่ฉันสามารถตรวจสอบได้หรือฟังก์ชั่นดั้งเดิมที่คุณกล่าวถึงไม่ทำงานอีกต่อไป
ทิม

3
การใช้ POSTMAN และพารามิเตอร์ส่วนหัว X-Requested-With: XMLHttpRequest ใช้งานได้สำหรับฉัน ... ขอบคุณ
chemitaxis

ดังนั้นถ้าคุณมีสิ่งที่คุณตั้งใจจะทำโครงการ Web API อย่างแท้จริง ฉันกำลังทำงานในโครงการที่คนอื่นตั้งค่าและอนุญาตให้เปลี่ยนเส้นทางตามที่อธิบายไว้ที่นี่ แต่ฉันมีโครงการ API อื่นที่ทำงานได้ดี ต้องมีบางอย่างที่ทำให้สิ่งนี้คิดว่าเป็นแอป MVC แทนที่จะเป็นแอป API แต่ฉันไม่พบสิ่งที่อาจทำให้เสีย
Derek Greer

123

Brock Allen มีบล็อกโพสต์ที่ดีเกี่ยวกับวิธีคืน 401 สำหรับการโทร ajax เมื่อใช้การตรวจสอบคุกกี้และ OWIN http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/

วางสิ่งนี้ในวิธี ConfigureAuth ในไฟล์ Startup.Auth.cs:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
  AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
  LoginPath = new PathString("/Account/Login"),
  Provider = new CookieAuthenticationProvider
  {
    OnApplyRedirect = ctx =>
    {
      if (!IsAjaxRequest(ctx.Request))
      {
        ctx.Response.Redirect(ctx.RedirectUri);
      }
    }
  }
});

private static bool IsAjaxRequest(IOwinRequest request)
{
  IReadableStringCollection query = request.Query;
  if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
  {
     return true;
  }
  IHeaderDictionary headers = request.Headers;
  return ((headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest"));
}

68
รูปแบบที่หลากหลาย: หากการเรียกใช้เว็บ API ทั้งหมดของคุณผ่านเส้นทางที่กำหนดเช่น/apiคุณสามารถใช้เส้นทางเพื่อกำหนดว่าจะเปลี่ยนเส้นทางหรือไม่ มีประโยชน์อย่างยิ่งหากคุณมีลูกค้าที่ใช้รูปแบบอื่นเช่น JSON แทนที่เรียกร้องให้มีIsAjaxRequest if (!context.Request.Path.StartsWithSegments(new PathString("/api")))
Edward Brey

ไปงานปาร์ตี้สาย แต่วิธีนี้เป็นวิธีเดียวที่เหมาะกับฉันและดูเหมือนจะ "แม่นยำ" มากกว่า
สตีเฟ่นคอลลินส์

แม้จะสาย (r) ไปงานปาร์ตี้ แต่สิ่งนี้พิสูจน์ให้เห็นแล้วว่ามีประโยชน์มาก ... มันรบกวนจิตใจของฉันว่ารหัสที่สร้างโดยค่าเริ่มต้นทำสิ่งนี้ผิดอย่างนั้นในลักษณะที่ยากลำบากในการดีบัก
นิค

หากคุณอยู่หลังการแก้ปัญหา WebApi คำตอบของ Manik เป็นทางเลือกที่ดีสำหรับความคิดเห็นที่ได้รับการโหวตอย่างสูงที่นี่
Dunc

5
ใช้ C # 6 นี่คือ IsAjaxRequest รุ่นเล็ก: private static bool IsAjaxRequest(IOwinRequest request) { return request.Query?["X-Requested-With"] == "XMLHttpRequest" || request.Headers?["X-Requested-With"] == "XMLHttpRequest"; }
Peter Örneholm

86

หากคุณเพิ่ม asp.net WebApi ภายในเว็บไซต์ asp.net MVC คุณอาจต้องการตอบกลับคำขอบางอย่างโดยไม่ได้รับอนุญาต แต่โครงสร้างพื้นฐาน ASP.NET เริ่มเล่นและเมื่อคุณพยายามตั้งรหัสสถานะการตอบสนองเป็น HttpStatusCode ไม่ได้รับอนุญาตคุณจะได้รับ 302 เปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ

หากคุณใช้ข้อมูลประจำตัวของ asp.net และการรับรองความถูกต้องโดยใช้ Owin นี่เป็นรหัสที่สามารถช่วยแก้ไขปัญหาดังกล่าวได้:

public void ConfigureAuth(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider()
        {
            OnApplyRedirect = ctx =>
            {
                if (!IsApiRequest(ctx.Request))
                {
                    ctx.Response.Redirect(ctx.RedirectUri);
                }
            }
        }
    });

    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}


private static bool IsApiRequest(IOwinRequest request)
{
    string apiPath = VirtualPathUtility.ToAbsolute("~/api/");
    return request.Uri.LocalPath.StartsWith(apiPath);
}

1
ฉันเปลี่ยน discriminant เพื่อตรวจสอบว่าคำขอยอมรับ text / html หรือ application / xhtml เป็นการตอบสนองหรือไม่หากพวกเขาไม่คิดว่าเป็นคำขอของลูกค้า "อัตโนมัติ" คำขอ ajax ดังกล่าว
L.Trabacchin

4
ฉันชอบวิธีนี้เช่นกัน สิ่งเดียวที่ฉันทำคือการแปลง LocalPath .ToLower () ในกรณีที่พวกเขาร้องขอ "/ API" หรือบางอย่าง
FirstDivision

1
ขอบคุณมาก. มันช่วยชีวิตฉันไว้ :)
Amit Kumar

ใครมีโชคกับเรื่องนี้? CookieAuthenticationOptions ไม่มีคุณสมบัติผู้ให้บริการในฐานะของ aspnet core 1.1
Jeremy

27

ฉันได้รับสถานการณ์เดียวกันเมื่อ OWIN เปลี่ยนเส้นทางการตอบกลับ 401 หน้าการเข้าสู่ระบบจาก WebApi.Our API เว็บของเราไม่เพียงสนับสนุนการโทร ajax จาก Angular แต่ยังรวมถึงการโทรออกมือถือแบบชนะ ดังนั้นวิธีการแก้ปัญหาเพื่อตรวจสอบว่าคำขอเป็นคำขอ ajax ไม่ได้ถูกจัดเรียงสำหรับกรณีของเราจริงๆ

ฉันได้เลือกใช้วิธีอื่นคือฉีดการตอบสนองส่วนหัวใหม่: Suppress-Redirectหากการตอบสนองมาจาก webApi การใช้งานอยู่บนตัวจัดการ:

public class SuppressRedirectHandler : DelegatingHandler
{
    /// <summary>
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith(task =>
        {
            var response = task.Result;
            response.Headers.Add("Suppress-Redirect", "True");
            return response;
        }, cancellationToken);
    }
}

และลงทะเบียนตัวจัดการนี้ในระดับโลกของ WebApi:

config.MessageHandlers.Add(new SuppressRedirectHandler());

ดังนั้นในการเริ่มต้น OWIN คุณสามารถตรวจสอบว่าส่วนหัวการตอบสนองมีSuppress-Redirect:

public void Configuration(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationMode = AuthenticationMode.Active,
        AuthenticationType = DefaultApplicationTypes.ApplicationCookie,
        ExpireTimeSpan = TimeSpan.FromMinutes(48),

        LoginPath = new PathString("/NewAccount/LogOn"),

        Provider = new CookieAuthenticationProvider()
        {
            OnApplyRedirect = ctx =>
            {
                var response = ctx.Response;
                if (!IsApiResponse(ctx.Response))
                {
                    response.Redirect(ctx.RedirectUri);
                }
            }
        }
    });
}

private static bool IsApiResponse(IOwinResponse response)
{
    var responseHeader = response.Headers;

    if (responseHeader == null) 
        return false;

    if (!responseHeader.ContainsKey("Suppress-Redirect"))
        return false;

    if (!bool.TryParse(responseHeader["Suppress-Redirect"], out bool suppressRedirect))
        return false;

    return suppressRedirect;
}

ขอบคุณ ! API ของเราทำงานได้กับทุก plateform ยกเว้น Xamarin / Android จะใช้วิธีนี้
Jurion

17

ใน ASP.NET เวอร์ชันก่อนหน้าคุณต้องทำทุกอย่างเพื่อให้มันทำงานได้

ข่าวดีก็คือเนื่องจากคุณใช้ ASP.NET 4.5 คุณสามารถปิดใช้งานการเปลี่ยนเส้นทางการรับรองความถูกต้องของแบบฟอร์มโดยใช้คุณสมบัติHttpResponse.SuppressFormsAuthenticationRedirectใหม่

ในGlobal.asax:

protected void Application_EndRequest(Object sender, EventArgs e)
{
        HttpApplication context = (HttpApplication)sender;
        context.Response.SuppressFormsAuthenticationRedirect = true;
}

แก้ไข : คุณอาจต้องการที่จะดูที่บทความนี้โดย Sergey Zwezdin ซึ่งมีวิธีการกลั่นกรองเพิ่มเติมของการบรรลุสิ่งที่คุณพยายามทำ

ตัวอย่างโค้ดที่เกี่ยวข้องและคำบรรยายของผู้เขียนวางไว้ด้านล่าง ผู้เขียนต้นฉบับของรหัสและคำบรรยาย - Sergey Zwezdin

ก่อน - มาตรวจสอบว่าคำขอ HTTP ปัจจุบันเป็นคำขอ AJAX หรือไม่ หากใช่เราควรปิดใช้งานการแทนที่ HTTP 401 ด้วย HTTP 302:

public class ApplicationAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var httpContext = filterContext.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;

        if (request.IsAjaxRequest())
            response.SuppressFormsAuthenticationRedirect = true;

        base.HandleUnauthorizedRequest(filterContext);
    }
}

สอง - ลองเพิ่มเงื่อนไข :: ถ้าผู้ใช้รับรองความถูกต้องแล้วเราจะส่ง HTTP 403; และ HTTP 401 เป็นอย่างอื่น

public class ApplicationAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var httpContext = filterContext.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;
        var user = httpContext.User;

        if (request.IsAjaxRequest())
        {
            if (user.Identity.IsAuthenticated == false)
                response.StatusCode = (int)HttpStatusCode.Unauthorized;
            else
                response.StatusCode = (int)HttpStatusCode.Forbidden;

            response.SuppressFormsAuthenticationRedirect = true;
            response.End();
        }

        base.HandleUnauthorizedRequest(filterContext);
    }
}

ทำได้ดี. ตอนนี้เราควรแทนที่การใช้ทั้งหมดของ AuthorizeAttribute มาตรฐานด้วยตัวกรองใหม่นี้ มันอาจจะไม่สามารถใช้ได้กับคนที่มี sime ซึ่งเป็นรหัสที่น่าสนใจ แต่ฉันไม่รู้วิธีอื่น หากคุณมีเราไปที่ความคิดเห็นโปรด

สิ่งสุดท้ายที่เราควรทำ - เพื่อเพิ่มการจัดการ HTTP 401/403 บนฝั่งไคลเอ็นต์ เราสามารถใช้ ajaxError ที่ jQuery เพื่อหลีกเลี่ยงการทำซ้ำรหัส:

$(document).ajaxError(function (e, xhr) {
    if (xhr.status == 401)
        window.location = "/Account/Login";
    else if (xhr.status == 403)
        alert("You have no enough permissions to request this resource.");
});

ผลลัพธ์ -

  • หากผู้ใช้ไม่ได้รับการรับรองความถูกต้องแล้วเขาจะถูกนำไปยังหน้าเข้าสู่ระบบหลังจากการโทร AJAX
  • หากผู้ใช้รับรองความถูกต้อง แต่มีสิทธิ์ไม่เพียงพอเขาจะเห็นข้อความ erorr ที่ใช้งานง่าย
  • หากผู้ใช้ได้รับการรับรองความถูกต้องและมีสิทธิ์เพียงพอจะไม่มีข้อผิดพลาดใด ๆ และคำขอ HTTP จะดำเนินการตามปกติ

ฉันกำลังใช้เฟรมเวิร์กเอกลักษณ์ใหม่สำหรับการรับรองความถูกต้องผ่าน mvc การตั้งค่านี้จะไม่ป้องกันการเข้าสู่ระบบ mvc จากการทำงานรวมถึงการโทรผ่าน webapi หรือไม่?
ทิม

5
เมื่อฉันตรวจสอบตัวอย่างนี้จะปรากฏว่าอนุญาตแอตทริบิวต์ที่ใช้เป็นรุ่น MVC มากกว่ารุ่น WebApi อย่างไรก็ตามรุ่น webapi ไม่มีตัวเลือกสำหรับการยับยั้งการรับรองความถูกต้องของฟอร์ม
ทิม

คำขอของฉันไม่มีวิธี IsAjaxRequest
ทิม

1
Tim ดูที่นี่สำหรับ IsAjaxRequest: brockallen.com/2013/10/27/… หากคุณใช้ AngularJs โดยไม่ต้องแก้ไขส่วนหัวคุณจะไม่มี "XMLHttpRequest" และเพิ่มหรือตรวจสอบอย่างอื่น
ทิม

10

หากคุณใช้งานWeb APIจากภายในMVCโครงการคุณจะต้องสร้างแบบกำหนดเองAuthorizeAttributeเพื่อใช้กับAPIวิธีการของคุณ ภายในIsAuthorized overrideคุณจะต้องคว้าปัจจุบันHttpContextเพื่อป้องกันการเปลี่ยนเส้นทางเช่นนี้:

    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        if (string.IsNullOrWhiteSpace(Thread.CurrentPrincipal.Identity.Name))
        {
            var response = HttpContext.Current.Response;
            response.SuppressFormsAuthenticationRedirect = true;
            response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
            response.End();
        }

        return base.IsAuthorized(actionContext);
    }

8

การใช้การรวม Azure Active Directory ด้วยตนเองวิธีการใช้CookieAuthenticationมิดเดิลแวร์ไม่ได้ผลสำหรับฉัน ฉันต้องทำดังต่อไปนี้:

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        ...
        Notifications = new OpenIdConnectAuthenticationNotifications
        {   
            ...         
            RedirectToIdentityProvider = async context =>
            {
                if (!context.Request.Accept.Contains("html"))
                {
                    context.HandleResponse();
                }
            },
            ...
        }
    });

หากคำขอมาจากเบราว์เซอร์ (และไม่ใช่การเรียก AJAX เป็นต้น) ส่วนหัว Accept จะมีสตริงhtmlอยู่ที่ใดที่หนึ่ง เฉพาะเมื่อลูกค้ายอมรับ HTML ฉันจะพิจารณาการเปลี่ยนเส้นทางบางอย่างที่มีประโยชน์

แอปพลิเคชันไคลเอนต์ของฉันสามารถจัดการ 401 แจ้งให้ผู้ใช้ทราบว่าแอปไม่มีการเข้าถึงเพิ่มเติมและจำเป็นต้องโหลดซ้ำเพื่อเข้าสู่ระบบอีกครั้ง


นี่คล้ายกับวิธีแก้ปัญหาที่เสนอสำหรับคำถามที่เกี่ยวข้อง: stackoverflow.com/questions/34997674/…
Guillaume LaHaye

6

ฉันยังมีแอปพลิเคชัน MVC5 (System.Web) พร้อม WebApi (ใช้ OWIN) และต้องการป้องกัน 401 คำตอบจาก WebApi ที่เปลี่ยนเป็น 302 คำตอบ

สิ่งที่ใช้ได้ผลสำหรับฉันคือการสร้าง WebizeIizeizeAttribute รุ่นที่กำหนดเองเช่นนี้:

public class MyAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        base.HandleUnauthorizedRequest(actionContext);
        HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true;
    }
}

และใช้แทนมาตรฐาน WebApi AuthorizeAttribute ฉันใช้ MVC AuthorizeAttribute มาตรฐานเพื่อให้พฤติกรรม MVC ไม่เปลี่ยนแปลง


ใช้งานได้ แต่ตอนนี้ฉันมีปัญหาที่ลูกค้าได้รับสถานะ -1 แทน 401
Sebastián Rojas

@ SebastiánRojasฉันไม่แน่ใจว่าจะเกิดอะไรขึ้น - การตั้งค่าSuppressFormsAuthenticationRedirect สถานะทำให้เกิดการส่งคืน 401 เดิมสำหรับฉัน
Jono Job

3

เพียงติดตั้งแพ็คเกจ NeGet ต่อไปนี้

ติดตั้งแพ็คเกจ Microsoft.AspNet.WebApi.Owin

เขียนรหัสต่อไปนี้ในไฟล์ WebApiConfig

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        //Web API configuration and services
        //Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
    }
}

ฉันทั้งหมดที่จำเป็นต้องทำคือเพิ่มตัวกรองนี้และการทำงานconfig.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));อย่างอื่นUser.Identity.IsAuthenticatedอยู่เสมอfalse
ริคาร์โด้ Saracino

1

หากคุณต้องการจับ Content-Type == application / json คุณสามารถใช้รหัสนั้น:

private static bool IsAjaxRequest(IOwinRequest request)
    {
        IReadableStringCollection queryXML = request.Query;
        if ((queryXML != null) && (queryXML["X-Requested-With"] == "XMLHttpRequest"))
        {
            return true;
        }

        IReadableStringCollection queryJSON = request.Query;
        if ((queryJSON != null) && (queryJSON["Content-Type"] == "application/json"))
        {
            return true;
        }

        IHeaderDictionary headersXML = request.Headers;
        var isAjax = ((headersXML != null) && (headersXML["X-Requested-With"] == "XMLHttpRequest"));

        IHeaderDictionary headers = request.Headers;
        var isJson = ((headers != null) && (headers["Content-Type"] == "application/json"));

        return isAjax || isJson;

    }

ความนับถือ!!


1

ฉันมีปัญหาในการรับทั้งรหัสสถานะและการตอบกลับข้อความที่ทำงานในวิธีการ OnAuthorization / HandleUnauthorizedRequest นี่เป็นทางออกที่ดีที่สุดสำหรับฉัน:

    actionContext.Response = new HttpResponseMessage()
    {
        StatusCode = HttpStatusCode.Forbidden,
        Content = new StringContent(unauthorizedMessage)
    };

1

หลังจากที่พยายามหลีกเลี่ยงการเปลี่ยนเส้นทางไปยังหน้าการเข้าสู่ระบบฉันรู้สึกว่ามันค่อนข้างเหมาะสมสำหรับแอตทริบิวต์อนุญาต มันบอกว่าจะไปและได้รับอนุญาต สำหรับการโทรของ Api ที่ไม่ได้รับอนุญาตฉันแค่ต้องการไม่เปิดเผยข้อมูลใด ๆ ที่จะเป็นแฮ็กเกอร์ วัตถุประสงค์นี้ง่ายต่อการบรรลุโดยตรงโดยการเพิ่มแอตทริบิวต์ใหม่ที่ได้รับอนุญาตซึ่งแทนที่จะซ่อนเนื้อหาเป็นข้อผิดพลาด 404:

public class HideFromAnonymousUsersAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
         actionContext.Response = ActionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "Access Restricted");
    }
}

1

การผสม MVC และ WebAPI หากคำขอไม่ได้รับอนุญาตก็จะเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบแม้ในคำขอ WebAPI ด้วย เพื่อที่เราสามารถเพิ่มรหัสด้านล่างเพื่อส่งคำตอบไปยังแอปพลิเคชันมือถือ

protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
    var httpContext = HttpContext.Current;
    if (httpContext == null)
    {
        base.HandleUnauthorizedRequest(actionContext);
        return;
    }

    actionContext.Response = httpContext.User.Identity.IsAuthenticated == false ?
        actionContext.Request.CreateErrorResponse(
      System.Net.HttpStatusCode.Unauthorized, "Unauthorized") :
       actionContext.Request.CreateErrorResponse(
      System.Net.HttpStatusCode.Forbidden, "Forbidden");

    httpContext.Response.SuppressFormsAuthenticationRedirect = true;
    httpContext.Response.End();
}

0

ขอบคุณเพื่อน!

ในกรณีของฉันฉันรวมคำตอบของcuongle & Shivaและได้รับสิ่งนี้:

ในตัวจัดการ OnException () ของตัวจัดการสำหรับข้อยกเว้น API:

filterContext.ExceptionHandled = true;
//...
var response = filterContext.HttpContext.Response;
response.Headers.Add("Suppress-Redirect", "true");
response.SuppressFormsAuthenticationRedirect = true;

ในรหัสกำหนดค่าเริ่มต้นแอป:

app.UseCookieAuthentication(new CookieAuthenticationOptions {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider {
            OnValidateIdentity = ctx => {
                return validateFn.Invoke(ctx);
            },
            OnApplyRedirect = ctx =>
            {
                bool enableRedir = true;
                if (ctx.Response != null)
                {
                    string respType = ctx.Response.ContentType;
                    string suppress = ctx.Response.Headers["Suppress-Redirect"];
                    if (respType != null)
                    {
                        Regex rx = new Regex("^application\\/json(;(.*))?$",
                            RegexOptions.IgnoreCase);
                        if (rx.IsMatch(respType))
                        {
                            enableRedir = false;
                        }  
                    }
                    if ((!String.IsNullOrEmpty(suppress)) && (Boolean.Parse(suppress)))
                    {
                        enableRedir = false;
                    }
                }
                if (enableRedir)
                {
                    ctx.Response.Redirect(ctx.RedirectUri);
                }
            }
        }
    });

-1

ใน MVC 5 ที่มี Dot Net Framework 4.5.2 เราได้รับ "application / json, ข้อความธรรมดา .. " ภายใต้หัวข้อ "Accept" มันจะดีที่จะใช้ดังนี้:

isJson = headers["Content-Type"] == "application/json" || headers["Accept"].IndexOf("application/json", System.StringComparison.CurrentCultureIgnoreCase) >= 0;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.