最近在研究 Asp.net Core 基于 JWT 授权码模式去实现策略授权 (根据用户的角色动态判断是否拥有对访问接口的权限). 在完成 jwt 的授权码和模拟用户,角色等数据后,想着去在策略授权的时候直接去访问数据库的数据。发现这并不容易。查找一些资料后
# 核心代码
| // 连接字符串 |
| services.AddDbContext<JWTContext>(options => |
| options.UseSqlServer (Configuration.GetConnectionString ("JWTDBConnection"))); |
| |
| services.AddScoped<IAppSettings, AppSettings>(); |
| |
| // 读取数据库配置策略授权 (重点) |
| services.AddOptions<AuthorizationOptions>().Configure<IServiceScopeFactory>((options, sp) => |
| { |
| using (var scope = sp.CreateScope ()) |
| { |
| IAppSettings settings = scope.ServiceProvider.GetRequiredService<IAppSettings>(); |
| var Permission = settings.userPermissions (); |
| options.AddPolicy ("Permission", policy => policy.Requirements.Add (Permission)); |
| } |
| }); |
查看上面的代码我们可以发现,在连接数据的下面。我们让容器中注入了一个服务:
services.AddScoped<IAppSettings, AppSettings>()
# 定义接口
| public interface IAppSettings |
| { |
| PolicyRequirement userPermissions(); |
| } |
# 实现接口
| public class AppSettings:IAppSettings |
| { |
| public JWTContext context; |
| |
| public AppSettings(JWTContext context) |
| { |
| this.context = context; |
| } |
| |
| public PolicyRequirement userPermissions() |
| { |
| PolicyRequirement policyRequirement = new PolicyRequirement(); |
| |
| var _jWTContext = context; |
| var perssions = _jWTContext.permssions; |
| var roles = _jWTContext.roles; |
| policyRequirement.DeniedAction = new PathString("/api/nopermission"); |
| policyRequirement.UserPermissions = (from r in roles |
| join p in perssions |
| on r.Id equals p.RoleId |
| select new UserPermission |
| { |
| UserName = r.Name, |
| Url = "/WeatherForecast" + p.Permssions |
| }).ToList(); |
| |
| |
| return policyRequirement; |
| } |
# 策略授权的核心代码
| public class PolicyRequirement: IAuthorizationRequirement |
| { |
| |
| /// <summary> |
| /// 用户权限集合 |
| /// </summary> |
| public List<UserPermission> UserPermissions { get; set; } |
| /// <summary> |
| /// 无权限 action |
| /// </summary> |
| public string DeniedAction { get; set; } |
| /// <summary> |
| /// 构造 |
| /// </summary> |
| public PolicyRequirement () |
| { |
| |
| // 没有权限则跳转到这个路由 |
| DeniedAction = new PathString ("/api/nopermission"); |
| // 用户有权限访问的路由配置,当然可以从数据库获取 |
| UserPermissions = new List<UserPermission> { |
| new UserPermission { Url="/WeatherForecast/Tourist", UserName="user"}, |
| }; |
| } |
| } |
| |
| |
| |
| /// <summary> |
| /// 用户权限承载实体 |
| /// </summary> |
| public class UserPermission |
| { |
| /// <summary> |
| /// 用户名 |
| /// </summary> |
| public string UserName { get; set; } |
| /// <summary> |
| /// 请求 Url |
| /// </summary> |
| public string Url { get; set; } |
| } |
| public class PolicyHandler : AuthorizationHandler<PolicyRequirement> |
| { |
| |
| |
| protected override Task HandleRequirementAsync (AuthorizationHandlerContext context, PolicyRequirement requirement) |
| { |
| //var httpContext = ((context.Resource) as Microsoft.AspNetCore.Routing.RouteEndpoint); |
| //dynamic httpContext = context.Resource; |
| |
| var httpContext = context.Resource as HttpContext; |
| |
| var questUrl = httpContext.Request.Path.Value; |
| |
| // 赋值用户权限 |
| var userPermissions = requirement.UserPermissions; |
| // 是否经过验证 |
| var isAuthenticated = context.User.Identity.IsAuthenticated; |
| |
| if (isAuthenticated) |
| { |
| if (userPermissions.GroupBy (g => g.Url).Any (w => w.Key == questUrl)) |
| { |
| // 用户名 |
| var userName = context.User.Claims.Where (x=>x.Type=="roless").First ().Value; |
| if (userPermissions.Any (w => w.UserName == userName && w.Url== questUrl)) |
| { |
| context.Succeed (requirement); |
| } |
| else |
| { |
| // 无权限跳转到拒绝页面 |
| //context.Fail (); |
| //httpContext.Response.Redirect ("https://localhost:5001/api/nopermission"); |
| context.Fail (); |
| var Response = httpContext.Response; |
| var message = Encoding.UTF8.GetBytes ("User with Super Admin role cannot be edited"); |
| |
| Response.OnStarting (async () => |
| { |
| httpContext.Response.StatusCode = 429; |
| await Response.Body.WriteAsync (message, 0, message.Length); |
| }); |
| } |
| } |
| else |
| { |
| context.Succeed (requirement); |
| } |
| |
| } |
| return Task.CompletedTask; |
| } |
| } |