仍然登录MVC站点,但无法调用Web API [英] Still logged in MVC site, but can't call web API

查看:21
本文介绍了仍然登录MVC站点,但无法调用Web API的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an ASP.NET MVC site, IdentityServer4 host and a web API.

当我使用外部提供程序 (Facebook) 登录 MVC 站点时,我可以正常登录.从 MVC 站点,我也可以正确使用 Web API.

When I log in the MVC site, using external provider (Facebook), I'm logged in fine. From the MVC site I can also consume the web API correctly.

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

However, the next day, I'm still logged in into the MVC site, but when I then try to access the web API, I get a 'not authorized exception'.

因此,虽然我仍然登录到 MVC 站点,但我不再通过身份验证从 MVC 站点内调用 Web API.

So although I'm still logged in in the MVC site, I'm not authenticated anymore to call a web API from within the MVC site.

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

I'm wondering how to handle this situation, and how IdentityServer4 should be configured.

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

MVC 应用程序配置如下:

The MVC application is configured like:

 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 客户端配置如下:

In IdentityServer the MVC client is configured like:

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:

And finally the 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 和承载.

There are two types of authentication, cookie and bearer.

cookie 让您保持登录状态,而持有者令牌则不能.因为不记名令牌会在某个时间点过期,而不允许您更改其生命周期.

Where the cookie keeps you logged in, the bearer token can't. Because the bearer token is set to expire at some point, without allowing you to change the lifetime.

访问令牌过期后访问资源(api)的唯一方法是让用户再次登录或使用 刷新令牌,无需用户交互.

The only way to access the resource (api) after the access token expires is to either let the user login again or request a new access token using a refresh token, without needing user interaction.

您已经配置好了:

options.Scope.Add("offline_access");

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

On each login the request will at least contain a refresh token. Store it at a safe place and use it when needed. By default it is set to one time use only.

您可以使用类似此代码的代码来更新令牌(因为您实际上并没有刷新它,而是替换了它).您需要包含IdentityModel"NuGet 包,如 IdentityServer 的示例中所示.

You can use something like this code to renew the token (as you are not actually refreshing it, but rather replacing it). You'll need to include the 'IdentityModel' NuGet package, as seen in the samples from 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;
}

默认情况下,刷新令牌的使用是配置作为一次性使用.请注意,当存储新的刷新令牌失败并且您应该丢失它时,那么请求新的刷新令牌的唯一方法是强制用户重新登录.

By default the refresh token usage is configured as one time use. Please note that when storing the new refresh token fails and you should lose it, then the only way to request a new refresh token is to force the user to login again.

另请注意,刷新令牌可能会过期.

Also note that the refresh token can expire.

退后一步,您需要在访问令牌过期或即将过期时使用它:

And taking it one step back, you'll need to use this when the access token expired or is about to expire:

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.

这篇关于仍然登录MVC站点,但无法调用Web API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆