Google Code Prettify

顯示具有 CORS 標籤的文章。 顯示所有文章
顯示具有 CORS 標籤的文章。 顯示所有文章

存取的 RESTful API 使用 JWT 作為身份驗證方式,出現 401 Unauthorized 錯誤

出現 401 Unauthorized 錯誤通常表示在授權過程中出現問題。以下是一些常見的原因以及解決方案:

   JWT 沒有正確附加到請求標頭:確認 JWT 被正確地從 Session 中獲取並附加到請求的授權標頭中。
   API URL 錯誤或過期:確認 API 的 URL 正確並且沒有過期。
   JWT 過期:確認 JWT 沒有過期,如果過期需要重新獲取。
   CORS 問題:如果你從不同的域名訪問 API,確保 API 設置了正確的 CORS 政策。
   API 配置問題:確保 API 正確配置了身份驗證和授權策略。


以下是一些診斷和解決這些問題的步驟:

檢查 JWT 是否正確附加到請求標頭
確保 AuthorizedHttpClientHandler 中的 JWT 確實被附加到請求標頭中。你可以添加一些調試輸出來確認這一點:

csharp

public class AuthorizedHttpClientHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = _httpContextAccessor.HttpContext.Session.GetString("JWT");
        if (!string.IsNullOrEmpty(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }
        else
        {
            // 記錄沒有獲取到 Token 的情況
            Console.WriteLine("No JWT token found in session.");
        }

        // 記錄請求詳細信息
        Console.WriteLine($"Request: {request.Method} {request.RequestUri}");
        Console.WriteLine($"Authorization: {request.Headers.Authorization}");

        return await base.SendAsync(request, cancellationToken);
    }
}

檢查 API URL 和配置
確保你使用的是正確的 API URL,並且 API 正確配置了身份驗證和授權策略。

確認 JWT 沒有過期
如果 JWT 過期,你需要重新獲取新的 JWT。你可以在 API 的響應中檢查是否返回了過期錯誤信息。

配置 CORS
如果你從不同的域名訪問 API,請確保 API 設置了正確的 CORS 政策。例如:

csharp

// 在 API 的 Startup.cs 中配置 CORS
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("AllowSpecificOrigin",
            builder => builder.WithOrigins("https://yourclientapp.com")
                              .AllowAnyHeader()
                              .AllowAnyMethod());
    });

    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseCors("AllowSpecificOrigin");

    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
驗證整個流程
確保整個流程沒有問題,從登入到 API 請求。


完整示例
以下是更新後的完整示例:

csharp

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("AuthorizedClient")
        .AddHttpMessageHandler<AuthorizedHttpClientHandler>();

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddTransient<AuthorizedHttpClientHandler>();

    services.AddControllersWithViews();
    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(30);
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseSession();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

// AccountController.cs
public class AccountController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public AccountController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public IActionResult Login()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Login(LoginViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        var client = _httpClientFactory.CreateClient();
        var loginUrl = "https://yourapi.com/api/auth/login";
        var loginData = new
        {
            Username = model.Username,
            Password = model.Password
        };

        var content = new StringContent(JsonSerializer.Serialize(loginData), Encoding.UTF8, "application/json");
        var response = await client.PostAsync(loginUrl, content);

        if (response.IsSuccessStatusCode)
        {
            var responseString = await response.Content.ReadAsStringAsync();
            var jwt = JsonSerializer.Deserialize<JwtResponse>(responseString);

            // 存儲 JWT 到 Session
            HttpContext.Session.SetString("JWT", jwt.Token);

            return RedirectToAction("Index", "Home");
        }

        ModelState.AddModelError(string.Empty, "Invalid login attempt.");
        return View(model);
    }
}

// AuthorizedHttpClientHandler.cs
public class AuthorizedHttpClientHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = _httpContextAccessor.HttpContext.Session.GetString("JWT");
        if (!string.IsNullOrEmpty(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }
        else
        {
            Console.WriteLine("No JWT token found in session.");
        }

        Console.WriteLine($"Request: {request.Method} {request.RequestUri}");
        Console.WriteLine($"Authorization: {request.Headers.Authorization}");

        return await base.SendAsync(request, cancellationToken);
    }
}

// SomeController.cs
public class SomeController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public SomeController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<IActionResult> SomeAction()
    {
        var client = _httpClientFactory.CreateClient("AuthorizedClient");
        var response = await client.GetAsync("https://yourapi.com/api/protectedresource");

        if (response.IsSuccessStatusCode)
        {
            var data = await response.Content.ReadAsStringAsync();
            // 處理數據
        }
        else
        {
            // 處理錯誤
        }

        return View();
    }
}
總結
這些步驟應該可以幫助你診斷和解決 401 Unauthorized 錯誤。請確保 JWT 被正確地附加到每個需要授權的請求中,並且 API 端配置正確。

要使用 asp.net core 8 的 CORS 有什麼資料可以參考?

使用 ASP.NET Core 8 的 CORS(跨源資源共用)功能,你可以參考 Microsoft 的官方文件,這是一個很好的起點。以下是一些相關資源:

  1. Microsoft 官方文件:ASP.NET Core 官方文件提供了很好的指南和範例,可以幫助你設定和使用 CORS。你可以在 Microsoft 的 ASP.NET Core 文件中找到相關資料。ASP.NET Core 文件

  2. Microsoft 的 GitHub 存儲庫:Microsoft 在 GitHub 上維護了 ASP.NET Core 的源代碼和相關資源。你可以在 ASP.NET Core 的 GitHub 存儲庫 中找到相關的示例代碼和問題追蹤。

  3. 博客文章和教程:許多開發者和技術專家在個人博客上分享了他們的經驗和最佳實踐。你可以通過搜尋引擎查找一些針對 ASP.NET Core 8 的 CORS 的博客文章和教程,這些文章可能會提供實用的技巧和技巧。

  4. 專業課程和培訓資源:一些在線學習平台提供了針對 ASP.NET Core 的課程,其中可能包括有關 CORS 的相關章節或課程。你可以查看一些知名的在線學習平台,如 Pluralsight、Udemy、Coursera 等。

通過參考以上資源,你應該能夠找到足夠的資訊來了解如何在 ASP.NET Core 8 中有效地使用 CORS。 

web api 跨域请求,ajax跨域调用webapi

1、跨域问题仅仅发生在Javascript发起AJAX调用,或者Silverlight发起服务调用时,其根本原因是因为浏览器对于这两种请求,所给予的权限是较低的,通常只允许调用本域中的资源,除非目标服务器明确地告知它允许跨域调用。假设我们页面或者应用已在 http://www.test1.com 上了,而我们打算从 http://www.test2.com 请求提取数据。一般情况下,如果我们直接使用 AJAX 来请求将会失败,浏览器也会返回“源不匹配”的错误,"跨域"也就以此由来。
2、什么是跨域呢?
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。通常来说,跨域分为以下几类:


在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
 
3、当发起AJAX跨域(cross domain)调用ASP.NET MVC或者ASP.NET Web API编写的服务时,会发生无法访问的情况。
出现如下错误信息

Failed to load https://localhost:44348/api/Employee/List: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:44302' is therefore not allowed access.


在这里跟大家解释一下 Access-Control-Allow-Origin是HTML5中定义的一种服务器端返回Response header,用来解决资源(比如字体)的跨域权限问题。
当Access-Control-Allow-Origin后面跟URL 或 *,如果是 URL 则只会允许来自该 URL 的请求,* 则允许任何域的请求
例如:header('Access-Control-Allow-Origin:http://A.abc.com')||header('Access-Control-Allow-Origin:*')
意思是说只有当你请求的资源被允许跨域的时候才可以被访问。
那么我们该怎么设置Access-Control-Allow-Origin呢?
 
4、第一种是用JSONP来获得跨域的数据,而WebAPI本身是不支持javascript的callback的
 比较一下json与jsonp格式的区别:
json格式:
{
    "message":"获取成功",
    "state":"1",
    "result":{"name":"工作组1","id":1,"description":"11"}
}
jsonp格式:
callback({
    "message":"获取成功",
    "state":"1",
    "result":{"name":"工作组1","id":1,"description":"11"}
})
看出来区别了吧,在url中callback传到后台的参数是神马callback就是神马,jsonp比json外面有多了一层,callback()。
只需要给全局注册一个JsonCallbackAttribute,就可以判断接口的访问是属于跨域,还是非跨域,正常的返回。
因为我们的接口,可能是用来给 移动端(Android 、IOS)做数据接口,也有可能是给网站用,所以,考虑到可能存在跨域的问题。
   GlobalConfiguration.Configuration.Filters.Add(new JsonCallbackAttribute());
复制代码
复制代码
 public class JsonCallbackAttribute : ActionFilterAttribute
    {
        private const string CallbackQueryParameter = "callback";

        public override void OnActionExecuted(HttpActionExecutedContext context)
        {
            var callback = string.Empty;

            if (IsJsonp(out callback))
            {
                var jsonBuilder = new StringBuilder(callback);

                jsonBuilder.AppendFormat("({0})", context.Response.Content.ReadAsStringAsync().Result);

                context.Response.Content = new StringContent(jsonBuilder.ToString());
                //context.Response.Content = new StringContent("C(\"a\")");
            }

            base.OnActionExecuted(context);
        }

        private bool IsJsonp(out string callback)
        {
            callback = System.Web.HttpContext.Current.Request.QueryString[CallbackQueryParameter];

            return !string.IsNullOrEmpty(callback);
        }
复制代码
复制代码
结合下面图片不难开出,请求的地址带回了,callback的参数标识。
当然也可以用解决跨域问题的jQuery插件-jquery-jsonp,有第一种方式的基础,使用jsonp插件也就比较简单了,server端代码无需任何改动。
5、服务端直接修改配置文件,个人认为这种方式好一点,毕竟我们所写的api是对外公开的,安全访问的控制还是要通过其他方法来保证。
针对ASP.NET MVC,只需要在web.config中添加如下的内容即可
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
  
针对ASP.NET Web API,除了上面这样的设置,还需要添加一个特殊的设计,就是为每个APIController添加一个OPTIONS的方法,但无需返回任何东西。
public string Options()
{
return null; // HTTP 200 response with empty body
}
6、还有用CORS(跨域资源共享,Cross-Origin Resource Sharing)来解决的, CORS定义一种跨域访问的机制,可以让AJAX实现跨域访问。CORS 允许一个域上的网络应用向另一个域提交跨域 AJAX 请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。
hander() 设置,“*”号表示允许任何域向我们的服务端提交请求

     


  也可以设置指定的域名,如域名 http://www.test2.com ,那么就允许来自这个域名的请求
     


这种没有仔细研究,其实跟上面的配置文件,差不多,都是去设置响应头。
 
本文是结合好多其他作者的文章:
http://www.cnblogs.com/chenxizhang/p/3821703.html
http://www.cnblogs.com/sunxucool/p/3433992.html
http://www.cnblogs.com/Darren_code/p/cors.html






from: https://www.cnblogs.com/inconceivable/p/5504732.html