访问受IdentityServer3保护的API时出现错误401 [英] Error 401 when accessing an API protected by IdentityServer3

查看:168
本文介绍了访问受IdentityServer3保护的API时出现错误401的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尝试从受IdentityServer3保护的API访问资源时遇到401错误.

I'm getting a 401 error when I try to access a resource from an API protected by IdentityServer3.

我可以登录并从IdentityServer3的主机应用程序中静静地获取 access_token ,但是我不能使用 access_token 来消耗此资源.

I can log in and get the access_token quietly from the Host application of IdentityServer3, but I cannot use the access_token to consume this resource.

我在 Startup 类中配置了IdentityServer的主机,如下所示:

I configured my Host of IdentityServer in Startup class like this:

public void Configuration(IAppBuilder app)
{
    Log.Logger = new LoggerConfiguration()
        .WriteTo.Trace()
        .CreateLogger();

    AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
    JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

    // Configure IdentityServer3
    app.Map("/identity", configuration =>
    {
        configuration.UseIdentityServer(new IdentityServerOptions
        {
            SiteName = "IdentityServer3 Sample",
            SigningCertificate = LoadCertificate(),
            Factory = ServiceFactory.Create(),
            RequireSsl = true,

            CspOptions = new CspOptions
            {
                Enabled = true,
                FontSrc = "fonts.googleapis.com"
            },

            AuthenticationOptions = new AuthenticationOptions
            {
                EnablePostSignOutAutoRedirect = true,
            }
        });
    });
}

在我的 ServiceFactory 类中,我有:

public static IdentityServerServiceFactory Create()
{
    var factory = new IdentityServerServiceFactory
    {
        ScopeStore = new Registration<IScopeStore>(
            new InMemoryScopeStore(Scopes.GetScopes())),
        ClientStore = new Registration<IClientStore>(
            new InMemoryClientStore(Clients.GetClients())),
        CorsPolicyService = new Registration<ICorsPolicyService>(
            new DefaultCorsPolicyService {AllowAll = true})
    };

    //factory.UseInMemoryUsers(Users.GetUsers());

    ConfigureServices(factory);

    return factory;
}

private static void ConfigureServices(IdentityServerServiceFactory factory)
{
    factory.UserService = new Registration<IUserService, UserService>();

    factory.Register(new Registration<BaseContext>(resolver => new BaseContext()));

    factory.Register(new Registration<AppUserManager>(resolver => new AppUserManager(
        new UserStore<User>(resolver.Resolve<BaseContext>()))));
}

范围:

return new List<Scope>
{
    StandardScopes.OpenId,
    StandardScopes.Profile,
    StandardScopes.OfflineAccess,

    new Scope
    {
        Enabled = true,
        Name = "roles",
        Type = ScopeType.Identity,
        IncludeAllClaimsForUser = true,
        Claims = new List<ScopeClaim>
        {
            new ScopeClaim("role")
        }
    },

    new Scope
    {                    
        Enabled = true,
        Name = "ro",
        Type = ScopeType.Resource,
        IncludeAllClaimsForUser = true,
        Claims = new List<ScopeClaim>
        {
            new ScopeClaim("role")
        }
    }
};

客户:

return new List<Client>
{
    new Client
    {
        Enabled = true,
        ClientName = "Hibrid Flow Client",
        ClientId = AppIdentityConstants.ClientIdForHibridFlow,
        Flow = Flows.Hybrid,

        RequireConsent = false,
        AccessTokenType = AccessTokenType.Reference,
        UpdateAccessTokenClaimsOnRefresh = true,

        ClientSecrets = new List<Secret>
        {
            new Secret(AppIdentityConstants.ClientSecret.Sha256())
        },
        AllowedScopes = new List<string>
        {
            Constants.StandardScopes.OpenId,
            Constants.StandardScopes.Profile,
            Constants.StandardScopes.Email,
            Constants.StandardScopes.Roles,
            Constants.StandardScopes.OfflineAccess,
        },
        RedirectUris = new List<string>
        {
            AppIdentityConstants.IdentityAddress,
            AppIdentityConstants.CRMAddress
        },
        PostLogoutRedirectUris = new List<string>
        {
            AppIdentityConstants.IdentityAddress,
            AppIdentityConstants.CRMAddress
        },
        LogoutSessionRequired = true
    },

    new Client
    {
        Enabled = true,
        ClientName = "Resource Owner Client",
        ClientId = AppIdentityConstants.ClientIdForResourceOwnerFlow,
        Flow = Flows.ResourceOwner,

        RequireConsent = false,
        AccessTokenType = AccessTokenType.Jwt,
        UpdateAccessTokenClaimsOnRefresh = true,
        AccessTokenLifetime = 3600,

        ClientSecrets = new List<Secret>
        {
            new Secret(AppIdentityConstants.ClientSecret.Sha256())
        },
        AllowedScopes = new List<string>
        {
            Constants.StandardScopes.OpenId,
            Constants.StandardScopes.Profile,
            Constants.StandardScopes.Email,
            Constants.StandardScopes.Roles,
            Constants.StandardScopes.OfflineAccess,
            "ro"
        },
        AllowAccessTokensViaBrowser = true,
        AbsoluteRefreshTokenLifetime = 86400,
        SlidingRefreshTokenLifetime = 43200,
        RefreshTokenUsage = TokenUsage.OneTimeOnly,
        RefreshTokenExpiration = TokenExpiration.Sliding
    },
};

这是IdentityServer3主机应用程序的源代码.

This is the source code for the Host Application of IdentityServer3.

现在,我将向您展示如何设置API. 这是我的启动类:

And now I'll show you how I've set up my API. This is my Startup class:

public void Configuration(IAppBuilder app)
{
    JwtSecurityTokenHandler.InboundClaimTypeMap.Clear();

    app.UseIdentityServerBearerTokenAuthentication(
        new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = AppIdentityConstants.IdentityBaseAddress,
        RequiredScopes = new[] { "ro", "offline_access" },
        ClientId = AppIdentityConstants.ClientIdForResourceOwnerFlow,
        ClientSecret = AppIdentityConstants.ClientSecret,
    });
}

AppIdentityConstants.IdentityBaseAddresshttps://localhost:44342/identity.

然后,在 Global.asax.cs 中,我将这些配置称为:

And, in Global.asax.cs I call these configurations:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        config.Formatters.Remove(config.Formatters.XmlFormatter);

        var formatters = GlobalConfiguration.Configuration.Formatters;
        var jsonFormatter = formatters.JsonFormatter;
        var settings = jsonFormatter.SerializerSettings;

        #if DEBUG
        settings.Formatting = Formatting.Indented;
        #endif

        settings.ContractResolver = new CamelCasePropertyNamesContractResolver();

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

AuthorizeAttribute 过滤器:

public class FilterConfig
{
    public static void RegisterGlobalFilters(HttpConfiguration configuration)
    {
        configuration.Filters.Add(new AuthorizeAttribute());
    }
}

要进行测试,我做了以下工作:

To test I did the following:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
    <script src="bower_components/jquery/dist/jquery.min.js"></script>
    <script>
        function done(response) { console.log(response); }
        function always(response) { console.log("always"); }
        function fail(response) { console.log("fail"); }
        var custom = {
            client_id: "ro",
            client_secret: "client_secret",
            scope: "ro offline_access",
        };

        $(function () {
            var settings = {
                "async": true,
                "crossDomain": true,
                "url": "https://localhost:44342/identity/connect/token",
                "method": "POST",
                "headers": {
                    "content-type": "application/x-www-form-urlencoded",
                    "cache-control": "no-cache"
                },
                "data": {
                    "client_id": custom.client_id,
                    "client_secret": custom.client_secret,
                    "scope": custom.scope,
                    "username": "user@test.com",
                    "password": "123456",
                    "grant_type": "password"
                }
            }
            $.ajax(settings).done(function (response){
                done(response);
                checkStatus(response.access_token);
            }).always(always).fail(fail);

            function checkStatus(access_token) {
                var settings2 = {
                    "async": true,
                    "crossDomain": true,
                    "url": "https://localhost:44352/api/importer/status",
                    "method": "GET",
                    xhrFields: {
                        withCredentials: true
                    },
                    "headers": {
                        "Authorization": "Bearer " + access_token,
                        "cache-control": "no-cache"
                    }
                }
                $.ajax(settings2).done(done).always(always).fail(fail);
            }
        });
    </script>
</body>
</html>

第一个用于获取访问数据(包括acess_token)的请求已成功完成.

The first request, which is to obtain the access data, including the acess_token, is done successfully.

但是对API发出的第二个请求返回401错误.
正如我之前所展示的, API受AuthorizeAttribute保护.

But the second request, which is made to the API, returns a 401 error.
And as I showed earlier, the API is protected with the AuthorizeAttribute.

怎么了?

推荐答案

如果调试checkStatus函数,acessData参数是否具有access_token属性?

If you debug the checkStatus function, does the acessData parameter have an access_token property?

如果是,那么您是否在Web API项目中安装了Microsoft.Owin.Host.SystemWeb NuGet软件包?可能发生的情况是您的OWIN管道未执行,因为您缺少该软件包.因此,访问令牌不会转换为身份,并且请求保持未经身份验证,这可以解释HTTP 401响应.

If so, then did you install the Microsoft.Owin.Host.SystemWeb NuGet package in your Web API project? What could happen is that your OWIN pipeline is not executed because you're missing that package. So the access token is not transformed into an identity, and the request stays unauthenticated, which could explain the HTTP 401 response.

这篇关于访问受IdentityServer3保护的API时出现错误401的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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