4. 使用IdentityServer4 实现 基于OIDC 的内部MVC客户端认证

1. 概述

  • 上一个实例 中实现了简单的 ResourceOwnerPassword 授权.

  • 本例将使用IdentityServer4 实现内部系统的单点登录,让组织内部的MVC 客户端使用认证中心的登录页实现登录,使用的是OAuth2 的隐式授权码模式Implicit AthorizationCode

  • MVC 客户端集成IdentityServer4 认证时,为简单起见,将使用cookie存储客户端凭据;同时启用IdentityServer4 内置的 OpenIdConnect(OIDC)身份认证服务。

2. 关键概念

  • IdentityResource : OIDC 协议中定义的身份认证资源,这种资源具有唯一名称,资源声明后将包含在用户的身份令牌中。参考: https://docs.identityserver.io/en/3.1.0/topics/resources.html

  • IIdentityServerInteractionService : 这是IdentityServer4提供的认证交互接口。

3. 准备MVC Web客户端

新建一个MVCClient 项目,设置访问地址和端口为:http://localhost:5030。

3.1 引用依赖包

 <PackageReference Include="IdentityServer4" Version="4.1.1" />

3.2 注册OIDC 服务,并启用认证中间件

// DI容器中注册服务
services.AddAuthentication(opt =>
            {
                opt.DefaultScheme = "Cookies";
                opt.DefaultChallengeScheme = "oidc";
            }).AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.SignInScheme = "Cookies";
                options.Authority = "http://localhost:5010";
                options.RequireHttpsMetadata = false;

                options.ClientId = "mvc";
                options.SaveTokens = true;
            });

// ConfigureService 中启用中间件
            app.UseAuthentication();

// 将受保护页面使用【Authorize】 保护起来,访问首页时将重定向到Account/Login
 [Authorize]
    public class HomeController : Controller
{
……  ……
}

4. 服务端配置

4.1 添加IdentityResource(在server端的Config 类中添加) 和client

        public static List<IdentityResource> GetIdentityResources()
        {
            var result = new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile()
            };
            return result;
        }

// 新增一个client
           new Client
            {
                ClientId="mvc",
                ClientName="MVC Client",
                RequireConsent=false,
                AllowedGrantTypes= GrantTypes.Implicit, // 隐式授权码
                RedirectUris={"http://localhost:5030/signin-oidc"}, // 这里的回调地址是MVC客户端的地址
                PostLogoutRedirectUris={"http://localhost:5030/signout-callback-oidc"},
                AllowedScopes= new List<string>
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile
                }

            }

// Startup 注册IdentityResource

services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryApiResources(Config.GetApiResources)
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryClients(Config.GetClients)
                .AddInMemoryApiScopes(Config.GetApiScopes) // 3.1 新增的坑,不加会报invalid_scope
                .AddTestUsers(Config.GetTestUsers())
                ;

5. 测试验证

启动服务端5010,和客户端5030 此时客户端将重定向到5010 的登录页Account/Login

可以输入TestUsers中配置的用户名密码登录

5.1 Account/Login 的登录方法

       [HttpPost]
        public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
        {
            ViewData["ReturnUrl"] = returnUrl;
            var user = _users.FindByUsername(model.UserName);

            if (user == null)
            {
                ModelState.AddModelError(nameof(model.UserName), "User not exists.");
            }
            else
            {
                if (_users.ValidateCredentials(model.UserName, model.Password))
                {
                    var props = new AuthenticationProperties
                    {
                        IsPersistent = true,
                        ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30))
                    };
                    var u = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username };

                    await Microsoft.AspNetCore.Http.AuthenticationManagerExtensions.SignInAsync(
                          this.HttpContext,
                          u,
                          props);
                    return Redirect(returnUrl);
                }
            }
            return View();

        }
posted @ 2020-11-09 17:40  aimigi  阅读(714)  评论(0编辑  收藏  举报