基于Angular和AspNetCore的权限管理系统(三)登录流程

认证方式

aspnetcore的基本认证方式是:携带着认证信息的数据进入应用程序,经过认证中间件。如果验证通过,会把信息钟携带的用户的claim集中生成ClaimsPrincipal,然后就可以在程序中访问用户信息了。

在本系统中使用了JWT的方式进行认证,关于jwt可以看这里。aspnetcore使用jwt也很简单,官方文档有很直接的例子。在系统中实现了这样几项和jwt相关的功能。

  • jwt是有时效性的,为了防止token过期影响用户操作。在客户端创建了http请求的管道,在进行任何服务器请求前先对token的expire进行判断是否过期。如果将要过期或已经过期,就去请求后台的刷新token接口。后台接口会读取旧token,刷新服务是允许token过期一小段时间的,验证通过则会根据旧token信息派发新token。用户取得新token后,才会继续后面的处理。这样就完成了在用户无感知情况下保持登录状态。
  • 为了防止token被滥用,在服务端生成token的同时会以用户名为key,token数据为value记录在缓存中,相当于白名单。如果账号在别地登录,或者管理员修改用户信息,都会导致token缓存发生变化。在认证过程中,就会失败并返回错误信息。这时客户端可以根据错误信息,强制用户注销。jwt本身的认证方式和白名单并不冲突,因为白名单不仅可以记录token本身,还可以记录ip等信息来防止异地访问。下面是白名单验证的代码:
services.AddAuthentication(ops =>
            {
                ops.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                ops.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(ops =>
            {
                ops.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidIssuer = option.Issuer,
                    ValidateIssuer = true,
                    ValidAudience = option.Audience,
                    ValidateAudience = true,
                    IssuerSigningKey = option.SecurityKey,
                    ValidateIssuerSigningKey = true,
                    ValidateLifetime = true,
                };

                ops.Events = new JwtBearerEvents()
                {
                    OnMessageReceived = context => { return Task.CompletedTask; },
                    OnTokenValidated = context =>
                     {
                         var memoryCache = context.HttpContext.RequestServices.GetRequiredService<IMemoryCache>();

                         var userName = context.Principal.GetUserName();
                         var exist = memoryCache.TryGetValue(userName, out string token);
                         if (exist)
                         {
                             var bearerToken = context.Request.Headers["Authorization"].ToString();
                             if (!bearerToken.Contains(token))
                             {
                                 context.Fail("TokenNoExist");
                             }
                         }
                         else
                         {
                             context.Fail("TokenNoExist");
                         }
                         return Task.CompletedTask;
                     },
                    OnChallenge = context =>
                    {
                        if (context.AuthenticateFailure.Message == "TokenNoExist")
                        {
                            context.HandleResponse();
                            context.Response.StatusCode = 470;
                        }
                        return Task.CompletedTask;
                    }

                };
            });

授权方式

在上边的认证中获取了用户的信息,所以这里就需要根据用户的信息去给用户进行授权。微软提供了丰富的授权方式,我们的需求要做的是API的访问授权,需要自定授权策略。

这里的步骤就简单了,我们先建立ApiAuthorizationHandler这个类,对请求进行处理判断。首先我们获取请求的api路径和http方法。然后判断此请求是否在用户的角色中的元素的api的合集中,如果在说明用户有权限访问此处理,如果没有则禁止授权。

然后将ApiAuthorizationRequirement和ApiAuthorizationHandler注册到服务中。

            //【授权】
            services.AddAuthorization(options =>
            {
                options.AddPolicy("ApiPermission", policy => policy.Requirements.Add(new ApiAuthorizationRequirement()));
            });

            // 注入权限处理器
            services.AddTransient<IAuthorizationHandler, ApiAuthorizationHandler>();

最后一步,绑定控制器

    /// <summary>
    /// 授权访问API控制器
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiController]
    [Authorize]
    [Authorize("ApiPermission")]
    public abstract class AuthorizeController : ControllerBase
    {
    }

然后所有需要进行认证和授权的控制器继承此类就完成了。

授权方式

下边说一下系统的登录流程,画了一个简单的图

在token中只保存了用户名和角色等简单的信息。页面访问的Identifycation和route是放在response中返回到web端,由web端自行处理。角色的api的访问权限放在缓存中,授权时根据用户的角色进行判断。用户的数据过滤器放在缓存中,数据的仓储类实例化时会自动加载缓存中的过滤器。这样就完成了整个的权限认证过程。

除了用户名密码登录也可以任意更改成其他类型的登录方式,譬如手机登录,微信第三方登录。所有登录方式都需要去取得用户数据,然后后面的流程就都一样了。

总结

这三篇文章讲了一下系统思路,可能有错误的地方。要是觉得有点帮助,可以star一下。有时间会继续完善重构现在的功能,未来计划添加更多模块,做成一个大系统。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章