zoukankan      html  css  js  c++  java
  • 我的第一个微服务系列(三):搭建IdentityServer服务器

      本篇介绍如何通过IdentityServer服务器调用User.Api来验证用户信息。

      新建User.Identity项目,引入Nuget包:IdentityServer4,关于IdentityServer4的相关知识可以参考另一篇博文:https://www.cnblogs.com/jesen1315/p/11427294.html 。

      在此项目中,将使用手机号和验证码来作为注册或登录验证的判断,这需要调用到User.Api项目来判断,如果该手机号未注册,则自动完成注册。User.Identity 将通过网关来访问,所有的外部访问请求需要先从User.Identity获取token后再发起,也就是说所有的请求都是经过网关再转发到对应的Api中。

      因此,我们需要自定义我们的GrantType,而IdentityServer4中自定义GrantType需要实现 IExtensionGrantValidator 接口。所以先来定义实现IExtensionGrantValidator的类SmsAuthCodeValidator,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Threading.Tasks;
    using IdentityServer4.Models;
    using IdentityServer4.Validation;
    using User.Identity.Services;
    
    namespace User.Identity.Authentication
    {
        /// <summary>
        /// 自定义验证码授权验证
        /// </summary>
        public class SmsAuthCodeValidator : IExtensionGrantValidator
        {
            private readonly IUserService _userService;
            private readonly IAuthCodeService _authCodeService;
    
            public SmsAuthCodeValidator(IAuthCodeService authCodeService
                ,IUserService userService)
            {
                _authCodeService = authCodeService;
                _userService = userService;
            }
    
            public string GrantType => "sms_auth_code"; //http 请求时的granttype
    
            public async Task ValidateAsync(ExtensionGrantValidationContext context)
            {
                var phone = context.Request.Raw["phone"];
                var code = context.Request.Raw["auth_code"];
    
                var errorValidationResult = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
    
                if(string.IsNullOrWhiteSpace(phone) || string.IsNullOrWhiteSpace(code))
                {
                    context.Result = errorValidationResult;
                    return;
                }
    
                // 检查验证码
                if (!_authCodeService.Validate(phone, code))
                {
                    context.Result = errorValidationResult;
                    return;
                }
    
                // 用户注册
                var userInfo = await _userService.CheckOrCreateAsync(phone);
                if (userInfo == null)
                {
                    context.Result = errorValidationResult;
                    return;
                }
    
                var claims = new Claim[] {
                    new Claim("name",userInfo.Name??string.Empty),
                    new Claim("company",userInfo.Company??string.Empty),
                    new Claim("title",userInfo.Title??string.Empty),
                    new Claim("avatar",userInfo.Avatar??string.Empty),
                };
    
                context.Result = new GrantValidationResult(userInfo.Id.ToString(), GrantType,claims);
            }
        }
    }

       默认情况下,IdentityServer只能从认证(authentication)Cookie中获取保存的claims信息。但是将用户的所有可用的信息都保存到Cookie中很显然是不现实的,也是一种不好的实践,所以,IdentityServer定义了一个可扩展的接口,允许动态的加载用户的Claim,这个接口就是IProfileService。开发人员通常实现此接口来访问包含用户数据(claims)的自定义数据库或API。IdentityServer4会在每次请求User信息的时候调用实现了IProfileService的类中的GetProfileDataAsync方法,而IProfileService的另一个方法IsActiveAsync,则使用来确定用户是否有效或激活状态。

      因此,我们还需要定义一个实现IProfileService的类ProfileService。

    using IdentityServer4.Models;
    using IdentityServer4.Services;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace User.Identity.Authentication
    {
    
        public class ProfileService : IProfileService
        {
            public Task GetProfileDataAsync(ProfileDataRequestContext context)
            {
                var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
    
                var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
    
                if(!int.TryParse(subjectId,out int intUserId))
                {
                    throw new ArgumentException("Invalid subject identifier");
                }
    
                context.IssuedClaims = context.Subject.Claims.ToList();
                return Task.CompletedTask;
            }
    
            public Task IsActiveAsync(IsActiveContext context)
            {
                var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
    
                var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
    
                context.IsActive = int.TryParse(subjectId, out int intUserId);
    
                return Task.CompletedTask;
            }
        }
    }

       完成这两步之后,在之前介绍IdentityServer4的时候知道需要配置Client、IdentityResource、ApiResource来授予受信任的客户端获取信息。

    using IdentityServer4;
    using IdentityServer4.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace User.Identity
    {
        public class Config
        {
            public static IEnumerable<Client> GetClients()
            {
                return new List<Client> {
                    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",
                            "contact_api",
                            "user_api",
                            "project_api",
                            "recommend_api",
                            IdentityServerConstants.StandardScopes.OfflineAccess,
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile
                        }
                    }
                };
            }
    
            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"),
                    new ApiResource("contact_api","contact Service"),
                    new ApiResource("user_api","User Service"),
                    new ApiResource("project_api","Project Service"),
                    new ApiResource("recommend_api","Recommend Service")
                };
            }
        }
    }

      到此,关于IdentityServer的配置基本完成,接下来要做的是如何在User.Identity项目中跨服务调用User.Api项目来验证用户合法性。普通的方式我们是在配置文件中配置User.Api的Url后再代码中使用HttpClient或HttpWebRequest直接调用来验证,但是这种方式在微服务中已经不再适用,因为微服务中User.Api可能部署的不只一台而是多台,这个时候就需要引入服务注册和发现这种机制来解决,我们下篇再来讨论。

      

  • 相关阅读:
    排序——插入排序
    利用socket传文件
    Segmentation fault (core dumped)
    Linux网络编程
    3G功能设计及实现
    rpm命令
    安装包相互依赖的问题
    centos网站(一些软件的下载)
    解决vim显示中文的问题
    glGetFloatv (GL_MODELVIEW_MATRIX, mat)
  • 原文地址:https://www.cnblogs.com/jesen1315/p/11498887.html
Copyright © 2011-2022 走看看