c# - 仍登录 MVC 站点,但无法调用 Web API

标签 c# asp.net-core-mvc identityserver4 asp.net-core-webapi

我有一个 ASP.NET MVC 站点、IdentityServer4 主机和一个 Web API。

当我使用外部提供商 (Facebook) 登录 MVC 站点时,我登录正常。从 MVC 站点,我还可以正确使用 Web API。

但是,第二天,我仍然登录到 MVC 站点,但当我尝试访问 Web API 时,我收到“未授权异常”。

因此,尽管我仍然登录 MVC 站点,但我不再通过身份验证来从 MVC 站点内调用 Web API。

我想知道如何处理这种情况,以及应该如何配置 IdentityServer4。

  • 为什么一天后我仍然登录 MVC 站点?如何配置?
  • 如果我仍然登录 MVC 站点,为什么我仍然无法调用 Web API?
  • 我可以同步过期时间吗?或者我该如何处理?

MVC 应用程序的配置如下:

 services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc"; 
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";
            options.Authority = mgpIdSvrSettings.Authority;
            options.RequireHttpsMetadata = false;                
            options.ClientId = mgpIdSvrSettings.ClientId;
            options.ClientSecret = mgpIdSvrSettings.ClientSecret; // Should match the secret at IdentityServer
            options.ResponseType = "code id_token"; // Use hybrid flow
            options.SaveTokens = true;                
            options.GetClaimsFromUserInfoEndpoint = true;                
            options.Scope.Add("mgpApi");
            options.Scope.Add("offline_access");                  
        });            

所以它使用混合流。

在 IdentityServer 中,MVC 客户端的配置如下:

new Client
{
     EnableLocalLogin = false,

     ClientId = "mgpPortal",
     ClientName = "MGP Portal Site",
     AllowedGrantTypes = GrantTypes.Hybrid,

     // where to redirect to after login
     RedirectUris = mgpPortalSite.RedirectUris,

     // where to redirect to after logout
     PostLogoutRedirectUris = mgpPortalSite.PostLogoutRedirectUris,

     // secret for authentication
     ClientSecrets = mgpPortalSite.ClientSecrets.Select(cs => new Secret(cs.Sha256())).ToList(),

     AllowedScopes = new List<string>
     {
            IdentityServerConstants.StandardScopes.OpenId,
            IdentityServerConstants.StandardScopes.Profile,
            "mgpApi"
     },

     AllowOfflineAccess = true,                             
     RequireConsent = false,
},

最后是 Web API:

 services.AddAuthentication("Bearer")                
           .AddIdentityServerAuthentication(options =>
            {
                options.Authority = mgpIdSvrSettings.Authority;
                options.RequireHttpsMetadata = false;                    
                options.ApiName = mgpIdSvrSettings.ApiName;
                options.EnableCaching = true;
                options.CacheDuration = TimeSpan.FromMinutes(10);                    
            });

最佳答案

有两种类型的身份验证:cookie 和 bearer。

Cookie 可以让您保持登录状态,但不记名 token 却不能。因为不记名 token 被设置为在某个时间点过期,并且不允许您更改生命周期。

访问 token 过期后访问资源 (api) 的唯一方法是让用户再次登录或使用 refresh token 请求新的访问 token 。 ,无需用户交互。

您已经配置了它:

options.Scope.Add("offline_access");

每次登录时,请求至少包含一个刷新 token 。将其存放在安全的地方并在需要时使用。默认情况下,它设置为仅使用一次。

<小时/>

您可以使用类似此代码的内容来更新 token (因为您实际上并不是刷新它,而是替换它)。您需要包含“IdentityModel”NuGet 包,如 IdentityServer 中的示例所示。

private async Task<TokenResponse> RenewTokensAsync()
{
    // Initialize the token endpoint:
    var client = _httpClientFactory.CreateClient();
    var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");

    if (disco.IsError) throw new Exception(disco.Error);

    // Read the stored refresh token:
    var rt = await HttpContext.GetTokenAsync("refresh_token");
    var tokenClient = _httpClientFactory.CreateClient();

    // Request a new access token:
    var tokenResult = await tokenClient.RequestRefreshTokenAsync(new RefreshTokenRequest
    {
        Address = disco.TokenEndpoint,

        ClientId = "mvc",
        ClientSecret = "secret",
        RefreshToken = rt
    });

    if (!tokenResult.IsError)
    {
        var old_id_token = await HttpContext.GetTokenAsync("id_token");
        var new_access_token = tokenResult.AccessToken;
        var new_refresh_token = tokenResult.RefreshToken;
        var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);

        // Save the information in the cookie
        var info = await HttpContext.AuthenticateAsync("Cookies");

        info.Properties.UpdateTokenValue("refresh_token", new_refresh_token);
        info.Properties.UpdateTokenValue("access_token", new_access_token);
        info.Properties.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture));

        await HttpContext.SignInAsync("Cookies", info.Principal, info.Properties);
        return tokenResult;
    }
    return null;
}

默认情况下,刷新 token 使用量为 configured作为一次性使用。请注意,当存储新的刷新 token 失败并且您应该丢失它时,请求新的刷新 token 的唯一方法是强制用户再次登录。

另请注意,刷新 token 可能会过期。

<小时/>

退一步来说,当访问 token 过期或即将过期时,您需要使用它:

var accessToken = await HttpContext.GetTokenAsync("access_token");

var tokenHandler = new JwtSecurityTokenHandler();

var jwtSecurityToken = tokenHandler.ReadJwtToken(accessToken);

// Depending on the lifetime of the access token.
// This is just an example. An access token may be valid
// for less than one minute.
if (jwtSecurityToken.ValidTo < DateTime.UtcNow.AddMinutes(5))
{
    var responseToken = await RenewTokensAsync();
    if (responseToken == null)
    {
        throw new Exception("Error");
    }
    accessToken = responseToken.AccessToken;
}

// Proceed, accessToken contains a valid token.

关于c# - 仍登录 MVC 站点,但无法调用 Web API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54498454/

相关文章:

c# - 如何添加自定义 header ,然后在兔子消息 header 中检索自定义 header

c# - 在asp.net core 2.0中重写url

c# - 具有 ASP.NET Core 身份的 Web API 的 IdentityServer4 基于角色的授权

c# - 使用 IServiceCollection.AddTransient、IServiceCollection.AddSingleton 和 IServiceCollectionAddScoped 方法的实际场景有哪些?

c# - 使用 net7mma 项目中的 RtspServer 进行多播流

c# - 将数组作为单独的值传递给参数

c# - 在 _ViewImports.cshtml 中导入命名空间

sql-server - ASP.Net Core 2.0 FileTable的用法

c# - IdentityServer4 token 签名验证

C# .NET Core 托管 Blazor WebAssembly - 将其他客户端添加到 .Server 项目 API