使用 IdentityServer 向特定客户端验证特定用户
Authenticating a particular user to a particular client using IdentityServer
我正在使用 IdentityServerV3
为少数客户端验证用户。我在以特定 user
必须能够登录到特定“客户端”的方式配置 IdentityServer
时遇到问题。
让我们来看下面的场景:
我有 2 个客户 -> client1
、client2
。
我有 3 个用户 -> user1
、user2
、user3
.
user1
& user2
只能访问 client1
。而 user3
可以单独访问 client2
。
当我尝试使用 user3
登录 client1
时,身份服务器验证成功。我不想要。
public static class Clients
{
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
Enabled = true,
ClientName = "MVC Client",
ClientId = "mvc",
Flow = Flows.Hybrid,
RedirectUris = new List<string>
{
"https://localhost:44319/"
}
},
new Client
{
Enabled = true,
ClientName = "MVC Client 2",
ClientId = "mvc2",
Flow = Flows.Hybrid,
RedirectUris = new List<string>
{
"https://localhost:44319/"
}
}
};
}
}
用户是:
public static class Users
{
public static List<InMemoryUser> Get()
{
return new List<InMemoryUser>
{
new InMemoryUser
{
Username = "bob",
Password = "secret",
Subject = "1",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Bob"),
new Claim(Constants.ClaimTypes.FamilyName, "Smith")
}
},
new InMemoryUser
{
Username = "rob",
Password = "secret",
Subject = "2",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Rob"),
new Claim(Constants.ClaimTypes.FamilyName, "Thompson")
}
},
new InMemoryUser
{
Username = "Jerry",
Password = "secret",
Subject = "3",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Jerry"),
new Claim(Constants.ClaimTypes.FamilyName, "Smith")
}
}
};
}
}
现在 OWIN 启动 class 是:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/identity", idsrvApp =>
{
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Embedded IdentityServer",
SigningCertificate = LoadCertificate(),
Factory = InMemoryFactory.Create(
users : Users.Get(),
clients: Clients.Get(),
scopes : StandardScopes.All)
});
});
}
X509Certificate2 LoadCertificate()
{
return new X509Certificate2(
string.Format(@"{0}\bin\identityServer\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test");
}
}
我不明白 IdentityServer
如何知道哪个用户 bleongs 到哪个客户。 IdentityServer
不支持我的方案,还是我遗漏了什么。
更新
public class LocalRegistrationUserService : UserServiceBase
{
public class CustomUser
{
public string Subject { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public List<Claim> Claims { get; set; }
}
public static List<CustomUser> Users = new List<CustomUser>();
public string ClientId { get; set; }
public override Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
var user = Users.SingleOrDefault(x => x.Username == context.UserName && x.Password == context.Password);
if (user != null)
{
context.AuthenticateResult = new AuthenticateResult(user.Subject, user.Username);
}
return Task.FromResult(0);
}
}
While registering the user i am redirecting him to a controller in Identity Server Host
.
public class LocalRegistrationController : Controller
{
[Route("core/localregistration")]
[HttpGet]
public ActionResult Index(string signin)
{
return View();
}
[Route("core/localregistration")]
[HttpPost]
public ActionResult Index(string signin, LocalRegistrationModel model)
{
var ctx = Request.GetOwinContext();
if (ModelState.IsValid)
{
var user = new LocalRegistrationUserService.CustomUser
{
Username = model.Username,
Password = model.Password,
Subject = Guid.NewGuid().ToString(),
Claims = new List<Claim>()
};
LocalRegistrationUserService.Users.Add(user);
user.Claims.Add(new Claim(Constants.ClaimTypes.GivenName, model.First));
user.Claims.Add(new Claim(Constants.ClaimTypes.FamilyName, model.Last));
return Redirect("~/core/" + Constants.RoutePaths.Login + "?signin=" + signin);
}
return View();
}
}
startup.cs
app.Map("/core", coreApp =>
{
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
// different examples of custom user services
//var userService = new RegisterFirstExternalRegistrationUserService();
//var userService = new ExternalRegistrationUserService();
var userService = new LocalRegistrationUserService();
// note: for the sample this registration is a singletone (not what you want in production probably)
factory.UserService = new Registration<IUserService>(resolver => userService);
factory.ViewService = new Registration<IViewService, CustomViewService>();
var options = new IdentityServerOptions
{
SiteName = "Identity Server 3",
SigningCertificate = Certificate.Get(),
Factory = factory,
AuthenticationOptions = new AuthenticationOptions
{
IdentityProviders = ConfigureAdditionalIdentityProviders,
LoginPageLinks = new LoginPageLink[] {
new LoginPageLink{
Text = "Register",
// Href = "~/externalregistration",
Href = "~/localregistration",
//Href = "localregistration"
},
new LoginPageLink{
Text = "Forgot Password?",
Href = "~/forgotpassword",
//Href = "localregistration"
}
}
},
EventsOptions = new EventsOptions
{
RaiseSuccessEvents = true,
RaiseErrorEvents = true,
RaiseFailureEvents = true,
RaiseInformationEvents = true
}
};
coreApp.UseIdentityServer(options);
});
我需要了解在注册时将 user
映射到 client
的最佳方法,并确保用户只能登录到该客户端,而不能登录任何其他客户端,即使 [=36] =] 和 password
对于该用户的所有客户端都是相同的。
如果您在 id_token 中提供一个自定义声明,名为 application_access
,其值为用户有权访问的 application/client,会怎样?其中可能有很多。您可以在 IUserService
实施中添加此自定义声明,并以与映射其他用户特定声明相同的方式映射它。
然后,在您的客户端中,检查该特定声明并检查登录用户是否具有针对该特定客户端的特定值的声明。
(标记:RequireScope
用于访问令牌和 API,但您已了解总体思路)
我正在使用 IdentityServerV3
为少数客户端验证用户。我在以特定 user
必须能够登录到特定“客户端”的方式配置 IdentityServer
时遇到问题。
让我们来看下面的场景:
我有 2 个客户 -> client1
、client2
。
我有 3 个用户 -> user1
、user2
、user3
.
user1
& user2
只能访问 client1
。而 user3
可以单独访问 client2
。
当我尝试使用 user3
登录 client1
时,身份服务器验证成功。我不想要。
public static class Clients
{
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
Enabled = true,
ClientName = "MVC Client",
ClientId = "mvc",
Flow = Flows.Hybrid,
RedirectUris = new List<string>
{
"https://localhost:44319/"
}
},
new Client
{
Enabled = true,
ClientName = "MVC Client 2",
ClientId = "mvc2",
Flow = Flows.Hybrid,
RedirectUris = new List<string>
{
"https://localhost:44319/"
}
}
};
}
}
用户是:
public static class Users
{
public static List<InMemoryUser> Get()
{
return new List<InMemoryUser>
{
new InMemoryUser
{
Username = "bob",
Password = "secret",
Subject = "1",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Bob"),
new Claim(Constants.ClaimTypes.FamilyName, "Smith")
}
},
new InMemoryUser
{
Username = "rob",
Password = "secret",
Subject = "2",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Rob"),
new Claim(Constants.ClaimTypes.FamilyName, "Thompson")
}
},
new InMemoryUser
{
Username = "Jerry",
Password = "secret",
Subject = "3",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Jerry"),
new Claim(Constants.ClaimTypes.FamilyName, "Smith")
}
}
};
}
}
现在 OWIN 启动 class 是:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/identity", idsrvApp =>
{
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Embedded IdentityServer",
SigningCertificate = LoadCertificate(),
Factory = InMemoryFactory.Create(
users : Users.Get(),
clients: Clients.Get(),
scopes : StandardScopes.All)
});
});
}
X509Certificate2 LoadCertificate()
{
return new X509Certificate2(
string.Format(@"{0}\bin\identityServer\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test");
}
}
我不明白 IdentityServer
如何知道哪个用户 bleongs 到哪个客户。 IdentityServer
不支持我的方案,还是我遗漏了什么。
更新
public class LocalRegistrationUserService : UserServiceBase
{
public class CustomUser
{
public string Subject { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public List<Claim> Claims { get; set; }
}
public static List<CustomUser> Users = new List<CustomUser>();
public string ClientId { get; set; }
public override Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
var user = Users.SingleOrDefault(x => x.Username == context.UserName && x.Password == context.Password);
if (user != null)
{
context.AuthenticateResult = new AuthenticateResult(user.Subject, user.Username);
}
return Task.FromResult(0);
}
}
While registering the user i am redirecting him to a controller in Identity Server Host
.
public class LocalRegistrationController : Controller
{
[Route("core/localregistration")]
[HttpGet]
public ActionResult Index(string signin)
{
return View();
}
[Route("core/localregistration")]
[HttpPost]
public ActionResult Index(string signin, LocalRegistrationModel model)
{
var ctx = Request.GetOwinContext();
if (ModelState.IsValid)
{
var user = new LocalRegistrationUserService.CustomUser
{
Username = model.Username,
Password = model.Password,
Subject = Guid.NewGuid().ToString(),
Claims = new List<Claim>()
};
LocalRegistrationUserService.Users.Add(user);
user.Claims.Add(new Claim(Constants.ClaimTypes.GivenName, model.First));
user.Claims.Add(new Claim(Constants.ClaimTypes.FamilyName, model.Last));
return Redirect("~/core/" + Constants.RoutePaths.Login + "?signin=" + signin);
}
return View();
}
}
startup.cs
app.Map("/core", coreApp =>
{
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
// different examples of custom user services
//var userService = new RegisterFirstExternalRegistrationUserService();
//var userService = new ExternalRegistrationUserService();
var userService = new LocalRegistrationUserService();
// note: for the sample this registration is a singletone (not what you want in production probably)
factory.UserService = new Registration<IUserService>(resolver => userService);
factory.ViewService = new Registration<IViewService, CustomViewService>();
var options = new IdentityServerOptions
{
SiteName = "Identity Server 3",
SigningCertificate = Certificate.Get(),
Factory = factory,
AuthenticationOptions = new AuthenticationOptions
{
IdentityProviders = ConfigureAdditionalIdentityProviders,
LoginPageLinks = new LoginPageLink[] {
new LoginPageLink{
Text = "Register",
// Href = "~/externalregistration",
Href = "~/localregistration",
//Href = "localregistration"
},
new LoginPageLink{
Text = "Forgot Password?",
Href = "~/forgotpassword",
//Href = "localregistration"
}
}
},
EventsOptions = new EventsOptions
{
RaiseSuccessEvents = true,
RaiseErrorEvents = true,
RaiseFailureEvents = true,
RaiseInformationEvents = true
}
};
coreApp.UseIdentityServer(options);
});
我需要了解在注册时将 user
映射到 client
的最佳方法,并确保用户只能登录到该客户端,而不能登录任何其他客户端,即使 [=36] =] 和 password
对于该用户的所有客户端都是相同的。
如果您在 id_token 中提供一个自定义声明,名为 application_access
,其值为用户有权访问的 application/client,会怎样?其中可能有很多。您可以在 IUserService
实施中添加此自定义声明,并以与映射其他用户特定声明相同的方式映射它。
然后,在您的客户端中,检查该特定声明并检查登录用户是否具有针对该特定客户端的特定值的声明。
(标记:RequireScope
用于访问令牌和 API,但您已了解总体思路)