zoukankan      html  css  js  c++  java
  • 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增删改查,然后因为每次请求的时候都是从数据库读取数据的,如果数据被修改了,会立即生效。

  • 相关阅读:
    mysql数据库常用指令
    解决windows的mysql无法启动 服务没有报告任何错误的经验。
    “Can't open file for writing”或“operation not permitted”的解决办法
    启动Apache出现错误Port 80 in use by "Unable to open process" with PID 4!
    如何打开windows的服务services.msc
    常见的HTTP状态码 404 500 301 200
    linux系统常用的重启、关机指令
    (wifi)wifi移植之命令行调试driver和supplicant
    linux(debian)安装USB无线网卡(tp-link TL-WN725N rtl8188eu )
    alloc_chrdev_region申请一个动态主设备号,并申请一系列次设备号
  • 原文地址:https://www.cnblogs.com/norain/p/IdentityServer.html
Copyright © 2011-2022 走看看