我问了一个question之前给出的答案是正确的,但我越深入这个兔子洞,我就越意识到;我不认为我在问正确的问题。
让我用最简单的术语解释一下...我有一个 AngularJS 单页应用程序(客户端),它指向一个 asp.net webapi (OWIN) 站点(资源服务器?),以及一个单独的asp.net“授权/认证”服务器。
auth server 将为多个应用程序提供身份验证和授权。我需要能够在资源服务器中使用 Authorize 属性,以及从 Angular 获取 token 。我还需要对所有内容使用 Windows 身份验证(集成),没有用户名或密码。声明信息存储在数据库中,需要添加到 token 中。
我在 asp.net core 中使用带有 JwtBearerToken 和“密码流”的 openiddict 完成了 SSO 风格的声明授权实现?并想尝试做类似的事情( token 等)。从我之前的实现中,我对它的工作原理有一个基本的了解,但我完全迷失了试图弄清楚如何让 JWT 与 Windows Auth 一起工作。我上一个问题的答案提供了一些很好的建议,但我很难看出它如何适用于这种情况。
目前,我一直在尝试让 IdentityServer3 使用 WindowsAuthentication 扩展来执行此操作,主要是从示例中提取的。但我真的很难将其与客户联系在一起并真正发挥作用。当前的客户端和服务器代码如下,请注意,我真的不知道这是否接近正确的解决方案。
客户:
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Passive,
AuthenticationType = "windows",
Authority = "http://localhost:21989",
ClientId = "mvc.owin.implicit",
ClientSecret = "api-secret",
RequiredScopes = new[] { "api" }
});
授权服务器:
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-configuration”以及“/.well-known/jwks”的身份验证端点请求,并且我在正在调用的 Controller 操作上具有授权属性,但我没有看到授权方面发生任何其他事情。我还向 usewindowsauthservice WindowsAuthenticationOptions 添加了一个 ICustomClaimsProvider 实现,但它甚至没有被调用。
最佳答案
I've done a SSO style claims authoriztion implementation in asp.net core using openiddict with JwtBearerToken and 'password flow?'
如果您要将 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;
// });
}
授权 Controller :
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);
}
}
可以使用 OWIN/Katana 版本的 ASOS(OpenIddict 背后的 OpenID Connect 服务器中间件)在遗留 ASP.NET 应用程序中实现类似的场景:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseOpenIdConnectServer(options =>
{
// 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.SigningCredentials.AddEphemeralKey();
// Enable the authorization endpoint.
options.AuthorizationEndpointPath = new PathString("/connect/authorize");
// During development, you can disable the HTTPS requirement.
options.AllowInsecureHttp = true;
// Implement the ValidateAuthorizationRequest event to validate the response_type,
// the client_id and the redirect_uri provided by the client application.
options.Provider.OnValidateAuthorizationRequest = context =>
{
if (!context.Request.IsImplicitFlow())
{
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedResponseType,
description: "The provided response_type is invalid.");
return Task.FromResult(0);
}
if (!string.Equals(context.ClientId, "spa-application", StringComparison.Ordinal))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "The provided client_id is invalid.");
return Task.FromResult(0);
}
if (!string.Equals(context.RedirectUri, "http://spa-app.com/redirect_uri", StringComparison.Ordinal))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "The provided redirect_uri is invalid.");
return Task.FromResult(0);
}
context.Validate();
return Task.FromResult(0);
};
// Implement the HandleAuthorizationRequest event to return an implicit authorization response.
options.Provider.OnHandleAuthorizationRequest = context =>
{
// Retrieve the Windows principal: if a null value is returned, apply an HTTP challenge
// to allow IIS/SystemWeb to initiate the unmanaged integrated authentication dance.
var principal = context.OwinContext.Authentication.User as WindowsPrincipal;
if (principal == null)
{
context.OwinContext.Authentication.Challenge();
return Task.FromResult(0);
}
// 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.AuthenticationType);
// 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);
}
context.Validate(identity);
return Task.FromResult(0);
};
});
}
}
客户端代码不应与使用隐式流的任何其他 JS 应用程序不同。您可以查看此示例,了解如何使用 oidc-client JS 库实现它:https://github.com/openiddict/openiddict-samples/tree/master/samples/ImplicitFlow/AureliaApp
关于c# - AngularJs、WebAPI、JWT,带有(集成的)Windows 身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41291729/
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:
当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested
我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下