Detailed Explanation of the Claim of IdentityServer4 in Asp.Net Core

tags: ASP.NET CORE  IdentityServer4

I. Introduction

Due to the epidemic, I started to learn and share technology by blogging (the process of continuous sharing is also the process of my own learning and growth), and at the same time, it also allows more beginners to learn relevant knowledge. If there is analysis in my article If it is not in place, please give us your advice; I will continue to update my article in the future, and hope that you will support and pay more attention.
The last few articles mainly sharedIdentityServer4In the application of Asp.Net Core 3.x, in the above several sharing articles, some bloggers asked me such a question: "He uses IdentityServer4 to build the authorization center gateway service, how can he access protected Api resources? How about getting the relevant identity information of the user?".
Then this article is mainly to share an important part of the certification processClaimBefore we start, it is strongly recommended that students who have not read the IdentityServer4 series of articles written by me take a look. The following articles areArchitectural thinking brings everyone into the world of IdentityServer4

  • Basic concepts in Asp.Net Core IdentityServer4
  • Application combat of IdentityServer4 authorization center in Asp.Net Core
  • Custom authorization model of IdentityServer4 authorization center in Asp.Net Core
  • IdentityServer4 authorization principle in Asp.Net Core and application of refresh token

2. What is Claim

Claim

Claim My understanding is astatement, Stores a key-value pair relationship, which is equivalent toID cardmiddle Name: Trump,gender: The series of elements of male ID, etc., each item is a key value, let’s take a lookClaimMain code

public class Claim
{
    public string ClaimType { get; set; }

    public string ClaimValue { get; set; }
}

The two main core attributes in the codeClaimType withClaimValue; ClaimType is Key, and ClaimValue represents a Value. In this case, just one key-value pair can be stored. At this momentName: TrumpCan it be deposited?
At the same time, Microsoft also provides a defaultClaimType, Some of the default ones are as follows:
[External link image transfer failed, the source site may have anti-leeching mechanism, it is recommended to save the image and upload it directly (img-pnHicXFe-1584892018434)(https://img2020.cnblogs.com /blog/824291/202003/824291-20200321223735081-1098598478.png)]
Claim The introduction is almost finished, relatively simple and clear,ClaimIt can be said that it is the smallest unit of identity declaration (Identity unit)。

ClaimsIdentity

Let's take a lookClaimsIdentityPart of the code, the code is as follows:

public class ClaimsIdentity:IIdentity
{
    public ClaimsIdentity(IEnumerable<Claim> claims){}
    
         //The name is so important, of course you can’t let others change it at will, so I don’t allow it to set, except for my son and my last name, so it’s virtual
    public virtual string Name { get; }
    public string Label { get; set; }
    
         //Identity unit collection
    public virtual IEnumerable<Claim> Claims { get; }
    
         //This is the type of my certificate and it is also very important. Set is also not allowed
    public virtual string AuthenticationType { get; }
    
    public virtual void AddClaim(Claim claim);
    
    public virtual void RemoveClaim(Claim claim);
    
    public virtual void FindClaim(Claim claim);
}

As you can see from the code, there is aClaimsThe attribute is a collection. Can we associate our ID card with it? Each of us has a "ID card"(ClaimIdentity), the ID contains multiple "Identity unit"(Claim) and other information.
There is a particularly important attribute from the codeAuthenticationType translate toAuthentication type, Here is equivalent to the document type, such as an ID card, its document type is "ID", and the passport type of passport machine is "passport".
At the same timeClaimsIdentityinheritIIdentityAbstract interface, let's take a look at the code of this abstract interface:

// Define the basic functions of the document object.
public interface IIdentity
{
         //the name of your ID 
    string Name { get; }
    
         // The type of carrier used to identify the document.
    string AuthenticationType { get; }
    
         //Whether it is a legal document.
    bool IsAuthenticated { get; }
}

to hereClaimsIdentityAlmost the introduction,ClaimsIdentityIs equivalent toID card,orpassportSomething like that, a document that can prove identity.

ClaimsPrincipal

Once a person has an identity, there will be multiple identities, for example, you aredriverprincipalbossWait, then you will havedriver licenseTeacher certificationCompany's business licenseAnd other documents. These documents need a carrier to accommodate, thenClaimsPrincipalThis is equivalent to the carrier of these certificates, let's take a look at some of its core code:

    public class ClaimsPrincipal : IPrincipal
    {
       
        public ClaimsPrincipal();
       
        public ClaimsPrincipal(IEnumerable<ClaimsIdentity> identities);
        
        public ClaimsPrincipal(IIdentity identity);
        
        public ClaimsPrincipal(IPrincipal principal);
      
        public virtual IIdentity Identity { get; }
       
        public virtual IEnumerable<ClaimsIdentity> Identities { get; }
        
                 //Add the certificate to the carrier
        public virtual void AddIdentities(IEnumerable<ClaimsIdentity> identities);
                 //Add the certificate to the carrier
        public virtual void AddIdentity(ClaimsIdentity identity);
        
                 //The following are all operations such as obtaining documents from the carrier
        public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match);
      
        public virtual IEnumerable<Claim> FindAll(string type);
        
        public virtual Claim FindFirst(string type);
       
        public virtual Claim FindFirst(Predicate<Claim> match);
        
        public virtual bool HasClaim(Predicate<Claim> match);

                 //Does belong to a role
        public virtual bool IsInRole(string role);
    }

ClaimsPrincipal After the introduction, I will putClaimsPrincipal It's calledContainer carrier
We already know the relationship between "Claims", "ClaimsIdentity", and "ClaimsPrincipal".
Let’s briefly look at the simplified flow chart of the information carried in identity authentication:

Okay, hereClaimThe related concepts are clear, and I also need to thank the Microsoft MVP boss @Savorboard's article https://www.cnblogs.com/savorboard/p/aspnetcore-identity.html for letting me sort out these relationships!

Three, actual combat

I am here to continue writing based on the code of my last few articles. Those who need code can visit https://github.com/a312586670/IdentityServerDemo. The code will be updated synchronously with the blog.
The following three projects have been created in the solution in the previous articles:
[External link image transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the image and upload it directly (img-saVXdrUQ-1584892018435)(https://img2020.cnblogs.com /blog/824291/202003/824291-20200321224606475-145405767.png)]

  • Jlion.NetCore.Identity: Identity public base class library
  • Jlion.NetCore.Identity.Service: Ids4 authorization service, which is also mentioned in the previous articlesAuthorization CenterService simple version
  • Jlion.NetCore.Identity.UserApiService: User service gateway (protected resource)

Authorization Center (Ids4 Authorization Service)

Jlion.NetCore.Identity.Service

We are firstAuthorization Center(Ids4) Add user-related information in the code to verify the user in the serviceClaims, The core code is as follows:
If you are unfamiliar, please move first
Application combat of IdentityServer4 authorization center in Asp.Net Core This article

public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
 {
        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            try
            {
                var userName = context.UserName;
                var password = context.Password;

                                 //Verify the user, so you can go to the database to verify that the user name and password are correct
                var claimList = await ValidateUserAsync(userName, password);

                                 // Verify account
                context.Result = new GrantValidationResult
                (
                    subject: userName,
                    authenticationMethod: "custom",
                    claims: claimList.ToArray()
                );
            }
            catch (Exception ex)
            {
                                 //Verify abnormal results
                context.Result = new GrantValidationResult()
                {
                    IsError = true,
                    Error = ex.Message
                };
            }
        }

        #region Private Method
        /// <summary>
                 /// Verify user
        /// </summary>
        /// <param name="loginName"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        private async Task<List<Claim>> ValidateUserAsync(string loginName, string password)
        {
                         //TODO Here you can verify whether it exists in the database by username and password,
                         // And role-related information, I still use the user and password that already exists in the memory
            var user = OAuthMemoryData.GetTestUsers();

            if (user == null)
                                 throw new Exception("Login failed, user name and password are incorrect");
                         //I am here to test, simply hard-coded, the production environment can read the relevant information from the database to construct `Claim` (**identity unit**)
            return new List<Claim>()
            {
                new Claim(ClaimTypes.Name, $"{loginName}"),
                                 new Claim(EnumUserClaim.DisplayName.ToString(),"test user"),
                new Claim(EnumUserClaim.UserId.ToString(),"10001"),
                new Claim(EnumUserClaim.MerchantId.ToString(),"000100001"),
            };
        }
        #endregion
    }

Now, multiple claims have been built, multipleClaimA user identity is constructed, and they all belong to the identity unit owned by the user who is about to log in. Next, we need to implementIProfileServiceAbstract interface,
The code is as follows:

public class UserProfileService : IProfileService
{
        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            try
            {
                
                var claims = context.Subject.Claims.ToList(); 
                                 // The identity of the authenticated user
                context.IssuedClaims = claims.ToList();
            }
            catch { }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task IsActiveAsync(IsActiveContext context)
        {
            context.IsActive = true;
        }
    }

GetProfileDataAsyncMainly loaded for usersClaims, Implement the code and passAddProfileService<T>()Only when the method is added to DI can the user's identity information be obtained in the API resource. The code is as follows:

  public void ConfigureServices(IServiceCollection services)
  {
      services.AddControllers();

      
             #region Database storage method
      services.AddIdentityServer()
          .AddDeveloperSigningCredential()
          .AddInMemoryApiResources(OAuthMemoryData.GetApiResources())
          //.AddInMemoryClients(OAuthMemoryData.GetClients())
          .AddClientStore<ClientStore>()
          .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
                     .AddExtensionGrantValidator<WeiXinOpenGrantValidator>()//Add the verification of the WeChat end custom method (the code for the custom authorization method in the previous article)
          .AddProfileService<UserProfileService>();

      #endregion

  }

Api resources (protected resources)

Jlion.NetCore.Identity.UserApiService

Let's createUserIdentityExtensionExtension class, the code is as follows:

 public static class UserIdentityExtension
 {
        /// <summary>
                 /// Get the user's Name
        /// </summary>
        /// <param name="this"></param>
        /// <returns></returns>
        public static string Name(this ClaimsPrincipal @this)
        {
            return @this?.Identity?.Name;
        }

        /// <summary>
                 /// Get DisplayName
        /// </summary>
        /// <param name="this"></param>
        /// <returns></returns>
        public static string DisplayName(this ClaimsPrincipal @this)
        {
            var value = @this?.Claims?.FirstOrDefault(oo => oo.Type == EnumUserClaim.DisplayName.ToString())?.Value;
            return value;
        }

        public static string UserId(this ClaimsPrincipal @this)
        {
            return @this?.Claims?.FirstOrDefault(oo => oo.Type == EnumUserClaim.UserId.ToString())?.Value;
        }

        public static string MerchantId(this ClaimsPrincipal @this)
        {
            return @this?.Claims?.FirstOrDefault(oo => oo.Type == EnumUserClaim.MerchantId.ToString())?.Value;
        }
 }

Add on the original codeUserControllerController, the code is as follows:

[Authorize]
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{

    private readonly ILogger<UserController> _logger;

    public UserController(ILogger<UserController> logger)
    {
        _logger = logger;
    }
 }

UserControllerThe controller has been created and inheritedControllerBaseBase class, let's take a lookControllerBaseWhat information is included, the core code is as follows:

/// <summary>
/// A base class for an MVC controller without view support.
/// </summary>
[Controller]
public abstract class ControllerBase
{
           //Obtain User (document carrier container) from the request context
     public ClaimsPrincipal User => HttpContext?.User;

         //Other core codes are not posted, you can see the official source code for details
}

Looking at the source code, can we consider using User to obtainIDSome ofIdentity elementWhat? ,inUserControllerAdd an interface to obtain user information, the complete code is as follows:

[Authorize]
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{

  private readonly ILogger<UserController> _logger;

  public UserController(ILogger<UserController> logger)
  {
      _logger = logger;
  }

  
  [HttpGet]
  public async Task<object> Get()
  {
     //Obtain the identity components of certain documents through ClaimsPrincipal (document container carrier)
     var userId = User.UserId();
     return new
     {
          name = User.Name(),
          userId = userId,
          displayName = User.DisplayName(),
          merchantId = User.MerchantId(),
     };
  }
}

Well, the code has been built! ! !
Now I start the two services through the command line.
Jlion.NetCore.Identity.ServiceStart as shown below:

I still start with "http://localhost:5000" here
Jlion.NetCore.Identity.UserApiService Start as shown below:

Start here with "http://localhost:5001" address

Now we access via postmanids4The server obtains the accesstoken as shown below:
[External link image transfer failed, the source site may have anti-leeching mechanism, it is recommended to save the image and upload it directly (img-oNqCJEPg-1584892018437)(https://img2020.cnblogs.com /blog/824291/202003/824291-20200321233058690-545184526.png)]
Getaccess_token Success, I will carryaccess_tokenVisitUser Service Gateway,As shown below:

Successfully obtained user identity informationClaimsRelated Information.

in conclusion:ids4Construct user identity information (Claim) in authorization service through identity container carrierClaimsPrincipalLoading (where is it loaded? How is it carried toApi resourcesIn the gateway? The next article will share the specific principles and processes); and then go through the protectedApi resourcesThrough the gatewayClaimsPrincipalAfter the identity container carrier obtains the relevant information of the current user, it can do something based onRole authorizationandbusinessRelated things.

Source code address of blog series articles: https://github.com/a312586670/IdentityServerDemo
Reference article:
https://www.cnblogs.com/savorboard/p/aspnetcore-identity.html

Welcome to follow WeChat public [Dr. dotNET]

Intelligent Recommendation

IdentityServer4 (4): API access in ASP.NET Core

Article Directory Modification of client configuration in authentication and authorization service Modification of MVC website client Use `Access Token` to access API service This article is based on ...

Asp.net WebApi(non core) + IdentityServer4 build process

Recent projects to be citedIdentityserver 4 is used for multi-client authentication. According to the tutorial on the official website, a WebApi+MVC client based on asp.net core is built, but there is...

Application combat of IdentityServer4 authorization center in Asp.Net Core

I. Introduction After consulting most of the relevant information, most of the IdentityServer4 application articles I found are relatively simple and mostly written by translating the documents of the...

IdentityServer4 implements implicit (ASP.NET Core and API access)

Source document:IdentityServer4 documentation This article uses **.netcore2.1**, which can be implemented normally. I can't create it normally with .netCore3.1. If any classmate can run 3.0, you can t...

IdentityServer4 (6): Integrated ASP.NET Core Identity

Integrate ASP.NET Core Identity Add context Add a service to the container Modification of login controller data migration Seed default user data More we have used beforeEFCorewillIdentityServer4Relat...

More Recommendation

Asp.Net Core service integration Identityserver4 certification

Article Directory Preface Steps for usage 1. Create a webapi project and name it OrderConsumer 2. Introduce IdentityServer4.AccessTokenValidation library version 3.0.1 3. Controller modification 4. St...

ASP.NET CORE IdentityServer4 integration with traditional roles

content Written in front before the start Implementation Start implementation Server 1, generate custom token Client 1, custom authorization tag CustomrbacauThorize 2, custom authorization IAUTHORZATI...

Detailed explanation of asp.net core middleware and actual project

Preface inPrevious articleMainly introduces the status of the DotNetCore project. This article is actually used in the development of our own projects. It is more suitable for practical applications. ...

ASP.NET CORE ACTIONFILTER filter detailed explanation

ActionFilter is a method filter or interceptor provided by ASP.NET CORE Generally used to perform certain operations (AOP) before the method execution or method execution The function is a bit similar...

User Claim in IdentityServer4 Identity Resource

  Identity Resource Identity resource, UserClaims inside are some attributes of the user, by default, even if you write more attributes, only one sub will be returned in the token, we need to add...

Copyright  DMCA © 2018-2026 - All Rights Reserved - www.programmersought.com  User Notice

Top