“Permission Design” about the permission design of some schemes, here is the use of.net Core to achieve JWT authorization verification, in order to facilitate the usual fast access, out of the box. JWT has issued token end validation (JwtSecurityTokenExtension) and the receiving end (PermissionsControl) two pieces of content, making the source code.

Jwt Token issuer

Sometimes simply by JWT Identity4 don’t need to, kill the goose that how to use curtilage, JwtSecurityTokenExtension class library token is generated using the Microsoft System. IdentityModel. Tokens. JWT, Encryption algorithm is symmetric encryption (HS256), create a token and the method of the refresh token, talk about ideas, key steps and code specific code can read JwtSecurityTokenExtension themselves to making the class library

Token Indicates the content of the ciphertext

By default, the token generation takes several parameters, including the header information, encryption method and type, and the body. This is mandatory for the verification end to check whether the token has expired. The generated token can be parsed through jwt.calebb.net/

{
    alg: "HS256",
    typ: "JWT"
}.
{
    exp:"JWT expiration time",
    nbf:"JWT effective Time"
}
Copy the code

The CreateToken() method is a dictionary of parameters that you need to pass into the token and encrypt it together.

    public interface IJwtSecurityTokenExtension
    {
        TokenOut CreateToken(Dictionary<string, string> claimsContent = null);
        TokenOut RefreshToken(string refreshToken);
    }
Copy the code

The return value TokenOut contains the token value, the refreshed token value, and the expiration time for the front-end to verify whether the token is expired. The front-end can verify whether the token is expired first.

    public class TokenOut
    {
        /// <summary>
        /// token
        /// </summary>
        public string Token { get; set; } /// <summary> // token expiration time /// </summary> public DateTime TokenExpires {get;set; } /// </summary> // </summary> public string RefreshToken {get;set; } /// </summary> // </summary> public DateTime RefreshTokenExpires {get;set; }}Copy the code

Usage of the Token and refreshToken

When the client requests the authorization server, the expiration time of the two tokens is short, while the expiration time of the refreshToken is long. If the token expires, the client uses the refreshToken to exchange a new token with the authorization server. This eliminates the need for frequent login, as each login requires the user to fill in a login password or third-party authorization.

For this purpose, I actually had a question at the beginning of this project. Since refreshToken can be used to replace the new token, why not directly set the time of the token to be longer? It’s good to be simple and rough

In fact, this can be done, but sometimes for security reasons, dual tokens can increase security a little bit (reduce the possibility of an attack if the token is intercepted), because the client interacts with the application service far more often than the client interacts with the authorized service. It is highly likely that a hacker can hijack the tokens that interact with the client and application services. However, the tokens are relatively secure due to their short expiration time.

Change the storage mode of refreshToken

RefreshToken creation inherits from IRefreshTokenStore and is injected as TryAddSingleton. You can replace the refreshToken implementation with your own Startup re-injection (TryAddSingleton feature)

public interface IRefreshTokenStore { RefreshTokenModel Get(string refreshToken); void Set(string refreshToken, RefreshTokenModel model, TimeSpan expirationTime); } // inject services.TryAddSingleton<IRefreshTokenStore, RefreshTokenStore>();Copy the code

Jwt Token verification end

There is an article about several schemes (resource based/role based/permission based)

The Github source PermissionsControl module implements each of these cases and components. Can handle a wide variety of situations without lifting the wheel.

use

Only ConfigureServices injection is required, but only permission-based and role-based.

Resource-based unity is very poor, so you need to write it yourself (DocumentAuthorization under WebApp project is demo).

[Authorize] verifies only whether the token time has expired. [UserPermissionCheck()] and [RoleCheck(“admin”)] verify permissions and roles respectively.

services.AddExtensionAuthorization(Configuration);
Copy the code

Based on the role

If you need this to recognize the role, you need to add it when you create the token, rol is the convention

            var dic = new Dictionary<string, string>
            {
                {"rol", role},// role};return _tokenHelper.CreateToken(dic);
Copy the code

[RoleCheck(“admin”)] can be used directly

This is for projects where roles are linked to permissions and permissions change less. Just figure out the characters during development.

Based on the permissions

If you want this privilege to be recognized, you need to add it when you create the token. Per is the convention

            var dic = new Dictionary<string, string>
            {
                {"per", role},// permissions};return _tokenHelper.CreateToken(dic);
Copy the code

[PermissionCheck(“update”)]

This method is applicable to projects with few permissions. Because all permission names are stored in tokens, the token length increases, resulting in a waste of network resources for each request.

So it extends

[UserPermissionCheck()]

To use this tag, you need to implement the IUserPermissionStore interface to obtain user permissions. In this way, permissions are persisted to the storage medium and then queried by GetUserPermission(), so that the token does not need to carry all user permissions. In fact, if you do this level, whether to use JWT is also a problem, session or every time query redis verification is better, it depends on opinion.

    public interface IUserPermissionStore
    {
        string [] GetUserPermission(object key);
    }
Copy the code

This approach is more suitable for microservices where multiple systems (subsystems) are the same as user systems. Users and their permissions can be managed in a unified manner, but the permissions of each subsystem are different. When each service is started, all permissions of the service will be synchronized to the general management system for unified management.