【问题标题】:Identity Server 4 | MVC | Set User in Context by RequestPasswordTokenAsync身份服务器 4 | MVC |通过 RequestPasswordTokenAsync 在上下文中设置用户
【发布时间】:2020-04-14 11:02:40
【问题描述】:

首先 - 我在 google 和 stack-overflow 上检查了 2 天.... 我发现数以千计的示例和教程仍然有遗漏,并且没有完整的画面。

所以:

我的架构:

1) 身份服务器 2) 5 个 +/- MVC 网站(如生产网站、全球管理员、帮助台等)(受身份服务器保护) 3) 数十个微服务(受身份服务器保护)

现在 - 我还没有完全理解:

1) 登录: 现在我设置了重定向流程。我的意思是....在网站中我设置了身份服务器,例如:

 services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.SignInScheme = "Cookies";
                options.Authority = "https://localhost:44396";
                options.RequireHttpsMetadata = true;

                options.ClientId = "<<Here is client ID>>";
                options.ClientSecret = "<<HERE IS PASSWORD>>";
                options.ResponseType = "code id_token";

                options.SaveTokens = true;
                options.GetClaimsFromUserInfoEndpoint = true;

                options.Scope.Add("api1.read");
                options.Scope.Add("offline_access");
            });

还有

 app.UseHttpsRedirection();

            app.UseAuthentication();

            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

现在,如果用户尝试使用 Autorize 属性打开页面 - 用户重定向到身份服务器登录那里并返回到受保护的页面。一切正常。

但是……

1) 我想登录 MVC 页面。无需重定向到 Identity Server。 我上网查了一下,发现需要使用identityserver resource owner password flow

然后我将 IdentityServer 设置为:

 new Client {
                    ClientId = "myclient",
                    ClientName = "My first client",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,// GrantTypes.HybridAndClientCredentials,
                    ClientSecrets = new List<Secret> { new Secret("superSecretPassword".Sha256())},
                    AllowedScopes = new List<string> { "openid", "profile", "api1.read", IdentityServerConstants.StandardScopes.Email},
                    AllowOfflineAccess = true,
                    RedirectUris = { "https://localhost:44321/signin-oidc" },
                    RequireConsent = false
                 },

在我的 MVC 中我可以获得令牌:

public static async Task HandleToken(this HttpClient client, string authority, string clientId, string secret, string apiName)
        {

            var accessToken = await client.GetRefreshTokenAsync(authority, clientId, secret, apiName);
            client.SetBearerToken(accessToken);
        }

        private static async Task<string> GetRefreshTokenAsync(this HttpClient client, string authority, string clientId, string secret, string apiName)
        {
            var disco = await client.GetDiscoveryDocumentAsync(authority);
            if (disco.IsError) throw new Exception(disco.Error);

            var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
            {
                UserName = "<<HERE IS USERNAME>>",
                Password = "<<HERE IS PASSWORD>>",
                Address = disco.TokenEndpoint,
                ClientId = clientId,
                ClientSecret = secret,
                Scope = apiName
            });




 var user_info = await client.GetUserInfoAsync(new UserInfoRequest() { Address = disco.UserInfoEndpoint, Token = tokenResponse.AccessToken });

Here I have all user claims and Now I want set them in Controller => User




                if (!tokenResponse.IsError) return tokenResponse.AccessToken;
                return null;
            }

现在我得到了令牌......很好............但是

2 个问题:

1) 如何在 Controller.User (ClaimsPrincipal) 中设置用户身份?

**** 更新 我找到了一个解决方案

我可以使用HttpContext.SignInAsync,并且在我从上面的代码中获得令牌和用户信息之后 - 我可以登录我的 Web MVC 项目并手动设置用户声明。如果这是好方法?

2) 对用户个人资料数据的所有操作,例如 ChangePassword、Update FirstName、LastName 等... 我需要怎么做?? 为身份成员构建微服务?

P.S - 在 IdentityServer 我使用 Asp Identity :

 services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            var builder = services.AddIdentityServer(options =>
                {
                    options.Events.RaiseErrorEvents = true;
                    options.Events.RaiseInformationEvents = true;
                    options.Events.RaiseFailureEvents = true;
                    options.Events.RaiseSuccessEvents = true;
                })
                .AddInMemoryIdentityResources(Config.Ids)
                .AddInMemoryApiResources(Config.Apis)
                .AddInMemoryClients(Config.Clients)
                .AddAspNetIdentity<ApplicationUser>();

最后一个问题是:

如果我想使用 DynamoDB 作为用户存储 - 那么我需要通过自定义 Identity Provider 构建吗? (对吗??)

我在github找到了这个解决方案,我只需要更新到Asp Core 3.1

https://github.com/c0achmcguirk/AspNetIdentity_DynamoDB

【问题讨论】:

    标签: c# asp.net-core identityserver4


    【解决方案1】:

    对于第一个问题,您只需要为 Identity Server 配置 API,然后当客户端发出适当的请求时,它将自动填充。 (包括其访问令牌)

    示例 API 配置

    public void ConfigureServices(IServiceCollection services)
    {
      services.AddControllers();
      services.AddCors(r => r.AddDefaultPolicy(o =>
                                               o.AllowAnyOrigin()
                                               .AllowAnyMethod()
                                               .AllowAnyHeader()));
      services.AddAuthentication()
               .AddJwtBearer(options =>
                            {
                              options.Audience = "apix"; // this apis scope
                              options.Authority = "http://localhost:5000"; // Identity server url                                                
                             });
    
      services.AddAuthorization(options =>
      {
         options.DefaultPolicy =
                         new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
                        .RequireAuthenticatedUser()
                        .Build();
                });
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
    
              // ...
                app.UseCors();
    
                app.UseAuthentication();
                app.UseAuthorization();
    
              // ...
            }
    
    

    您还需要使用[Authorize] 属性装饰您的API 方法。

    对于第二个问题,这是一个偏好问题。有一个名为 QuickStart 的模板,其中包含使用 IdentityServer4 的用户操作,它以 MVC 方式处理这些操作。您还可以创建 WEB API 并公开它们。而且您可能不需要为此创建单独的微服务,因为 IdentityServer 本身就是一个 WEB 应用程序。

    对于最后一个问题,人们通常会修改旧的存储库以使其与 DynamoDb 一起使用。赞这个one

    编辑: 对于问题How to set up the MVC to set User Claims after ResourceOwner flow login

    您需要实现一个IProfileService 服务并在启动时注册它。 (身份服务器)

    public async  Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
      var subject = context.Subject;
      var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
      var user = await _userManager.FindByIdAsync(subjectId); 
      var claims = GetClaimsFromUser(user,context.Caller); // here is the magic method arranges claims according to user and caller
      context.IssuedClaims = claims.ToList();
    }
    

    【讨论】:

    • 是的,身份服务器是 MVC - 但我不想让用户访问身份服务器。我有 3 个网站,针对 3 种不同类型的用户,我想在这个网站中让用户更改他的数据。并且 - 是的 - API 自动获取用户数据 - 我的问题是如何设置 MVC 以在 ResourceOwner 流登录后设置用户声明
    • 但是 _userManager 意味着我的 MVC 网站需要访问数据库。这样不好。如果我做重定向流程 - 我会自动在声明中获取用户数据。
    • @DavidAbaev 它不是客户端应用程序。它应该驻留在 IdentityServer 端。而IProfileService 属于 IdentityServer 而不是 AspNetIdentity。
    • 现在我在我的客户端应用程序中添加:Scope = apiName + "openid profile" 在我得到令牌后,我调用 UserInfo 端点: var user_info = await client.GetUserInfoAsync(new UserInfoRequest() { Address = disco.UserInfoEndpoint, 令牌 = tokenResponse.AccessToken });而且我有我的所有声明......现在我想将此声明添加到 Controller => User
    • @DavidAbaev 您应该通过配置而不是手动进行。
    猜你喜欢
    • 1970-01-01
    • 2018-07-01
    • 2022-01-16
    • 1970-01-01
    • 2019-07-19
    • 2018-01-24
    • 2020-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多