• IdentityServer4的最佳使用


    简介

      本人做微服务项目也有一段时间了,在微服务中让我感触颇深的就是这个IdentityServer4了,ID4(IdentityServer4的简称)中涉及的概念颇多,本文不谈概念(我怕读者没耐心看下去),在这分享下我个人的使用心得。

    目录

      1. ID4简介

      2. ID4使用

    ID4简介

      相信大家都知道面向对象中的封装(把通用的功能封装起来,减少程序中大量重复代码),我们知道在一个单体系统中有很多的重复模块,例如:身份认证、权限控制检查等,在单体系统中,这些都可以使用aop统一在一个地方控制。而在分布式系统,每个系统都需要进行身份认证、权限检查等。这时,每个系统都得写一套同样的代码来进行这些控制,我们能不能像单体系统那样在一个地方进行这些流程呢?这时我们可以使用IdentityServer4来实现。

      IdentityServer4是一个集成 身份认证和授权 的组件,使用OpenId Connect(身份识别框架) 和 Auth2.0(授权框架)来进行身份认证和授权的。

    ID4使用

    我这里只列出几个主要的类,其它,可以下载项目来看。关于如何使用,代码有点多,我比较懒,就没怎么讲解,感兴趣的小伙伴可以加个QQ:1983702356 来讨论下。

    WEBAPPLICATION1 (IDENTITYSERVER4)项目,安装 INSTALL-PACKAGE IDENTITYSERVER4 -VERSION 2.5.0,

    1. 以下几个类比较关键

      1. Config.cs。主要是获取身份资源

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        public class Config
        {
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
        return new List<IdentityResource>
        {
        new IdentityResources.OpenId(), //必须要添加,否则报无效的 scope 错误
        new IdentityResources.Profile(),
        new IdentityResources.Email()
        };
        }
        }
      2. TestClientStore.cs 加载IdentityServer4的client

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        /// <summary>
        /// 加载IdentityServer4的client
        /// </summary>
        public class TestClientStore : IClientStore
        {
        /// <summary>
        /// Service层中的一个接口
        /// </summary>
        public IClientService ClientSvc { get; set; }


        public TestClientStore(IClientService ClientSvc)
        {
        this.ClientSvc = ClientSvc;
        }
        public async Task<Client> FindClientByIdAsync(string clientId)
        {
        var dto = await ClientSvc.FindClientByIdAsync(clientId);
        if (dto==null)
        {
        return null;
        }
        var scopes = dto.APIResources.Select(e => e.Name).ToList();
        scopes.Add(IdentityServerConstants.StandardScopes.OpenId);
        scopes.Add(IdentityServerConstants.StandardScopes.Profile);
        return new Client
        {
        ClientId = dto.Client.Id,//API账号、客户端Id
        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
        ClientSecrets =
        {
        new Secret(dto.Client.Secret.Sha256())//秘钥
        },
        AllowedScopes = scopes//这个账号支持访问哪些应用
        };
        }
        }
      3. TestReourceStore.cs,加载IdentityServer的APIResource

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        /// <summary>
        /// 加载IdentityServer的APIResource
        /// </summary>
        public class TestReourceStore : IResourceStore
        {
        public IApiResourceService resourceSvc { get; set; }
        public TestReourceStore(IApiResourceService resourceSvc)
        {
        this.resourceSvc = resourceSvc;
        }
        public async Task<ApiResource> FindApiResourceAsync(string name)
        {
        var entity = await resourceSvc.GetByNameAsync(name);
        return new ApiResource(entity.Name,entity.DisplayName);
        }

        public async Task<IEnumerable<ApiResource>> FindApiResourcesByScopeAsync(IEnumerable<string> scopeNames)
        {
        var list = await resourceSvc.GetDatasByNamesAsync(scopeNames);
        return list.Select(e=>new ApiResource(e.Name,e.DisplayName));
        }

        public async Task<IEnumerable<IdentityResource>> FindIdentityResourcesByScopeAsync(IEnumerable<string> scopeNames)
        {
        return Config.GetIdentityResources().Where(e => scopeNames.Contains(e.Name)).ToList();
        }

        public async Task<Resources> GetAllResourcesAsync()
        {
        var list = await resourceSvc.GetNoramlAll();

        var resouces = list.Select(e => new ApiResource(e.Name, e.DisplayName)).ToList();
        return new Resources
        {
        ApiResources = resouces
        };

        }
        }
      4. TestResourceOwnerPasswordValidator.cs,IdentityServer4登录验证

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        /// <summary>
        /// IdentityServer4登录验证
        /// </summary>
        public class TestResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
        {
        public IUserService UserSvc { get; set; }
        public TestResourceOwnerPasswordValidator(IUserService UserSvc)
        {
        this.UserSvc = UserSvc;
        }
        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
        string account = context.UserName;
        string pwd = context.Password;

        var loginResult = await UserSvc.Login(account, pwd);

        if (loginResult == null)
        {
        context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential");
        return;
        }
        context.Result = new GrantValidationResult(
        subject: context.UserName,
        authenticationMethod: "custom",
        claims: new Claim[] {
        new Claim("Name",context.UserName),
        new Claim("UserId",loginResult.Id),
        new Claim("Roles","Admin,Contact"), //模拟获取登录用户的角色信息
        new Claim("Premissions","List,Delete") //模拟获取登录用户的权限信息
        });

        }
        }
      5. ProfileService.cs,用户信息

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        public class ProfileService : IProfileService
        {
        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
        var claims = context.Subject.Claims.ToList();
        context.IssuedClaims = claims.ToList();
        }

        public async Task IsActiveAsync(IsActiveContext context)
        {
        context.IsActive = true;
        }
        }
    1. Startup类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    public class Startup
    {
    public Startup(IConfiguration configuration)
    {
    Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    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.AddAuthentication("Bearer")
    // .AddIdentityServerAuthentication(options =>
    // {
    // options.Authority = "http://localhost:9500";//identity server 地址
    // options.RequireHttpsMetadata = false;
    // });

    string conStr = Configuration["connectionString"];
    services.AddDbContext<TestDbContext>(options =>
    {
    options.UseMySql(conStr);
    });


    ///依赖注入Service层
    AddSigletons(services);

    #region ID4服务配置
    var id4Build = services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryIdentityResources(Config.GetIdentityResources());
    //加载apiresource
    id4Build.Services.AddTransient<IResourceStore, TestReourceStore>();
    //加载client
    id4Build.Services.AddTransient<IClientStore, TestClientStore>();
    //登录验证
    id4Build.Services.AddTransient<IResourceOwnerPasswordValidator, TestResourceOwnerPasswordValidator>();
    //加载profile。profile是用户信息
    id4Build.Services.AddTransient<IProfileService, ProfileService>();
    #endregion


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


    }

    public void AddSigletons(IServiceCollection services)
    {
    var assem = Assembly.Load("Test.Service");
    var list = assem.GetTypes().Where(e => e.IsAbstract == false && typeof(ISignFac).IsAssignableFrom(e));
    foreach (var instanType in list)
    {
    foreach (var item in instanType.GetInterfaces())
    {
    services.AddSingleton(item, instanType);
    }
    }
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    if (env.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    }
    else
    {
    app.UseExceptionHandler("/Home/Error");
    }
    //插入id4中间件
    app.UseIdentityServer();
    //app.UseAuthentication();
    //app.UseStaticFiles();
    //app.UseCookiePolicy();
    //app.UseMvc(routes =>
    //{
    // routes.MapRoute(
    // name: "default",
    // template: "{controller=Home}/{action=Index}/{id?}");
    //});


    }
    }
    1. WebApplication3 API项目,修改Startup,并添加一个控制器,在方法上打上一个标签
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56

      public class Startup
      {
      public Startup(IConfiguration configuration)
      {
      Configuration = configuration;
      }

      public IConfiguration Configuration { get; }

      // This method gets called by the runtime. Use this method to add services to the container.
      public void ConfigureServices(IServiceCollection services)
      {
      services.AddAuthentication("Bearer")
      .AddIdentityServerAuthentication(options =>
      {
      options.Authority = "http://localhost:9500";//identity server 地址
      options.RequireHttpsMetadata = false;
      });

      string conStr = Configuration["connectionString"];
      services.AddDbContext<TestDbContext>(options =>
      {
      options.UseMySql(conStr);
      });


      ///依赖注入Service层
      AddSigletons(services);
      services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      }

      public void AddSigletons(IServiceCollection services)
      {
      var assem = Assembly.Load("Test.Service");
      var list = assem.GetTypes().Where(e => e.IsAbstract == false && typeof(ISignFac).IsAssignableFrom(e));
      foreach (var instanType in list)
      {
      foreach (var item in instanType.GetInterfaces())
      {
      services.AddSingleton(item, instanType);
      }
      }
      }
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
      {
      if (env.IsDevelopment())
      {
      app.UseDeveloperExceptionPage();
      }
      app.UseAuthentication();

      app.UseMvc();
      }
      }

    控制器

    运行CONSOLEAPP1控制台项目, 生成数据库,添加几条数据

    运行ConsoleApp1控制台项目

    运行效果

    1. 在 WebApplication1 目录下运行cmd命令 dotnet run ,启动IdentitServer4,端口是9500

    2. 运行项目 WebApplication3 ,端口是5000

    3. 当我们直接调用 WebApplication3 API中的方法时,发现返回状态码为 401

    4. 请求Id4,复制返回的 access_token

    5. 再请求WebApplication3 API,并在报文头带上token

    结束语

      个人认为我这种使用方式和其它使用方式最大的好处就是,可以写一个IdentityServer4的Client、APIResource增删改查,然后因为每次请求的时候都是从数据库读取数据的,如果数据被修改了,会立即生效。

  • 相关阅读:
    jQuery的事件处理
    《CSS mastery》 读书笔记
    javascript 基础三
    javascript 基础二
    webpack全局引入jquery的方法
    解决mongoVue的collections不显示问题
    javascript 实现一个回文数字
    nth-child和:nth-of-type的区别
    iscroll5实现一个下拉刷新上拉加载的效果
    js(jquery)解决input元素的blur事件和其他非表单元素的click事件冲突的方法
  • 原文地址:https://www.cnblogs.com/norain/p/IdentityServer.html
Copyright © 2020-2023  润新知