Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/32.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# AngularJs、WebAPI、JWT和(集成的)Windows身份验证_C#_Asp.net_Angularjs_Authentication_Asp.net Web Api - Fatal编程技术网

C# AngularJs、WebAPI、JWT和(集成的)Windows身份验证

C# AngularJs、WebAPI、JWT和(集成的)Windows身份验证,c#,asp.net,angularjs,authentication,asp.net-web-api,C#,Asp.net,Angularjs,Authentication,Asp.net Web Api,我以前问过一个问题,得到的答案是正确的,但我越深入这个兔子洞,我就越意识到这一点;我想我问的问题不对 让我用我能用的最简单的术语来解释这个。。。我有一个AngularJS单页应用程序(客户端),它指向一个asp.net webapi(OWIN)站点(资源服务器?),还有一个单独的asp.net“授权/认证”服务器 身份验证服务器将为多个应用程序提供身份验证和授权。我需要能够在资源服务器中使用Authorize属性,以及从angular获取令牌。我还需要使用windows身份验证(集成)的一切,没

我以前问过一个问题,得到的答案是正确的,但我越深入这个兔子洞,我就越意识到这一点;我想我问的问题不对

让我用我能用的最简单的术语来解释这个。。。我有一个AngularJS单页应用程序(客户端),它指向一个asp.net webapi(OWIN)站点(资源服务器?),还有一个单独的asp.net“授权/认证”服务器

身份验证服务器将为多个应用程序提供身份验证和授权。我需要能够在资源服务器中使用Authorize属性,以及从angular获取令牌。我还需要使用windows身份验证(集成)的一切,没有用户名或密码。索赔信息存储在数据库中,需要添加到令牌中

我已经在asp.net内核中使用openiddict和JwtBearerToken以及“密码流”实现了SSO风格的声明授权,并希望尝试做类似的事情(令牌等)。从我以前的实现中,我已经基本了解了它是如何工作的,但是我完全无法理解如何让JWT与Windows Auth一起工作。我对上一个问题的回答提供了一些很好的建议,但我很难看到这在这个场景中是如何应用的

目前,我一直在尝试使用WindowsAuthenticationExtensions让IdentityServer3实现这一点,主要是从示例中提取的。但我真的很难将这与客户联系起来,并让一些东西真正发挥作用。当前的客户端和服务器代码如下,请注意,我真的不知道这是否接近正确的解决方案

客户:

app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Passive,
            AuthenticationType = "windows",
            Authority = "http://localhost:21989",
            ClientId = "mvc.owin.implicit",
            ClientSecret = "api-secret",
            RequiredScopes = new[] { "api" }
        });
AuthServer:

app.Map("/windows", ConfigureWindowsTokenProvider);
app.Use(async (context, next) =>
{
     if (context.Request.Uri.AbsolutePath.EndsWith("/token", StringComparison.OrdinalIgnoreCase))
            {
                if (context.Authentication.User == null ||
                    !context.Authentication.User.Identity.IsAuthenticated)
                {
                    context.Response.StatusCode = 401;
                    return;
                }
            }

            await next();
        });
        var factory = new IdentityServerServiceFactory()
           .UseInMemoryClients(Clients.Get())
           .UseInMemoryScopes(Scopes.Get());

        var options = new IdentityServerOptions
        {
            SigningCertificate = Certificate.Load(),
            Factory = factory,
            AuthenticationOptions = new AuthenticationOptions
            {
                EnableLocalLogin = false,
                IdentityProviders = ConfigureIdentityProviders
            },
            RequireSsl = false
        };

        app.UseIdentityServer(options);


private static void ConfigureWindowsTokenProvider(IAppBuilder app)
    {
        app.UseWindowsAuthenticationService(new WindowsAuthenticationOptions
        {
            IdpReplyUrl = "http://localhost:21989",
            SigningCertificate = Certificate.Load(),
            EnableOAuth2Endpoint = false
        });
    }

    private void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
    {
        var wsFederation = new WsFederationAuthenticationOptions
        {
            AuthenticationType = "windows",
            Caption = "Windows",
            SignInAsAuthenticationType = signInAsType,
            MetadataAddress = "http://localhost:21989",
            Wtrealm = "urn:idsrv3"
        };
        app.UseWsFederationAuthentication(wsFederation);
    }
编辑:我看到“/.well-known/openid配置”和“/.well-known/jwks”的auth端点请求,并且我在正在调用的控制器操作上有Authorize属性,但是我没有看到在auth端发生任何其他事情。我还向usewindowsauthservice WindowsAuthenticationOptions添加了ICustomClaimsProvider实现,但它甚至没有被调用

我已经在asp.net内核中使用openiddict和JwtBearerToken以及“密码流”完成了一个SSO风格的声明授权实现

如果要将OpenIddict与Windows身份验证一起使用,那么使用OAuth2/OpenID Connect隐式流(这是JS应用程序最合适的流)实现将非常容易,而不需要任何WS-Federation代理:

启动配置:

public void ConfigureServices(IServiceCollection services)
{
    // Register the OpenIddict services.
    services.AddOpenIddict(options =>
    {
        // Register the Entity Framework stores.
        options.AddEntityFrameworkCoreStores<ApplicationDbContext>();

        // Register the ASP.NET Core MVC binder used by OpenIddict.
        // Note: if you don't call this method, you won't be able to
        // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
        options.AddMvcBinders();

        // Enable the authorization endpoint.
        options.EnableAuthorizationEndpoint("/connect/authorize");

        // Enable the implicit flow.
        options.AllowImplicitFlow();

        // During development, you can disable the HTTPS requirement.
        options.DisableHttpsRequirement();

        // Register a new ephemeral key, that is discarded when the application
        // shuts down. Tokens signed using this key are automatically invalidated.
        // This method should only be used during development.
        options.AddEphemeralSigningKey();
    });

    // Note: when using WebListener instead of IIS/Kestrel, the following lines must be uncommented:
    //
    // services.Configure<WebListenerOptions>(options =>
    // {
    //     options.ListenerSettings.Authentication.AllowAnonymous = true;
    //     options.ListenerSettings.Authentication.Schemes = AuthenticationSchemes.Negotiate;
    // });
}
public class AuthorizationController : Controller
{
    // Warning: extreme caution must be taken to ensure the authorization endpoint is not included in a CORS policy
    // that would allow an attacker to force a victim to silently authenticate with his Windows credentials
    // and retrieve an access token using a cross-domain AJAX call. Avoiding CORS is strongly recommended.

    [HttpGet("~/connect/authorize")]
    public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
    {
        // Retrieve the Windows principal: if a null value is returned, apply an HTTP challenge
        // to allow IIS/WebListener to initiate the unmanaged integrated authentication dance.
        var principal = await HttpContext.Authentication.AuthenticateAsync(IISDefaults.Negotiate);
        if (principal == null)
        {
            return Challenge(IISDefaults.Negotiate);
        }

        // Note: while the principal is always a WindowsPrincipal object when using Kestrel behind IIS,
        // a WindowsPrincipal instance must be manually created from the WindowsIdentity with WebListener.
        var ticket = CreateTicket(request, principal as WindowsPrincipal ?? new WindowsPrincipal((WindowsIdentity) principal.Identity));

        // Immediately return an authorization response without displaying a consent screen.
        return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
    }

    private AuthenticationTicket CreateTicket(OpenIdConnectRequest request, WindowsPrincipal principal)
    {
        // Create a new ClaimsIdentity containing the claims that
        // will be used to create an id_token, a token or a code.
        var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);

        // Note: the JWT/OIDC "sub" claim is required by OpenIddict
        // but is not automatically added to the Windows principal, so
        // the primary security identifier is used as a fallback value.
        identity.AddClaim(OpenIdConnectConstants.Claims.Subject, principal.GetClaim(ClaimTypes.PrimarySid));

        // Note: by default, claims are NOT automatically included in the access and identity tokens.
        // To allow OpenIddict to serialize them, you must attach them a destination, that specifies
        // whether they should be included in access tokens, in identity tokens or in both.

        foreach (var claim in principal.Claims)
        {
            // In this sample, every claim is serialized in both the access and the identity tokens.
            // In a real world application, you'd probably want to exclude confidential claims
            // or apply a claims policy based on the scopes requested by the client application.
            claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
                                  OpenIdConnectConstants.Destinations.IdentityToken);

            // Copy the claim from the Windows principal to the new identity.
            identity.AddClaim(claim);
        }

        // Create a new authentication ticket holding the user identity.
        return new AuthenticationTicket(
            new ClaimsPrincipal(identity),
            new AuthenticationProperties(),
            OpenIdConnectServerDefaults.AuthenticationScheme);
    }
}

客户端代码不应该与使用隐式流的任何其他JS应用程序有所不同。您可以看看这个示例,看看如何使用oidc客户机JS库实现它:

因此,这里的最终目的是使用数据库中的声明来增强现有ClaimsPrincipal上的声明,并希望能够在javascript中使用JWT。我无法使用IdentityServer 3使其正常工作。我最终实现了自己的基本解决方案,实现了IAAuthenticationFilter和IAAuthorizationFilter,使用操作上的属性提供声明名称

首先,authorize属性只接受用户必须访问操作的声明的名称

public class AuthorizeClaimAttribute : Attribute
{
    public string ClaimValue;
    public AuthorizeClaimAttribute(string value)
    {
        ClaimValue = value;
    }
}
然后是Authorize过滤器,它只检查用户是否拥有该属性的声明

public class AuthorizeClaimFilter : AuthorizeAttribute, IAuthorizationFilter
{
    private readonly string _claimValue;

    public AuthorizeClaimFilter(string claimValue)
    {
        _claimValue = claimValue;
    }

    public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {            
        var p = actionContext.RequestContext.Principal as ClaimsPrincipal;

        if(!p.HasClaim("process", _claimValue))
            HandleUnauthorizedRequest(actionContext);

        await Task.FromResult(0);
    }

    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
    }

}
身份验证筛选器,它调用webapi端点(使用windows身份验证)以从数据库获取自定义“声明”的用户列表。WebAPI只是一个标准的WebAPI实例,没有什么特别之处

public class ClaimAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter
{
    public ClaimAuthenticationFilter()
    {
    }

    public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {

        if (context.Principal != null && context.Principal.Identity.IsAuthenticated)
        {
            var windowsPrincipal = context.Principal as WindowsPrincipal;
            var handler = new HttpClientHandler()
            {
                UseDefaultCredentials = true
            };

            HttpClient client = new HttpClient(handler);
            client.BaseAddress = new Uri("http://localhost:21989");// to be stored in config

            var response = await client.GetAsync("/Security");
            var contents = await response.Content.ReadAsStringAsync();
            var claimsmodel = JsonConvert.DeserializeObject<List<ClaimsModel>>(contents);

            if (windowsPrincipal != null)
            {
                var name = windowsPrincipal.Identity.Name;
                var identity = new ClaimsIdentity();


                foreach (var claim in claimsmodel)
                {
                    identity.AddClaim(new Claim("process", claim.ClaimName));
                }

                var claimsPrincipal = new ClaimsPrincipal(identity);
                context.Principal = claimsPrincipal;
            }
        }
        await Task.FromResult(0);
    }

    public async Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
    {
        var challenge = new AuthenticationHeaderValue("Negotiate");
        context.Result = new ResultWithChallenge(challenge, context.Result);
        await Task.FromResult(0);
    }
}
公共类ClariqueAuthenticationFilter:ActionFilterAttribute,IAAuthenticationFilter
{
public claimerauthenticationfilter()
{
}
公共异步任务AuthenticateAsync(HttpAuthenticationContext上下文,CancellationToken CancellationToken)
{
if(context.Principal!=null&&context.Principal.Identity.IsAuthenticated)
{
var windowsPrincipal=context.Principal作为windowsPrincipal;
var handler=new-HttpClientHandler()
{
UseDefaultCredentials=true
};
HttpClient=新的HttpClient(处理程序);
client.BaseAddress=新Uri(“http://localhost:21989“”;//存储在配置中
var response=wait client.GetAsync(“/Security”);
var contents=wait response.Content.ReadAsStringAsync();
var claimsmodel=JsonConvert.DeserializeObject(内容);
如果(windowsPrincipal!=null)
{
var name=windowsPrincipal.Identity.name;
var identity=new ClaimsIdentity();
foreach(索赔模型中的var索赔)
{
identity.AddClaim(新索赔(“流程”,索赔.ClaimName));
}
var claimsPrincipal=新的claimsPrincipal(标识);
context.Principal=claimsPrincipal;
}
}
等待任务。从结果(0);
}
公共异步任务ChallengeAsync(HttpAuthenticationChallengeContext,CancellationToken CancellationToken)
{
var challenge=新的AuthenticationHeaderValue(“协商”);
context.Result=newresultwithchallenge(challenge,context.Result);
等待任务。从结果(0);
}
}
过滤器使用我的DI框架绑定到属性(本例中为ninject)

this.BindHttpFilter(FilterScope.Action)
.WhenActionMethodHas()时
.WithConstructorArgumentFlomactionAttribute(“claimValue”,o=>o.claimValue);
这适用于我的目的,web api端点可以在WebAPI实例和AngularJS应用程序中使用。然而,这显然并不理想。我真的更愿意使用“真正的”身份验证/授权流程。我不敢说这就是问题的答案
public class ClaimAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter
{
    public ClaimAuthenticationFilter()
    {
    }

    public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {

        if (context.Principal != null && context.Principal.Identity.IsAuthenticated)
        {
            var windowsPrincipal = context.Principal as WindowsPrincipal;
            var handler = new HttpClientHandler()
            {
                UseDefaultCredentials = true
            };

            HttpClient client = new HttpClient(handler);
            client.BaseAddress = new Uri("http://localhost:21989");// to be stored in config

            var response = await client.GetAsync("/Security");
            var contents = await response.Content.ReadAsStringAsync();
            var claimsmodel = JsonConvert.DeserializeObject<List<ClaimsModel>>(contents);

            if (windowsPrincipal != null)
            {
                var name = windowsPrincipal.Identity.Name;
                var identity = new ClaimsIdentity();


                foreach (var claim in claimsmodel)
                {
                    identity.AddClaim(new Claim("process", claim.ClaimName));
                }

                var claimsPrincipal = new ClaimsPrincipal(identity);
                context.Principal = claimsPrincipal;
            }
        }
        await Task.FromResult(0);
    }

    public async Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
    {
        var challenge = new AuthenticationHeaderValue("Negotiate");
        context.Result = new ResultWithChallenge(challenge, context.Result);
        await Task.FromResult(0);
    }
}
 this.BindHttpFilter<AuthorizeClaimFilter>(FilterScope.Action)
             .WhenActionMethodHas<AuthorizeClaimAttribute>()
         .WithConstructorArgumentFromActionAttribute<AuthorizeClaimAttribute>("claimValue", o => o.ClaimValue);