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

查看:51
本文介绍了仍登录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");                  
        });            

因此它正在使用混合流.

So it's using hybrid flow.

在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,
},

最后是网络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天全站免登陆