访问受IdentityServer3保护的API时出现错误401

时间:2016-12-15 16:45:47

标签: c# asp.net-web-api identityserver3 .net-4.6

当我尝试从受IdentityServer3保护的API访问资源时,我收到401错误。

我可以从IdentityServer3的主机应用程序中安静地登录并获取 access_token ,但我无法使用 access_token 来使用此资源。

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

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的主机应用程序的源代码。

现在,我将向您展示我是如何设置API的。 这是我的 Startup 类:

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 中,我称这些配置为:

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());
    }
}

为了测试我做了以下事情:

<!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,已成功完成。

但是,对API进行的第二个请求会返回401错误 正如我之前所示, API受AuthorizeAttribute 保护。

有什么问题?

1 个答案:

答案 0 :(得分:3)

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

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

相关问题