zoukankan      html  css  js  c++  java
  • NetCore项目实战篇04---集成IdentityService4

    大家都知道我们的项目中已有web api,现在可以正式访问,不论任何人只要通过输入对应的api网址就可以访问到我们的api 资源,这样是很不安全的,我们需求对当前用户进行身份验证,因此我们在项目中使用IdentityServer4来对受保护资源并实现身份验证和/或授权,直接开始上代码,这些代码直接可以在你的项目中使用,并跑起来。

    1、  新建一个空的.netcore web项目,并引入IdentityService4的NuGet包。

    2、  在项目中增加一个config.cs文件

     public class Config
        {
            public static IEnumerable<Client> GetClients()
            {
                return new List<Client>
                {
                    new Client
                    {
                        ClientId="iphone",
                        ClientSecrets = new List<Secret>
                        {
                            new Secret("secret".Sha256())
                        },
                        RefreshTokenExpiration = TokenExpiration.Sliding,
                        AllowOfflineAccess = true,
                        RequireClientSecret = false,
                        AllowedGrantTypes = new List<string>{"sms_suth_code"},
                        AlwaysIncludeUserClaimsInIdToken = true,
                        AllowedScopes = new List<string>
                        {
                            "gateway_api",
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile,
                            IdentityServerConstants.StandardScopes.OfflineAccess,
                        },
    
                    },
                    new Client
                    {
                        ClientId="android",
                        ClientSecrets = new List<Secret>
                        {
                            new Secret("secret".Sha256())
                        },
                        RefreshTokenExpiration = TokenExpiration.Sliding,
                        AllowOfflineAccess = true,
                        RequireClientSecret = false,
                        AllowedGrantTypes = new List<string>{"sms_auth_code"},
                        AlwaysIncludeUserClaimsInIdToken = true,
                        AllowedScopes = new List<string>
                        {
                            "gateway_api",
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile,
                            IdentityServerConstants.StandardScopes.OfflineAccess,
                        },
    
                    }
                };
            }
            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("gateway_api","user service")
                };
            }
            
        }

    3、在项目中新增类SmsAuthCodeValidator该类主要是实现IdentityServer4组件中的IextensionGrantValidator接口ValidateAsync()的方法,在该方法写上自己的验证逻辑,这里我们只用户在登录时输入的手机号和验证码进行了校验,当然在校验时会调用用户模块的api,也就是我们_userService.CheckOrCreate(phone)方法,服务之前如何发现和调用这里不展开,下节介绍。SmsAuthCodeValidator代码如下

    public class SmsAuthCodeValidator : IExtensionGrantValidator
        {
            private readonly IAuthCodeService _authCodeService;
            private readonly IUserService _userService;
            public SmsAuthCodeValidator(IAuthCodeService authCodeService, IUserService userService)
            {
                _authCodeService = authCodeService;
                _userService = userService;
            }
            public string GrantType => "sms_auth_code";
    
            public async Task ValidateAsync(ExtensionGrantValidationContext context)
            {
                var phone = context.Request.Raw["phone"];
                var code = context.Request.Raw["auth_code"];
                var error = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
                if(!string.IsNullOrEmpty(phone)&& !string.IsNullOrEmpty(code))
                {
                    //用户检查
                    _authCodeService.Validate(phone, code);
                    //用户注册
                    var userId = await _userService.CheckOrCreate(phone);
                    if(userId <=0)
                    {
                        context.Result = error;
                        return;
                    }
    
                    context.Result = new GrantValidationResult(userId.ToString(), GrantType);
                }
                else
                {
                    context.Result = error;
    
                }
    
                
            }
        }

    4、在SmsAuthCodeValidator类我们引用了两个本地的服务,一个是对验证码进行校验的AuthCodeService类,一个是对手机号进行校验的UserService,也就是在这个类中对用户服务模块进行的手机号校验。现将这两个代码的代码写上

    public interface IAuthCodeService
        {
            /// <summary>
            /// 验证
            /// </summary>
            /// <param name="phone">手机号</param>
            /// <param name="authCone">验证码</param>
            /// <returns></returns>
            bool Validate(string phone, string authCone);
        }
    
    
    public class AuthCodeService : IAuthCodeService
        {
            public bool Validate(string phone, string authCone)
            {
                return true;
            }
        }
    public interface IUserService
        {
            /// <summary>
            /// 检查手机是否注册,如果没有就创建
            /// </summary>
            /// <param name="phone"></param>
            Task<int> CheckOrCreate(string phone);
        }
    
    public class UserService : IUserService
        {
            
            public async Task<int> CheckOrCreate(string phone)
            {
              
                return 1;
    
            }
        }

    5、最后我们需要增加IdentityServer4中间件,并对我们的服务进行配置

    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.AddIdentityServer()
                    .AddExtensionGrantValidator<SmsAuthCodeValidator>()
                    .AddDeveloperSigningCredential()
                    .AddInMemoryClients(Config.GetClients())
                    .AddInMemoryIdentityResources(Config.GetIdentityResources())
                    .AddInMemoryApiResources(Config.GetApiResources());
                services.AddSingleton(new HttpClient());
                services.AddScoped<IAuthCodeService, AuthCodeService>()
                    .AddScoped<IUserService, UserService>();
    
                services.AddMvc();
            }
    
            // 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.UseIdentityServer();
                app.UseMvc();
            }
        }

    5、 生成并启动该项目,通过postman访问,需求增加如下六个参数

    6、如果没有问题的话我们会获取到系统反馈给我们的token值,返回结果如下:

    {
        "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjlhMjJlN2E3Zjg1ZmY5MjNiMTJmM2Nm
    NGZkMGM3YzYzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1ODg5MjE2MjUsImV4cCI6
    MTU4ODkyNTIyNSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDoxMTEwIiwiYXVkIjpb
    Imh0dHA6Ly9sb2NhbGhvc3Q6MTExMC9yZXNvdXJjZXMiLCJnYXRld2F5X2FwaSJd
    LCJjbGllbnRfaWQiOiJhbmRyb2lkIiwic3ViIjoiMyIsImF1dGhfdGltZSI6MTU4
    ODkyMTYyNSwiaWRwIjoibG9jYWwiLCJzY29wZSI6WyJvcGVua
    WQiLCJwcm9maWxlIiwiZ2F0ZXdheV9hcGkiLCJvZmZsaW5lX2FjY2VzcyJdLCJhb
    XIiOlsic21zX2F1dGhfY29kZSJdfQ.pyEKOe08jiqtg1rgcf0UGO0hmfEhI5a2cIXw
    _-YgXdLVceKa14Jhyy8Ezgom3ipNlci5FwmN-p5ro_3ORtzreU0qxhiCzI5kyPgLRP
    lOO8cFykYKY4yQOCD_z2LohSxyvAsTPn0B75_iodujGPQAB4Outs9uAjcHXAnxjBkn
    DKl6L5uu609ZaugG4X6T2xx0ZDU-VftrrmB-YX5oe6FU70R4jsRLayL8nrM-u-Q_We
    UIfY04M91REX9HqneOGyxSDj2Qku22pC68dlIYQNGhBlYUnSqRMkk39Pe9UmjO8dSp
    qqBMtHBEwCQn3cMzG7UbP5gB6F2GgTICUBERbxxwRA
    ", "expires_in": 3600, "token_type": "Bearer", "refresh_token": "f3051fa24cebf7cbfa73b55563a283bb3c15b129c8c5ff732324a653a7c6eff1" }

    7、 怀着好奇的心我们来看看这个access_token的值反馈给我们的是什么,其实他就是JWT(Json Web Token),解析成json格式如下

     {
     alg: "RS256",
    
     kid: "9a22e7a7f85ff923b12f3cf4fd0c7c63",
    
     typ: "JWT"
    
    }.
    {
     nbf: 1588921625,
    
     exp: 1588925225,
    
     iss: "http://localhost:1110",
    
     aud: [
      "http://localhost:1110/resources",
    
      "gateway_api"
    
     ],
    
     client_id: "android",
    
     sub: "3",
    
     auth_time: 1588921625,
    
     idp: "local",
    
     scope: [
      "openid",
    
      "profile",
    
      "gateway_api",
    
      "offline_access"
    
     ],
    
     amr: [
      "sms_auth_code"
    
     ]
    
    }. 
    [signature]

    顺便学习一下JWT吧:

           HTTP提供了一套标准的身份验证框架:服务器可以用来针对客户端的请求发送质询(challenge),客户端根据质询提供身份验证凭证。质询与应答的工作流程如下:服务器端向客户端返回401(Unauthorized,未授权)状态码,并在WWW-Authenticate头中添加如何进行验证的信息,其中至少包含有一种质询方式。然后客户端可以在请求中添加Authorization头进行验证,其Value为身份验证的凭证信息。

          Bearer认证(也叫做令牌认证)是一种HTTP认证方案,其中包含的安全令牌的叫做Bearer Token。因此Bearer认证的核心是Token。那如何确保Token的安全是重中之重。一种方式是使用Https,另一种方式就是对Token进行加密签名。而JWT就是一种比较流行的Token编码方式。可以看出JWT有三部分组成:

    <header>.<payload>.<signature>

    1. Header:由algtyp组成,alg是algorithm的缩写,typ是type的缩写,指定token的类型。该部分使用Base64Url编码。
    2. Payload:主要用来存储信息,包含各种声明,同样该部分也由BaseURL编码。
    3. Signature:签名,使用服务器端的密钥进行签名。以确保Token未被篡改。

    下一篇我们将介绍网关,通过网关来访问我们的用户模块的api资源,并集成IdentityServer4

  • 相关阅读:
    [Qt] 文本文件读写, 摘自官方文档
    [Windows] Socket Server Failed to bind, error 10048
    lodctr /R 失败的情况
    ModuleNotFoundError: No module named 'sklearn.cross_validation'
    [Qt] 通过socket将另一个程序的某个窗口调到最前端
    SortedDictionary<TKey, TValue> 类 表示根据键进行排序的键/值对的集合。
    finally不管有没有错都会运行 finally 块用于清除 try 块中分配的任何资源,以及运行任何即使在发生异常时也必须执行的代码
    HttpWebRequest使用证书请求
    string StartsWith 方法 Https
    设置https验证方式
  • 原文地址:https://www.cnblogs.com/zhengwei-cq/p/12851518.html
Copyright © 2011-2022 走看看