IdentityServer4学习及简单使用 一. IdentityServer的预备知识 二.  IdentityServer简单介绍 三.简单项目示例 IdentityServer集成MVC Web Application

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

本文,主要用来记录IdentityServer4的简单使用。

要学习IdentityServer,需要了解下基于Token的验证体系,其中涉及到Token, OAuth&OpenID,JWT,协议规范等。

如图过程,

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

二.  IdentityServer简单介绍

IdentityServer4 是一个基于OpenID ConnectOAuth 2.0的针对ASP.NET Core 2.0的框架,以中间件的形式存在。

通常你可以构建(或重新使用)包含登录和注销页面的应用程序,IdentityServer中间件会向其添加必要的协议头,以便客户端应用程序可以使用这些标准协议与其对话。

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

我们可以用IdentityServer来做什么?

  1. 身份验证服务:官方认证的OpenID Connect实现
  2. 单点登录/注销(SSO)
  3. 访问受控的API : 为不同的客户提供访问API的令牌,比如:MVC网站、SPAMobile APP
  4. ...等等

三.简单项目示例

先列出目录结构,以及创建顺序,来方便阅读

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

IdentityServerDemo --> APIService1和APIService2 --> MVCClient

其中,处MVCClient是asp.net core web mvc项目外,其他都是asp.net core web api 项目

创建名为IdentityServerDemo的认证服务

1. 创建一个asp.net core web api项目:IdentityServerDemo

注意,不要设置HTTPS,否则后面使用postman测试时,会no response

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

2. 添加InMemoryConfiguration

public class InMemoryConfiguration
    {
        public static IConfiguration Configuration { get; set; }
        /// <summary>
        /// Define which APIs will use this IdentityServer
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiResource> GetApiResources()
        { 
            return new[]
            {
                new ApiResource("clientservice", "CAS Client Service"),
                new ApiResource("productservice", "CAS Product Service"),
                new ApiResource("agentservice", "CAS Agent Service")
            };
        }

        /// <summary>
        /// Define which Apps will use thie IdentityServer
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client
                {
                    ClientId = "client.api.service",
                    ClientSecrets = new [] { new Secret("clientsecret".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                    AllowedScopes = new [] { "clientservice" }
                },
                new Client
                {
                    ClientId = "product.api.service",
                    ClientSecrets = new [] { new Secret("productsecret".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                    AllowedScopes = new [] { "clientservice", "productservice" }
                },
                new Client
                {
                    ClientId = "agent.api.service",
                    ClientSecrets = new [] { new Secret("agentsecret".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                    AllowedScopes = new [] { "agentservice", "clientservice", "productservice" }
                }
            };
        }

        /// <summary>
        /// Define which uses will use this IdentityServer
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<TestUser> GetUsers()
        {
            return new[]
            {
                new TestUser
                {
                    SubjectId = "10001",
                    Username = "test1@hotmail.com",
                    Password = "test1password"
                },
                new TestUser
                {
                    SubjectId = "10002",
                    Username = "test2@hotmail.com",
                    Password = "test2password"
                },
                new TestUser
                {
                    SubjectId = "10003",
                    Username = "test3@hotmail.com",
                    Password = "test3password"
                }
            };
        }
    }
View Code

3. 使用nuget管理器,添加IdentityServer4 ,并且修改StartUp.cs

修改StartUp.cs中的Configure方法

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //启用IdentityServer
            app.UseIdentityServer();
            app.UseMvc();
        }

修改StartUp.cs中的ConfigureServices方法

public void ConfigureServices(IServiceCollection services)
        {
            //添加IdentityServer
            services.AddIdentityServer()
                       .AddDeveloperSigningCredential()
                       .AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
                       .AddInMemoryClients(InMemoryConfiguration.GetClients())
                       .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

这个主要是为了把IdentityServer注册到容器中,需要对其进行配置,而这个配置主要包含三个信息:

  1. 哪些api可以使用这个AuthorizationServer
  2. 哪些client可以使用这个AuthorizationServer
  3. 哪些User可以被这个AuthorizationServer识别并授权

这里的AuthorizationServer 指的就是这个项目的服务:用来认证及授权使用的.

这里是使用基于内存的方式。

对于Token签名需要一对公钥和私钥,IdentityServer为开发者提供了一个AddDeveloperSigningCredential()方法,它会帮我们搞定这个事情并且存储到硬盘。当切换到正式环境,需要使用真正的证书,更换为

public void ConfigureServices(IServiceCollection services)
    {
        InMemoryConfiguration.Configuration = this.Configuration;

        services.AddIdentityServer()
            .AddDeveloperSigningCredential()
            .AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
            .AddInMemoryClients(InMemoryConfiguration.GetClients())
            .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());
    }
View Code

此项目,暂时不使用正式的证书了。

4.使用postman获取token

启动我们的IdentityServerDemo 项目,

然后使用postman发送请求

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

5.引入QuickStartUI

IdentityServer为我们提供了一套UI以使我们能快速的开发具有基本功能的认证/授权界面,下载地址:QuickStartUI

QuickStartUI引入到我们的项目中,目录结构如下:

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

5.修改StartUp.cs

修改Configure方法

添加静态文件中间件

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //启用IdentityServer
            app.UseIdentityServer();
            //for QuickStart-UI 启用静态文件
            app.UseStaticFiles();
            //app.UseMvc();
            app.UseMvcWithDefaultRoute(); //这里带有默认的路由
        }

6.运行程序

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

登录

点击here

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

登出

 IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

IdentityServer集成API Service

1.  添加asp.net core web api项目

注意,这里也是使用http方式;

2.nuget中安装IdentityServer4.AccessTokenValidation 

3.修改StartUp.cs文件

修改configureServices方法

public void ConfigureServices(IServiceCollection services)
        {
            //IdentityServer
            services.AddMvcCore().AddAuthorization().AddJsonFormatters();
            services.AddAuthentication(Configuration["Identity:Scheme"])
                        .AddIdentityServerAuthentication(options =>
                        {
                            options.RequireHttpsMetadata = false; //是否需要https
                            options.Authority = $"http://{Configuration["Identity:IP"]}:{Configuration["Identity:Port"]}";  //IdentityServer授权路径
                            options.ApiName = Configuration["Service:Name"];  //需要授权的服务名称
                        });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

修改Configure方法

UseMvc()之前启用Authentication中间件

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

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

            app.UseMvc();
        }

修改appsettings.json文件

{
  "Service": {
    "Name": "clientservice", //本服务的名称
    "Port": "53064",  //本服务的端口号,根据自己服务启动时的端口号进行更改
    "DocName": "clientservice",
    "Version": "v1",
    "Title": "CAS Client Service API",
    "Description": "CAS Client Service API provide some API to help you get client information from CAS",
    "Contact": {
      "Name": "CAS 2.0 Team",
      "Email": "EdisonZhou@manulife.com"
    },
    "XmlFile": "Manulife.DNC.MSAD.IdentityServer4Test.ApiService01.xml"
  },
  "Identity": { //去请求授权的Identity服务,这里即IdentityServerDemo的服务启动时的地址
    "IP": "localhost",
    "Port": "49363",  //IdentityServerDemo项目启动时的端口号,根据实际情况修改
    "Scheme": "Bearer"
  }
}

 上面是APIService1的添加,对应的服务名称是clientservice;

 APIService2与之类似,只是把appsettings.json中的clientservice改为productservice.

4. APIService1APIService2Controller添加[Authorize]特性

 [Authorize]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        ......
    }

 

5. 测试

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

注意,这里模拟的是clientservice服务(APIService1)去认证服务器请求token的过程,所以请求到token,也应该在获取clientservice相关授权的时候携带这个token.

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

 

如果在请求productservice的授权服务中,使用clientservicetoken则会显示未授权

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

过程总结:

  1. 首先,在授权服务中,设置需要请求的ApiResource,client,user
  2. postman(相当于client)中,输入client的相关信息(client_id,client_serect)去请求token
  3. 然后就可以根据授权服务中相应client的AllowedScopes设置的范围来请求服务了。

授权服务中的client设置

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

IdentityServer集成MVC Web Application

1. 新建一个ASP.NET Core MVC项目:MVCClient

 2.为指定方法添加[Authorize]特性

我们为HomeController下的Privacy方法上添加Authorize特性

     [Authorize]
        public IActionResult Privacy()
        {
            return View();
        }

这个时候,直接访问Privacy,会报错

而我们希望的效果是:当用户第一次点击Privacy,页面重定向到验证服务(IdentityServerDemo),当用户登录验证授权后,再重定向到该网站。

此后一定时间范围内的第二次,第三次点击,都不需要再重定向到验证服务,而是直接读取保存的token.

3.  MVCClient项目添加OpenID Connect Authentication

而这部分主要集中于做Authentication(身份验证)而非Authorization(授权)

public void ConfigureServices(IServiceCollection services)
        {
            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;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            //这部分主要是做身份验证的(Authentication),而不是授权(Authorization)
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc"; //oidc => open id connect
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.SignInScheme = "Cookies";
                options.Authority = $"http://{Configuration["Identity:IP"]}:{Configuration["Identity:Port"]}";
                options.RequireHttpsMetadata = false;
                options.ClientId = "cas.mvc.client.implicit";
                options.ResponseType = "id_token token";  //允许返回access token
                options.SaveTokens = true;
            });

        }

这里我们使用的是implicit这个flow,它主要用于客户端应用程序(主要指基于javascript的应用),它允许客户端程序重定向到验证服务(IdentityServerDemo),而后带着token重定向回来。

另外,这里的ResponseType为”id_token token”,表示既获取id_token也获取access_token. 而SaveTokens设置为true,表示会将从验证服务返回的token持久化到cookie中,这样就不用每次请求token了。

另在configure方法中,设置Authentication中间件:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseAuthentication();

            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

主要Authentication中间件,要再UseMvc之前。

4. 修改app.settings

{
  "Service": {
    "Name": "cas.mvc.client.implicit", //本服务的名称
    "Port": "56458",  //服务端口号,根据实际情况调整
    "DocName": "cas.mvc.client.implicit",
    "Version": "v1",
    "Title": "CAS Client Service API",
    "Description": "CAS Client Service API provide some API to help you get client information from CAS",
    "Contact": {
      "Name": "CAS 2.0 Team",
      "Email": "EdisonZhou@manulife.com"
    },
    "XmlFile": "Manulife.DNC.MSAD.IdentityServer4Test.ApiService01.xml"
  },
  "Identity": { //去请求授权的Identity服务
    "IP": "localhost",
    "Port": "49363"
  }
}

其中port根据自己此服务启动后的端口号修改

5.在验证服务(IdentityServerDemo)中添加MvcClient

修改 InMemoryConfiguration 中的GetClients方法:

public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client
                {
                    ClientId = "client.api.service",
                    ClientSecrets = new [] { new Secret("clientsecret".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                    AllowedScopes = new [] { "clientservice" }
                },
                new Client
                {
                    ClientId = "product.api.service",
                    ClientSecrets = new [] { new Secret("productsecret".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                    AllowedScopes = new [] { "clientservice", "productservice" }
                },
                new Client
                {
                    ClientId = "agent.api.service",
                    ClientSecrets = new [] { new Secret("agentsecret".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                    AllowedScopes = new [] { "agentservice", "clientservice", "productservice" }
                },
                new Client
                {
                    ClientId = "cas.mvc.client.implicit",
                    ClientName = "CAS MVC Web App Client",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    RedirectUris = { $"http://localhost:56458/signin-oidc" },
                    PostLogoutRedirectUris = { $"http://localhost:56458/signout-callback-oidc" },
                    AllowedScopes = new [] {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "agentservice", "clientservice", "productservice"
                    },
                    AllowAccessTokensViaBrowser = true // can return access_token to this client
                },
            };
        }

这里ClientId要和MvcClient中设置的一样。

RedirectUris是指登录成功以后需要重定向的地址(即重定向到MvcClient中的地址)

PostLogoutRedirectUris是指登出之后需要重定向的地址。

API Service Client的设置不同的就是AllowedScopes中给它增加了OpenIdProfile,因为我们为MvcClient设定的是oidc而不是bearer模式。

最后为了使用这些OpenID Connect Scopes,需要设置这些Identity Resources。

 

InMemoryConfiguration 中增加GetIdentityResources方法:

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

ConfigureServices方法中修改:

 public void ConfigureServices(IServiceCollection services)
        {
            //添加IdentityServer
            services.AddIdentityServer()
                       .AddDeveloperSigningCredential()
                       .AddInMemoryIdentityResources(InMemoryConfiguration.GetIdentityResources())
                       .AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
                       .AddInMemoryClients(InMemoryConfiguration.GetClients())
                       .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

6. MvcClient项目的Privacy 页面中修改如下:

@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>


@using Microsoft.AspNetCore.Authentication
<div>
    <strong>id_token</strong>
    <span>@await ViewContext.HttpContext.GetTokenAsync("id_token")</span>
</div>
<div>
    <strong>access_token</strong>
    <span>@await ViewContext.HttpContext.GetTokenAsync("access_token")</span>
</div>

<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

这里,我们会把id_token和access_token显示出来

7. 为了退出方便,暂时在HomeController下增加Logout方法

 public async Task Logout()
        {
            await HttpContext.SignOutAsync("Cookies");
            await HttpContext.SignOutAsync("oidc");
        } 

8. 简单测试

启动IdentityServerDemo这个验证服务;

启动MvcClient这个Mvc Web Application服务;

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

 IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

 IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

这里没有添加可点击的按钮,可直接在url中修改路径来登出

IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

 IdentityServer4学习及简单使用
一. IdentityServer的预备知识
二.  IdentityServer简单介绍
三.简单项目示例
IdentityServer集成MVC Web Application

参考网址:

https://www.cnblogs.com/edisonchou/p/identityserver4_foundation_and_quickstart_01.html

 另外推荐edisonchou微服务系列,感觉非常棒

 https://github.com/Vincent-yuan/IdentityServerDemo