zoukankan      html  css  js  c++  java
  • IdentityServer4学习及简单使用

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

    一. IdentityServer的预备知识

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

    如图过程,

    二.  IdentityServer简单介绍

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

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

    我们可以用IdentityServer来做什么?

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

    三.简单项目示例

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

    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

    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发送请求

    5.引入QuickStartUI

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

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

    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.运行程序

    登录

    点击here

    登出

     

    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. 测试

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

     

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

    过程总结:

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

    授权服务中的client设置

    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服务;

     

     

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

     

    参考网址:

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

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

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

  • 相关阅读:
    SICP习题 1.11 (一个函数的递归与迭代)
    SICP 实例 ExchangeMoney
    SICP 1.2.2 树形递归 与 线性迭代(斐波那契数)
    SICP习题 1.10(Ackermann函数)
    SICP习题 1.9 (递归与迭代初探)
    SICP实例 1.2.1 (阶乘的递归与迭代)
    SICP习题 1.8 (立方根)
    SICP习题 1.7 (求平方根改进)
    SICP习题 1.6 (再探 函数 与 正则序 应用序 关系)
    SICP实例 1.1.7 (求平方根)
  • 原文地址:https://www.cnblogs.com/Vincent-yuan/p/11006758.html
Copyright © 2011-2022 走看看