IdentityServer4 реализует вход в систему и управление разрешениями для аутентификации с помощью токена.

задняя часть .NET API Начать бизнес

Связанные очки знаний

Про IdentityServer4 рассказывать больше не буду, кто-то уже публиковал серию статей на блог-гарден, если не понятно, то можете глянуть:

Бог сверчков:Маленькие блюда для изучения программирования-IdentityServer4

Мастер Сяочэнь:IdentityServer4

Идентификация, претензия и другие сопутствующие знания:

Настольная доска:Введение в удостоверение ASP.NET Core (1),Введение в удостоверение ASP.NET Core (2)

Создайте службу IdentityServer4.

Создайте пустой веб-проект ASP.NET Core (asp.net core 2.0) с именем QuickstartIdentityServer, порт 5000.

NuGet-пакеты:

Измените настройки Startup.cs, чтобы использовать IdentityServer:

public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // configure identity server with in-memory stores, keys, clients and scopes
            services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryIdentityResources(Config.GetIdentityResourceResources())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients())
                .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
                .AddProfileService<ProfileService>();
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseIdentityServer();
        }
    }

Добавьте Config.cs для настройки IdentityResource, ApiResource и Client:

public class Config
   {
       public static IEnumerable<IdentityResource> GetIdentityResourceResources()
       {
           return new List<IdentityResource>
           {
               new IdentityResources.OpenId(), //必须要添加,否则报无效的scope错误
               new IdentityResources.Profile()
           };
       }
       // scopes define the API resources in your system
       public static IEnumerable<ApiResource> GetApiResources()
       {
           return new List<ApiResource>
           {
               new ApiResource("api1", "My API")
           };
       }
       // clients want to access resources (aka scopes)
       public static IEnumerable<Client> GetClients()
       {
           // client credentials client
           return new List<Client>
           {
               new Client
               {
                   ClientId = "client1",
                   AllowedGrantTypes = GrantTypes.ClientCredentials,
                   ClientSecrets =
                   {
                       new Secret("secret".Sha256())
                   },
                   AllowedScopes = { "api1",IdentityServerConstants.StandardScopes.OpenId, //必须要添加,否则报forbidden错误
                 IdentityServerConstants.StandardScopes.Profile},
               },
               // resource owner password grant client
               new Client
               {
                   ClientId = "client2",
                   AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                   ClientSecrets =
                   {
                       new Secret("secret".Sha256())
                   },
                   AllowedScopes = { "api1",IdentityServerConstants.StandardScopes.OpenId, //必须要添加,否则报forbidden错误
                 IdentityServerConstants.StandardScopes.Profile }
               }
           };
       }
   }

Поскольку вы хотите использовать сохраненного в данных пользователя для проверки при входе в систему, вам необходимо реализовать интерфейс IResourceOwnerPasswordValidator:

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",
                 claims: GetUserClaims());
            }
            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, "jaycewu"),
            new Claim(JwtClaimTypes.FamilyName, "yyy"),
            new Claim(JwtClaimTypes.Email, "977865769@qq.com"),
            new Claim(JwtClaimTypes.Role,"admin")
            };
        }
    }

IdentityServer предоставляет интерфейс для доступа к информации о пользователе, но данные, возвращаемые по умолчанию, являются только sub, которые являются предметом: context.UserName, установленным выше.Чтобы вернуть больше информации, вам необходимо реализовать интерфейс IProfileService:

public class ProfileService : IProfileService
    {
        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            try
            {
                //depending on the scope accessing the user data.
                var claims = context.Subject.Claims.ToList();
                //set issued claims to return
                context.IssuedClaims = claims.ToList();
            }
            catch (Exception ex)
            {
                //log your error
            }
        }
        public async Task IsActiveAsync(IsActiveContext context)
        {
            context.IsActive = true;
        }

context.Subject.Claims — это данные, предоставленные утверждениями: GetUserClaims(), когда ранее был реализован интерфейс IResourceOwnerPasswordValidator.
Кроме того, после отладки обнаруживается, что выполняется ValidateAsync в ResourceOwnerPasswordValidator, а затем выполняются IsActiveAsync и GetProfileDataAsync в ProfileService.

Запустите проект и используйте postman, чтобы сделать запрос на получение токена:

Затем используйте токен для получения соответствующей информации о пользователе:

Служба аутентификации по токену, как правило, отделена от веб-программы.Созданный выше проект QuickstartIdentityServer эквивалентен серверу, а веб-программа, которая нам нужна для написания бизнес-логики, эквивалентна клиенту. Когда пользователь запрашивает веб-программу, веб-программа принимает токен, полученный при входе пользователя в систему, на сервер IdentityServer для проверки.

Создать веб-приложение

Создайте пустой веб-проект ASP.NET Core (asp.net core 2.0) с именем API, порт 5001.

NuGet-пакеты:

Измените настройки Startup.cs, чтобы использовать IdentityServer для проверки:

public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvcCore(option=>
            {
                option.Filters.Add(new TestAuthorizationFilter());
            }).AddAuthorization()
                .AddJsonFormatters();
            services.AddAuthentication("Bearer")
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = "http://localhost:5000";
                    options.RequireHttpsMetadata = false;
                    options.ApiName = "api1";
                });
        }
        public void Configure(IApplicationBuilder app)
        {
            app.UseAuthentication();
            app.UseMvc();
        }
    }

Создайте ИдентитиКонтроллер:

[Route("[controller]")]
    public class IdentityController : ControllerBase
    {
        [HttpGet]
        [Authorize]
        public IActionResult Get()
        {
            return new JsonResult("Hello Word");
        }
    }

Запустите QuickstartIdentityServer, проект API соответственно. Получите доступ к API с помощью сгенерированного токена:

С помощью описанной выше процедуры уже можно выполнить функцию входа в систему с разделенными передней и задней частями.

На самом деле, в веб-приложениях нам часто нужно получить соответствующую информацию о текущем пользователе для операций, например, записать некоторые журналы операций пользователя. Как упоминалось ранее, IdentityServer предоставляет интерфейс /connect/userinfo для получения информации о пользователе. Раньше моя идея заключалась в том, чтобы использовать токен в веб-программе, чтобы запросить этот интерфейс для получения информации о пользователе, и выполнить соответствующую буферизацию после первого получения. Но это кажется немного странным. IdentityServer не может не думать об этом. Нормальный подход должен состоять в том, чтобы пройти проверку и вернуть информацию о пользователе в веб-программу. Снова возникает вопрос.Если IdentityServer действительно это делает, то как веб-программа может это получить?Я проверил официальную документацию и не смог ее найти. Затем я проверил это с помощью ключевого слова «Claim» (раньше я не знал ASP.NET). Identity) и, наконец, получить установленную информацию о пользователе через HttpContext.User.Claims:

Изменить IdentityController:

[Route("[controller]")]
    public class IdentityController : ControllerBase
    {
        [HttpGet]
        [Authorize]
        public IActionResult Get()
        {
            return new JsonResult(from c in HttpContext.User.Claims select new { c.Type, c.Value });
        }
    }

Контроль доступа

IdentityServer4 также предоставляет функцию управления правами, я посмотрел и не добился того, что хотел (у меня не хватило терпения изучить это).
Что мне нужно, так это определить коды разрешений (строки) для разных модулей и функций, и каждый код разрешения соответствует соответствующему разрешению функции. Когда пользователь делает запрос, оценивается, имеет ли пользователь разрешение соответствующей функции (дан ли соответствующий код строки разрешения) для достижения контроля разрешений.

Проверка IdentityServer предназначена для определения необходимости проверки соответствующего контроллера или действия с помощью функции авторизации. Здесь проверка разрешений также реализована с помощью пользовательских функций и расширена на исходную функцию авторизации. Допустимая схема наследует AuthorizeAttribute и перезаписывает его. Однако в .net core выводится сообщение о том, что нет метода OnAuthorization, который можно было бы переписать. Наконец, обратитесь к практике ABP, согласно которой фильтры и свойства используются вместе.

Новый TestAuthorizationFilter.cs

public class TestAuthorizationFilter : IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            if (context.Filters.Any(item => item is IAllowAnonymousFilter))
            {
                return;
            }
            if (!(context.ActionDescriptor is ControllerActionDescriptor))
            {
                return;
            }
            var attributeList = new List<object>();
            attributeList.AddRange((context.ActionDescriptor as ControllerActionDescriptor).MethodInfo.GetCustomAttributes(true));
            attributeList.AddRange((context.ActionDescriptor as ControllerActionDescriptor).MethodInfo.DeclaringType.GetCustomAttributes(true));
            var authorizeAttributes = attributeList.OfType<TestAuthorizeAttribute>().ToList();
            var claims = context.HttpContext.User.Claims;
            // 从claims取出用户相关信息,到数据库中取得用户具备的权限码,与当前Controller或Action标识的权限码做比较
            var userPermissions = "User_Edit";
            if (!authorizeAttributes.Any(s => s.Permission.Equals(userPermissions)))
            {
                context.Result = new JsonResult("没有权限");
            }
            return;
        }
    }

Новый атрибут TestAuthorizeAttribute

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
    public class TestAuthorizeAttribute: AuthorizeAttribute
    {
        public string Permission { get; set; }
        public TestAuthorizeAttribute(string permission)
        {
            Permission = permission;
        }
    }

Измените IdentityController [Authorize] на [TestAuthorize("User_Edit")], а затем запустите проект API.

Убедитесь, что он работает, изменив код разрешения

Помимо использования фильтров и функций в сочетании, похоже, есть и другие методы, которые я изучу, когда у меня будет время.

в этой статьеисходный код