2016-12-15 5 views
0

IdentityServer3에 의해 보호되는 API에서 리소스에 액세스하려고하면 401 오류가 발생합니다.IdentityServer3에 의해 보호되는 API에 액세스 할 때 오류 401

나는 로그인 IdentityServer3의 호스트 응용 프로그램에서 조용히 access_token이를 얻을 수 있지만이 자원을 소비 access_token이 를 사용할 수 있습니다.

나는 클래스 같은 시작에 IdentityServer 내 호스트를 구성 :

ServiceFactory 클래스에서
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, 
      } 
     }); 
    }); 
} 

, 내가 가진 :

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

스코프를을 :

(210)
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를 어떻게 설정했는지 보여 드리겠습니다.

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을 필터 :

<!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": "[email protected]", 
        "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> 
:

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

내가 다음 않았다 테스트하려면

acess_token을 포함하여 액세스 데이터를 얻는 첫 번째 요청이 성공적으로 완료됩니다.

그러나 API에 대해 수행 된 두 번째 요청은 401 오류를 반환합니다.
앞에서 설명한 것처럼 API는 AuthorizeAttribute으로 보호됩니다.

무엇이 잘못 되었나요?

답변

3

checkStatus 함수를 디버깅 할 경우 acessData 매개 변수의 매개 변수는 access_token입니까?

그렇다면 웹 API 프로젝트에 Microsoft.Owin.Host.SystemWeb NuGet 패키지를 설치 했습니까? 당신이 그 패키지를 놓치고 있기 때문에 당신의 OWIN 파이프 라인이 실행되지 않을 수도 있습니다.따라서 액세스 토큰은 ID로 변환되지 않으며 요청은 인증되지 않은 상태로 유지되어 HTTP 401 응답을 설명 할 수 있습니다.

+0

'Microsoft.Owin.Host.SystemWeb'의 설치가 효과적입니다. 하지만'WWW-Authenticate → Bearer error = "insufficient_scope"오류가 발생합니다. 무슨 일이 일어날 지 아십니까? 너 나 좀 도와 줄 수있어? – JamesTK

+0

간단히 말해서,'IdentityServerBearerTokenAuthenticationOptions'의'RequiredScopes' 속성은 배열이고 하나의 온라인 범위를 알려주고있었습니다. 해결! – JamesTK