Description

ASP.NET Core 3.0 A jwt lightweight role/user, a single API controlled authorization certificate library

Recently, I have to re-do a role authorization library. I used to make a role authorization library. I used Microsoft’s default interface to do a lot of documentation. Because I didn’t understand enough, I ended up having problems.

 

If I want to use Microsoft’s default interface, I personally think it is too complicated, and there is less information about this part. . .

 

Thanks to the guidance of Big Stupid, I used the holiday time to re-do one, using Microsoft’s own authorization certification, and on this basis to expand. The feature is that it is very easy to use and does not require too much configuration; because it does not have “making wheels”, it is very simple if it needs to be modified.

This library is updated to .Net Core 3.0. If you need to use it on 2.2X, you can download the project to the repository and replace the Nuget package with 2.2.

Thanks to the guidance of Big Stupid Brother.

Project warehouse address
https://github.com/whuanle/CZGL.Auth

1, define roles, APIs, users

Just create a new website or API project, such as MyAuth.

Search for CZGL.Auth in Nuget, follow the 2.0.1 version, or use the Package Manager command

Install-Package CZGL.Auth -Version 2.0.1

The design idea of ​​CZGL.Auth is that a website can have multiple roles, multiple users, and multiple APIs.

A role has APIs that can add or remove roles or modify APIs for role ownership access;

A user can belong to several roles at the same time.

The first step is to consider the role of the site, the user, and the API design.

CZGL.Auth stores this information in memory, a user has several roles, and which APIs have access to a role.

The role is related to the API, and the user and the role are many-to-many relationships.

Create a new class RoleService.cs, introduce using CZGL.Auth.Services;, and RoleService inherits ManaRole.

Operate role permission information through the following interfaces

        protected bool AddRole(RoleModel role);
        protected bool AddUser(UserModel user);
        protected bool RemoveRole(string roleName);
        protected bool RemoveUser(string userName);

Obviously add/remove a role and add/remove a user

If there are three roles A, B, and C,
there are 7 APIs in /A, /B, /C, /AB, /AC, /BC, /ABC, and set permissions.

A can access A, AB, AC, ABC

B can access B, AB, BC, ABC

C can access C, AC, BC, ABC

Here, the method of simulating data is used, and the actual data is not loaded from the database.

Add a method to the RoleService

        /// <summary>
        /// API
        /// </summary>
        public void UpdateRole()
        {
            List<RoleModel> roles = new List<RoleModel>
            {
                new RoleModel
                {
                    RoleName="A",
                    Apis=new List<OneApiModel>
                    {
                        new OneApiModel
                        {
                            ApiName="A",
                            ApiUrl="/A"
                        },
                        new OneApiModel
                        {
                            ApiName="AB",
                            ApiUrl="/AB"
                        },
                        new OneApiModel
                        {
                            ApiName="AC",
                            ApiUrl="/AC"
                        },
                        new OneApiModel
                        {
                            ApiName="ABC",
                            ApiUrl="/ABC"
                        }
                    }
                },
                new RoleModel
                {
                    RoleName="B",
                    Apis=new List<OneApiModel>
                    {
                        new OneApiModel
                        {
                            ApiName="B",
                            ApiUrl="/B"
                        },
                        new OneApiModel
                        {
                            ApiName="AB",
                            ApiUrl="/AB"
                        },
                        new OneApiModel
                        {
                            ApiName="BC",
                            ApiUrl="/BC"
                        },
                        new OneApiModel
                        {
                            ApiName="ABC",
                            ApiUrl="/ABC"
                        }
                    }
                },
                new RoleModel
                {
                    RoleName="A",
                    Apis=new List<OneApiModel>
                    {
                        new OneApiModel
                        {
                            ApiName="A",
                            ApiUrl="/A"
                        },
                        new OneApiModel
                        {
                            ApiName="AB",
                            ApiUrl="/AB"
                        },
                        new OneApiModel
                        {
                            ApiName="AC",
                            ApiUrl="/AC"
                        },
                        new OneApiModel
                        {
                            ApiName="ABC",
                            ApiUrl="/ABC"
                        }
                    }
                }
            };
            foreach (var item in roles)
            {
                AddRole(item);
            }

        }

With the role and corresponding API information, you have to add users.

Suppose there are three users aa, bb, cc, the password is 123456, aa belongs to the A role, bb belongs to the B role…

        public void UpdateUser()
        {
            AddUser(new UserModel { UserName = "aa", BeRoles = new List<string> { "A" } });
            AddUser(new UserModel { UserName = "bb", BeRoles = new List<string> { "B" } });
            AddUser(new UserModel { UserName = "cc", BeRoles = new List<string> { "C" } });
        }

In order to be able to load characters and users into CZGL.Auth, you need to use it when the program starts, for example in Program.

            RoleService roleService = new RoleService();
            roleService.UpdateRole();
            roleService.UpdateUser();

2, add a custom event

Authorization is, there may be various situations, you can add authorization information recorded by the user under the custom event record, affecting the authorization result.

Quote using CZGL.Auth.Interface;,

Add a class RoleEvents to inherit IRoleEventsHadner

    public class RoleEvents : IRoleEventsHadner
    {
        public async Task Start(HttpContext httpContext)
        {
            await Task.CompletedTask;
        }
        public void TokenEbnormal(object eventsInfo)
        {
        }
        public void TokenIssued(object eventsInfo)
        {
        }
        public void NoPermissions(object eventsInfo)
        {
        }
        public void Success(object eventsInfo)
        {
        }
        public async Task End(HttpContext httpContext)
        {
            await Task.CompletedTask;
        }
    }

Call Start before CZGL.Auth starts to verify the authorization, End when the end, the incoming parameter is HttpContext type, you can add custom authorization information inside, which can affect the request pipeline.

The other methods have the following meanings:

The Token carried by the TokenEbnormal client is not a valid Jwt token and will not be parsed.

After the token is decoded, the issuer or audience is incorrect.

NoPermissions does not have access to this API

The above method will be called at each stage of the authorization authentication.

3, inject authorization services and middleware

With CZGL.Auth, you need to inject the following two services

            services.AddRoleService(authOptions);
            services.AddSingleton<IRoleEventsHadner, RoleEvents>();

AddRoleServiceIs injecting an authorization service to AddSingletoninject your event.

AddRoleService requires an AuthConfigModel type as a parameter.

You can configure this

            var authOptions = new AuthBuilder()
                .Security("aaaafsfsfdrhdhrejtrjrt", "ASPNETCORE", "ASPNETCORE")
                .Jump("accoun/login", "account/error", false, false)
                .Time(TimeSpan.FromMinutes(20))
                .InfoScheme(new CZGL.Auth.Models.AuthenticateScheme
                {
                    TokenEbnormal = "Login authentication failed!",
                    TokenIssued = "Login authentication failed!",
                    NoPermissions = "Login authentication failed!"
                }).Build();
            services.AddRoleService(authOptions);

            services.AddSingleton<IRoleEventsHadner, RoleEvents>();

The security configuration key is related to the key string, issuer, and subscriber.

Jump address when Jump configuration authorization fails. The parameters are jump when unauthorized, and invalid jump for authorization. The latter two bools can be set to jump or jump.

Time Configures the token validity period.

InfoScheme authorization failure message, for example

A_jwt_lightweight_single_API_authorization_using_ASP.NET_Core_3.0_0.png

The above picture is the time-out prompt message. When the user requests the API to fail, it returns the 401 status code. The Header will carry the prompt message. In CZGL.Auth, there are three cases, the custom header:

The Token carried by the TokenEbnormal client is not a valid Jwt token and will not be parsed.

After the token is decoded, the issuer or audience is incorrect.

NoPermissions does not have access to this API

Add three middleware

            app.UseAuthentication();
            app.UseAuthorization();
            app.UseMiddleware<RoleMiddleware>();

app.UseAuthorization();It is the middleware authorized by Microsoft. CZGL.Auth will let the default authentication pipeline filter some invalid requests and authentication information, and then CZGL.Auth will verify the authorization.

4, how to set the authorization of the API

Very simple, CZGL.Auth authentication authorization, you just add it on the Controller or Action [Authorize].

CZGL.Auth will only use the [Authorize]entry into force characteristics of the Controller or Action.

If a Controller has been set [Authorize], but you want to skip the inside of the Action authorization certification, the use of [AllowAnonymous]modified Action.

The usage is exactly the same as Microsoft’s default. This does not require too much configuration.

If you want to define another feature to set up an additional license, you can go to my repository or follow me on WeChat.

Add an APIController ,

    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {

        [HttpGet("/A")]
        public JsonResult A()
        {
            return new JsonResult(new { Code = 200, Message = "Success!" });
        }

        [HttpGet("/B")]
        public JsonResult B()
        {
            return new JsonResult(new { Code = 200, Message = "Success!" });
        }

        [HttpGet("/C")]
        public JsonResult C()
        {
            return new JsonResult(new { Code = 200, Message = "Success!" });
        }
        [HttpGet("/AB")]
        public JsonResult AB()
        {
            return new JsonResult(new { Code = 200, Message = "Success!" });
        }
        [HttpGet("/BC")]
        public JsonResult BC()
        {
            return new JsonResult(new { Code = 200, Message = "Success!" });
        }
        [HttpGet("/AC")]
        public JsonResult AC()
        {
            return new JsonResult(new { Code = 200, Message = "Success!" });
        }

        [HttpGet("/ABC")]
        public JsonResult ABC()
        {
            return new JsonResult(new { claims = User.Claims });
        }


        /// <summary>
        /// deny everynone
        /// </summary>
        /// <returns></returns>
        [HttpGet("D")]
        public JsonResult D()
        {
            return new JsonResult(new { Code = 200, Message = "Success!" });
        }

        [HttpGet("error")]
        public JsonResult Denied()
        {
            return new JsonResult(
                new
                {
                    Code = 0,
                    Message = "acess failed!",
                    Data = "no rights!"
                });
        }
    }

5, add a login to issue a token

Add an AccountController.cs to issue logins, tokens.

    [Route("api/[controller]")]
    [ApiController]
    public class AccountController : ControllerBase
    {
        [HttpPost("/Login")]
        public async Task<JsonResult> Login([FromQuery]string username, string password, string rolename)
        {
            // name and password
            if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(rolename))
            {
                return new JsonResult(new 
                {
                    Code = 0,
                    Message = "invalid",
                });
            }

            if(!((username=="aa"||username=="bb"||username=="cc")&&password=="123456"))
            {
                return new JsonResult(new
                {
                    Code = 0,
                    Message = "error",
                });
            }

            // define role and user
            RoleService roleService = new RoleService();

            // check user and role
            var role = roleService.IsUserToRole(username,rolename);

            // CZGL.Auth 
            EncryptionHash hash = new EncryptionHash();

            // set user tag
            var userClaims = hash.BuildClaims(username, rolename);

          
            //var userClaims = new Claim[]
            //{
            //new Claim(ClaimTypes.Name, userName),
            //    new Claim(ClaimTypes.Role, roleName),
            //    new Claim(JwtRegisteredClaimNames.Aud, Audience),
            //    new Claim(ClaimTypes.Expiration, TimeSpan.TotalSeconds.ToString()),
            //    new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString())
            //};
            /*
            iss (issuer):
            exp (expiration time):
            sub (subject):
            aud (audience):
            nbf (Not Before):
            iat (Issued At):
            jti (JWT ID):
            */

            // 1, Token
            ResponseToken token = hash.BuildToken(userClaims);


            //2,
            //var identity = hash.GetIdentity(userClaims);
            //var jwt = hash.BuildJwtToken(userClaims);
            //var token = hash.BuildJwtResponseToken(jwt);

            return new JsonResult(token);
        }
    }

6. Partial explanation

Inject Jwt service, issue Token

CZGL.Auth encapsulates the services that use jwt and the tokens that are issued. This library is not “making wheels”, so you can easily extract this part of the code and design it.

This part of the code is located in RoleServiceExtension.cs, EncryptionHash.cs.

Authorized middleware

            app.UseAuthentication();
            app.UseAuthorization();
            app.UseMiddleware<RoleMiddleware>();

My approach is to use the jwt of ASP.NET Core to complete the basic authentication and authorization, and then implement the extended certification in the next pipeline. But the authentication itself is extended in app.UseAuthorization(); so using CZGL.Auth, you only need to use it in the usual way of jwt, just add a RoleMiddleware middleware.

CZGL.Auth is only temporarily written by my new ideas. . . It is best not to use it directly for production, go to the github library to download the project, and change it according to your own application scenario.

7, verification

Log in first with the aa user and select the A role when logging in.

A_jwt_lightweight_single_API_authorization_using_ASP.NET_Core_3.0_1.png

Because A users can only access APIs with “A”, “/A”, “/AB”, etc., so we can try.

A_jwt_lightweight_single_API_authorization_using_ASP.NET_Core_3.0_2.png

Continue to use this Token to access “/B”

A_jwt_lightweight_single_API_authorization_using_ASP.NET_Core_3.0_3.png

You can continue to try to add APIs or log in with other users to access different APIs.

Since others are not familiar with the front end, they do not write an example with a page~.

You can test it with Postman.

What example project can be downloaded from the repository with the name MyAuth.

Generally, user rights and role permission information are stored in the database, and another example is CZGL.Auth.Sample2.

This library is only a relatively rough authorization certificate, please download the source code to modify it with more abundant requirements~

If you have any questions, you can find me in the club.

I am in Shenzhen, Guangzhou, Changsha, Shanghai, etc., hehe, hehe.

Orignal link:https://www.cnblogs.com/whuanle/p/11743406.html