NETCORE - IdentityServer4 密码认证

NETCORE - IdentityServer4 密码认证

 

官网:http://www.identityserver.com.cn/Home/Detail/shiyongpingzhengbaohuapi 

ABP中身份认证可参考 :https://www.cnblogs.com/1285026182YUAN/p/15190387.html

Ocelot中的身份认证可参考:https://www.cnblogs.com/1285026182YUAN/p/15402841.html

单点登录参考:https://www.cnblogs.com/1285026182YUAN/p/15251708.html

从数据库账户中验证:https://zhuanlan.zhihu.com/p/112506129 

 https://blog.csdn.net/xiaoxionglove/article/details/114991779

 鉴权独立部署:https://www.jb51.net/article/183505.htm

 

 

IdentityServer4是基于openid+auth2.0基础实现的一整套完善的授权中心

 1. OAuth 2.0是有关如何颁发访问令牌的规范,提供Access Token用于访问服务接口的
 2. OpenID Connect是有关如何发行ID令牌的规范,提供Id Token用于用户身份标识,Id Token是基于JWT格式

 

 

案例环境:vs2019 , net core , webapi , .net5

1. 创建项目:NETCORE.IdentityServer4.Service

 

 增加 Nuget 包,identityServer4

 

 

 

2. 新增 Config.cs 类

  public class Config
    {

        //下载ids4的依赖
        //scopes define the resources in your system
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() };
        }

        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("API1", "第一个接口")
                {
                    //重要
                    Scopes = { "scope1" }
                }
            };
        }

        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new List<ApiScope> { new ApiScope("scope1") };
        }

        public static IEnumerable<TestUser> Users()
        {
            return new[]
            {
                new TestUser
                {
                    SubjectId="1",
                    Username="mail@qq.com",
                     Password="password"
                }
            };
        }

        // clients want to access resources (aka scopes)
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "app",
                    ClientSecrets={new Secret("secret".Sha256())},
            AccessTokenLifetime = 60,//设置 token 有效期 60秒。(过期时间默认偏移5分钟)
                    AllowOfflineAccess = true,//如果要获取refresh_tokens ,必须把AllowOfflineAccess设置为true AllowedGrantTypes
= GrantTypes.ResourceOwnerPasswordAndClientCredentials, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "scope1" } } }; } }

 

 

 3. 修改 startup.cs ,注入 IdentityService4 

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "NETCORE.IdentityServer4.Service", Version = "v1" });
            });

            services.AddIdentityServer()//Ids4服务
                .AddDeveloperSigningCredential()//添加开发人员签名凭据
                .AddTestUsers(Config.Users().ToList())
                .AddInMemoryIdentityResources(Config.GetIdentityResources())//添加内存apiresource
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryApiScopes(Config.GetApiScopes())
                .AddInMemoryClients(Config.GetClients());//把配置文件的client配置资源放到内存
        }

 

 

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NETCORE.IdentityServer4.Service v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            //启动ids4中间件
            app.UseIdentityServer();


        app.UseAuthentication();//添加鉴权验证

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

 

 

4. 启动项目 

此时授权服务中心已经运行,可以访问以下接口获取得到ids4默认discovery endpoint(定义了一个服务发现的规范),它定义了一个api( /.well-known/openid-configuration ),这个api返回一个json数据结构,其中包含了一些OIDC中提供的服务以及其支持情况的描述信息,这样可以使得oidc服务的RP可以不再硬编码OIDC服务接口信息。 

访问 https://localhost:5001/.well-known/openid-configuration

可得到如下数据:

{
    "issuer": "http://localhost:5000", //发行网址,也就是说我们的权限验证站点
    "jwks_uri": "http://localhost:5000/.well-known/openid-configuration/jwks", // 这个接口获取的是公钥,用于验证jwt的数字签名部分(数字签名由sso维护的私钥生成)用的。
    "authorization_endpoint": "http://localhost:5000/connect/authorize", // 授权服务器的授权端点的URL
    "token_endpoint": "http://localhost:5000/connect/token", // 获取token的URL接口
    "userinfo_endpoint": "http://localhost:5000/connect/userinfo", // 根据token获取用户信息
    "end_session_endpoint": "http://localhost:5000/connect/endsession", // 登录注销
    "check_session_iframe": "http://localhost:5000/connect/checksession", // 客户端对check_session_iframe执行监视,可以获取用户的登出状态
    "revocation_endpoint": "http://localhost:5000/connect/revocation", // 这个网址允许撤销访问令牌(仅access tokens 和reference tokens)。它实现了令牌撤销规范(RFC 7009)
    "introspection_endpoint": "http://localhost:5000/connect/introspect", // introspection_endpoint是RFC 7662的实现。 它可以用于验证reference tokens(或如果消费者不支持适当的JWT或加密库,则JWTs)
    "device_authorization_endpoint": "http://localhost:5000/connect/deviceauthorization", // 
    "frontchannel_logout_supported": true, // 可选。基于前端的注销机制
    "frontchannel_logout_session_supported": true, // 可选。基于session的注销机制
    "backchannel_logout_supported": true, // 指示OP支持后端通道注销
    "backchannel_logout_session_supported": true, // 可选的。指定RP是否需要在注销令牌中包含sid(session ID)声明,以在使用backchannel_logout_uri时用OP标识RP会话。如果省略,默认值为false
    "scopes_supported": [
      "openid",
      "profile",
      "scope1",
      "offline_access"
    ], 
    // 支持的范围
    "claims_supported": [
      "sub",
      "name",
      "family_name",
      "given_name",
      "middle_name",
      "nickname",
      "preferred_username",
      "profile",
      "picture",
      "website",
      "gender",
      "birthdate",
      "zoneinfo",
      "locale",
      "updated_at"
    ], 
    // 支持的claims
    "grant_types_supported": [
      "authorization_code",
      "client_credentials",
      "refresh_token",
      "implicit",
      "password",
      "urn:ietf:params:oauth:grant-type:device_code"
    ], 
    // 授权类型
    "response_types_supported": [
      "code",
      "token",
      "id_token",
      "id_token token",
      "code id_token",
      "code token",
      "code id_token token"
    ], 
    // 支持的请求方式
    "response_modes_supported": [
      "form_post",
      "query",
      "fragment"
    ], 
    // 传值方式
    "token_endpoint_auth_methods_supported": [
      "client_secret_basic",
      "client_secret_post"
    ], 
    // JSON数组,包含此令牌端点支持的客户端身份验证方法列表
    "id_token_signing_alg_values_supported": [
      "RS256"
    ],
    "subject_types_supported": [
      "public"
    ],
    // JSON数组,包含此OP支持的主题标识符类型列表。 有效值是 pairwise 和 public.类型。 更多信息
    "code_challenge_methods_supported": [
      "plain",
      "S256"
    ],
    // JSON数组,包含此授权服务器支持的PKCE代码方法列表
    "request_parameter_supported": true
  }

 

 

 

5. 获取token 

由于之前配置的GrantTypes.ResourceOwnerPasswordAndClientCredentials模式,同时支持password和client_credentials两种模式访问

1)  使用password模式授权,注意使用 x-www-form-urlencoded 方式 

地址:https://localhost:5001/connect/token

参数:

grant_type password
client_id app
client_secret secret
username mail@qq.com
password password

 

 

 

 

 

 

 

 

 

2)  使用 client_credentials 模式授权,注意使用 x-www-form-urlencoded 方式 

地址:https://localhost:5001/connect/token

参数:

grant_type password
client_id app
client_secret secret

 

 

 

 

 

 

 

从数据库账户中获取数据认证

1. 新增 ResourceOwnerPasswordValidator.cs 类 

using IdentityServer4.Validation;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using System;
using IdentityModel;
using IdentityServer4.Models;
using Newtonsoft.Json;

namespace NETCORE.IdentityServer4.Service
{
    public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
    {
        public ResourceOwnerPasswordValidator()
        {
        }
        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            //根据context.UserName和context.Password与数据库的数据做校验,判断是否合法
            if (context.UserName == "wjk" && context.Password == "123")
            {
                context.Result = new GrantValidationResult(
                 subject: context.UserName,
                authenticationMethod: "custom",
                //authenticationMethod: OidcConstants.AuthenticationMethods.Password,
                claims: GetUserClaims());

                //context.Result = new GrantValidationResult(
                //    JsonConvert.SerializeObject(context.UserName),
                //    OidcConstants.AuthenticationMethods.UserPresenceTest);

            }
            else
            {
                //验证失败
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential");
            }
        }
        //可以根据需要设置相应的Claim
        private Claim[] GetUserClaims()
        {
            return new Claim[]
            {
            new Claim("userid", 1.ToString()),
            new Claim(JwtClaimTypes.Name,"wjk"),
            new Claim(JwtClaimTypes.GivenName, "ssss"),
            new Claim(JwtClaimTypes.FamilyName, "ddddd"),
            new Claim(JwtClaimTypes.Email, "11111@qq.com"),
            new Claim(JwtClaimTypes.Role,"admin")
            };
        }
    }
}

 

 

 2. 在startup中,注入时使用 AddResourceOwnerValidator ,去掉 AddTestUsers

            services.AddIdentityServer()//Ids4服务
                .AddDeveloperSigningCredential()//添加开发人员签名凭据
                //.AddTestUsers(Config.Users().ToList())
                .AddInMemoryIdentityResources(Config.GetIdentityResources())//添加内存apiresource
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryApiScopes(Config.GetApiScopes())
                .AddInMemoryClients(Config.GetClients())//把配置文件的client配置资源放到内存
                .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();

 

 

3. 测试

 

 

 

接口鉴权

增加 Nuget 包: IdentityServer4.AccessToken.Validation

 

 

 

修改 startup.cs

增加鉴权管理

           services.AddAuthentication("Bearer")
        //.AddIdentityServerAuthentication(options =>
        //    {
        //        options.Authority = "http://localhost:5000";
        //        options.RequireHttpsMetadata = false;

        //        options.ApiName = "MyApi";
        // })
        .AddJwtBearer("Bearer", options =>
        {
            options.Authority = "https://localhost:5001";
            options.RequireHttpsMetadata = true;    // htpps就设置为true

            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false
            };

        // token 默认容忍5分钟过期时间偏移,这里设置为0,
        //这里就是为什么定义客户端设置了过期时间为5秒,过期后仍可以访问数据
        options.TokenValidationParameters.ClockSkew = TimeSpan.Zero;

        });

            //API授权
            services.AddAuthorization(options =>
            {
                //策略
                options.AddPolicy("ApiScope", policy =>
                {
                    policy.RequireAuthenticatedUser();
                    policy.RequireClaim("scope", "MyApi");
                });
            });

            services.AddControllers();
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NETCORE.IdentityServer4.Service v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            //启动ids4中间件
            app.UseIdentityServer();

            app.UseAuthentication();//添加鉴权验证

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

 

 

 

增加控制器:ValuesController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace NETCORE.IdentityServer4.Service.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        [HttpGet]
        [Route("GetValue")]
        public IActionResult GetValue() => Ok("test ok!");
    }
}

 

 

增加鉴权管理 ,属性标签

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace NETCORE.IdentityServer4.Service.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    
    public class ValuesController : ControllerBase
    {
        [HttpGet]
        [Route("GetValue")]
        [Authorize]
        public IActionResult GetValue() => Ok("test ok!");


        [HttpGet]
        [Route("test")]
        public IActionResult test() => Ok("test test!");

    }
}

 

未 使用 token 时,返回 401

 

使用token时,返回正确的数据

 

 刷新token 

 

 获取到新的 token 与 refresh_token

{
    "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE2MjM0RDczREY3MDkxN0MxOTY2QTczMzM3NjVBNUY3IiwidHlwIjoiSldUIn0.eyJuYmYiOjE2NjU1NDUyNzEsImV4cCI6MTY2NTU0NTU3MSwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImF1ZCI6ImFwcCIsImlhdCI6MTY2NTU0NTI3MSwiYXRfaGFzaCI6ImFiVV9lX3dMODN2aXdNVVBkNHRSUXciLCJzdWIiOiJ3amsiLCJhdXRoX3RpbWUiOjE2NjU1NDUwNTQsImlkcCI6ImxvY2FsIiwiYW1yIjpbImN1c3RvbSJdfQ.etyyf9zbSLVksUT0NotT4PB_IGBte4cEQinb6UsNAv1uE5baqcTD-Z1OqtbE4aiytiz0ceCRSKzNHyXNfAJV5KV--RXm0pEXfOAfj0xAbB5pZ0by2zlCUpqo3iEaxyTGAKdyJ3JbwTDAyQfmX9a7PMC2wB_omf0v0S90y2pzHbHRZHoO3XpCM-G7UEJws6lY7Lq7die22AlvlXzQz9hgdL3oIVdJWwbpdaVT7zIKBXTn4Ns75fAUWr9XK-jN35WjlwCJpqgDQ3T1pjpiAZPD1-blUbRootuUu3rNzTn5K8Ns840NZrBNKxr7wNv0p2bWlAu_Cb803jIyASqea8Ddhw",
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE2MjM0RDczREY3MDkxN0MxOTY2QTczMzM3NjVBNUY3IiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2NjU1NDUyNzEsImV4cCI6MTY2NTU0NTMzMSwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImF1ZCI6IkFQSTEiLCJjbGllbnRfaWQiOiJhcHAiLCJzdWIiOiJ3amsiLCJhdXRoX3RpbWUiOjE2NjU1NDUwNTQsImlkcCI6ImxvY2FsIiwianRpIjoiMjI4NUU0NEQxMEMyNkU4NUJGNkRDQzEyQjAyN0YyNjIiLCJpYXQiOjE2NjU1NDUwNTQsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJzY29wZTEiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsiY3VzdG9tIl19.J_M_zXEViDypF81IEBf9ZlDcxdM_RbeKrl9Aq4CKpLdO3nujHwwod-lHgqO4UmOqNGuoVT8JSPR1Y4v1HtaLylOZwbrsp8TqKV_Z5YximMywlXf4D8y1UG3Gb3KHA8KZLnKNC1F2AJT1Bd3WVDs_nu1jbv77H81Hj4TRJkwYQf0AYSUGo8vSx6MhHZutYO_ExrX7rWtZutByu85gj9ctc5V6t8fVjgVMmZZaWJaVFJtENcGY1TTGnRkAUD-HxlxcYOJ6F0Xgu2V91EJLtxzrH5Uoc-2cWVFy8t1h2vopFIiCOAOyZ7WLyIDNoKyLmYI-_dNpTCOhlCSPeVUzpar7yw",
    "expires_in": 60,
    "token_type": "Bearer",
    "refresh_token": "F1149C80C86F46E37A11C99735ABB5B6A4C52212718CE0E13D355FE7CC221824",
    "scope": "openid profile scope1 offline_access"
}

 

 

 其他接口

https://localhost:7015/connect/userinfo

 

 

 

 

 

 

 

 

 

 

参考: https://blog.csdn.net/ml344739968/article/details/110640458

 

项目:NETCORE.IdentityServer4.ervice
附代码:https://gitee.com/wuxincaicai/NETCORE.git

 

posted @ 2021-09-10 10:37  无心々菜  阅读(1260)  评论(1编辑  收藏  举报