c# - 使用 IdentityServer 向特定客户端验证特定用户

标签 c# authentication identityserver3 owin-middleware

我正在使用 IdentityServerV3 对少数客户端的用户进行身份验证。我在配置 IdentityServer 时遇到问题,特定的 user 必须能够登录到特定的“客户端”。

让我们来看下面的场景:

我有 2 个客户端 -> client1, client2。 我有 3 个用户 -> user1user2user3

user1user2 只能访问 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 启动类是:

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

在注册用户时,我将他重定向到 Identity Server Host 中的 Controller

 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 的最佳方法,并确保用户只能登录到该客户端,而不能登录任何其他客户端,即使usernamepassword 对于该用户的所有客户端都是相同的。

最佳答案

如果您在您的 id_token 中提供自定义声明,名为 application_access,其值为用户有权访问的应用程序/客户端,会怎样?其中可能有很多。您可以在您的 IUserService 实现中添加此自定义声明,并以与映射其他用户特定声明相同的方式映射它。

然后,在您的客户端中,检查该特定声明并检查登录用户是否具有针对该特定客户端的特定值的声明。

https://github.com/IdentityModel/Thinktecture.IdentityModel.45/blob/master/IdentityModel/Thinktecture.IdentityModel/Authorization/WebApi/ScopeAttribute.cs

(标记:RequireScope 用于访问 token 和 API,但您已了解总体思路)

关于c# - 使用 IdentityServer 向特定客户端验证特定用户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38529987/

相关文章:

openid - 如何通过IdentityServer3登录后将用户重定向到原始页面

certificate - 使用 OpenId Connect 时验证签名证书

c# - Nreco PdfGenrator 强制新页面

c# - Lua 或 C 相当于 System.Web.Helpers.Crypto.VerifyHashedPassword

c# - 从 C# 表单登录 Skype

android - 使用 LiveSDK 时出现 'redirect_url' 错误

oauth-2.0 - 发送带有对象标签的 http 请求 header

c# - 扩展可以访问同一程序集中的内部方法并被外部使用吗?

c# - 这段 C# 代码中的 "&="有什么作用?

php - FOS 用户包认证