OsharpNS轻量级.net core快速开发框架简明入门教程-Osharp.Permissions使用
OsharpNS轻量级.net core快速开发框架简明入门教程
教程目录
-
从零开始启动Osharp
1.1. 使用OsharpNS项目模板创建项目
1.2. 配置数据库连接串并启动项目
1.3. OsharpNS.Swagger使用实例(登录和授权)
1.4. Angular6的前端项目启动
-
Osharp代码生成器的使用
2.1 生成器的使用
-
Osharp部分模块使用
3.1 Osharp.Redis使用
-
Osharp深度学习和使用
4.2 多上下文配置(多个数据库的使用)
4.3. 自定义模块的定义(Senparc.Weixin的使用)
4.4. 继续学习中....
OsharpNS官方资源
项目地址:https://github.com/i66soft/osharp-ns20
演示地址:https://www.osharp.org 直接使用QQ登录可以查看效果
文档地址:https://docs.osharp.org 正在完善中....
发布博客:https://www.cnblogs.com/guomingfeng/p/osharpns-publish.html 大神看这个文档应该就能跑起来,从零开始启动Osharp基于此文档完成
VS生成器插件:https://marketplace.visualstudio.com/items?itemName=LiuliuSoft.osharp
官方交流QQ群:85895249
Osharp.Permissions使用
-
系统授权检查流程
核心代码位于
Osharp/Secutiry/FunctionAuthorizationBase
检查过程如下:
-
检查function是否为null,为null反馈错误,否则继续检查
-
检查function是否被禁用,被禁用反馈错误,否则继续检查
-
检查功能是否任何人可用,如果是,直接返回成功,否则继续检查
-
检查用户是否登陆,未登录反馈错误,否则继续检查
-
用户已登陆,判断功能是否登陆即可使用,如果是,反馈成功,否则继续检查
-
获取用户的角色,判断角色是否有角色允许执行功能,如果是,反馈成功,否则继续检查
-
获取用户能执行的所有功能,判断是否包含功能,如果是,反馈成功;如果否,反馈失败(系统除了给定用户角色,还能根据用户单独给定功能授权,所以检查完角色之后不满足条件,还要检查用户私有的功能是否包含)
/// <summary> /// 重写以实现权限检查核心验证操作 /// </summary> /// <param name="function">要验证的功能信息</param> /// <param name="principal">当前用户在线信息</param> /// <returns>功能权限验证结果</returns> protected virtual AuthorizationResult AuthorizeCore(IFunction function, IPrincipal principal) { if (function == null) { return new AuthorizationResult(AuthorizationStatus.NoFound); } if (function.IsLocked) { return new AuthorizationResult(AuthorizationStatus.Locked, $"功能“{function.Name}”已被禁用,无法执行"); } if (function.AccessType == FunctionAccessType.Anonymouse) { return AuthorizationResult.OK; } //未登录 if (principal == null || !principal.Identity.IsAuthenticated) { return new AuthorizationResult(AuthorizationStatus.Unauthorized); } //已登录,无角色限制 if (function.AccessType == FunctionAccessType.Logined) { return AuthorizationResult.OK; } return AuthorizeRoleLimit(function, principal); } /// <summary> /// 重写以实现 角色限制 的功能的功能权限检查 /// </summary> /// <param name="function">要验证的功能信息</param> /// <param name="principal">用户在线信息</param> /// <returns>功能权限验证结果</returns> protected virtual AuthorizationResult AuthorizeRoleLimit(IFunction function, IPrincipal principal) { //角色限制 if (!(principal.Identity is ClaimsIdentity identity)) { return new AuthorizationResult(AuthorizationStatus.Error, "当前用户标识IIdentity格式不正确,仅支持ClaimsIdentity类型的用户标识"); } //检查角色-功能的权限 string[] userRoleNames = identity.GetRoles().ToArray(); AuthorizationResult result = AuthorizeRoleNames(function, userRoleNames); if (result.IsOk) { return result; } result = AuthorizeUserName(function, principal.Identity.GetUserName()); return result; } /// <summary> /// 重写以实现指定角色是否有执行指定功能的权限 /// </summary> /// <param name="function">功能信息</param> /// <param name="roleNames">角色名称</param> /// <returns>功能权限检查结果</returns> protected virtual AuthorizationResult AuthorizeRoleNames(IFunction function, params string[] roleNames) { Check.NotNull(roleNames, nameof(roleNames)); if (roleNames.Length == 0) { return new AuthorizationResult(AuthorizationStatus.Forbidden); } if (function.AccessType != FunctionAccessType.RoleLimit || roleNames.Contains(SuperRoleName)) { return AuthorizationResult.OK; } string[] functionRoleNames = FunctionAuthCache.GetFunctionRoles(function.Id); if (roleNames.Intersect(functionRoleNames).Any()) { return AuthorizationResult.OK; } return new AuthorizationResult(AuthorizationStatus.Forbidden); } /// <summary> /// 重写以实现指定用户是否有执行指定功能的权限 /// </summary> /// <param name="function">功能信息</param> /// <param name="userName">用户名</param> /// <returns>功能权限检查结果</returns> protected virtual AuthorizationResult AuthorizeUserName(IFunction function, string userName) { if (function.AccessType != FunctionAccessType.RoleLimit) { return AuthorizationResult.OK; } Guid[] functionIds = FunctionAuthCache.GetUserFunctions(userName); if (functionIds.Contains(function.Id)) { return AuthorizationResult.OK; } return new AuthorizationResult(AuthorizationStatus.Forbidden); }
-
-
Controller里面对是否检查权限的控制
文件路径为
CanDoo.Test.Web.Areas.Admin.Controllers
,注意看文件中的[RoleLimit]
,带了这个就要做检查// ----------------------------------------------------------------------- // <copyright file="AdminApiController.cs" company="OSharp开源团队"> // Copyright (c) 2014-2018 OSharp. All rights reserved. // </copyright> // <site>http://www.osharp.org</site> // <last-editor>郭明锋</last-editor> // <last-date>2018-06-27 4:50</last-date> // ----------------------------------------------------------------------- using Microsoft.AspNetCore.Mvc; using OSharp.AspNetCore.Mvc; using OSharp.Core; namespace CanDoo.Test.Web.Areas.Admin.Controllers { [Area("Admin")] [RoleLimit] public abstract class AdminApiController : AreaApiController { } }
-
不通过数据库强制给定用户角色的方法
3.1 通过Claim实现
在`IdentityController.cs`中,用户登陆后,生成Claim时,将角色给定
private async Task<string> CreateJwtToken(User user) { //在线用户缓存 IOnlineUserCache onlineUserCache = HttpContext.RequestServices.GetService<IOnlineUserCache>(); if (onlineUserCache != null) { await onlineUserCache.GetOrRefreshAsync(user.UserName); } //生成Token,这里只包含最基本信息,其他信息从在线用户缓存中获取 Claim[] claims = { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.UserName), new Claim("ExtendRoles", "学生") //这行是新增的,强制给定学生角色,用户角色表中不存在 用户和学生 角色的关联 }; OsharpOptions options = HttpContext.RequestServices.GetService<IOptions<OsharpOptions>>().Value; string token = JwtHelper.CreateToken(claims, options); return token; }
在`CanDoo.Test.Core`中新建类`OnlineUserJwtSecurityTokenHandler.cs`
// ----------------------------------------------------------------------- // <copyright file="OnlineUserJwtSecurityTokenHandler.cs" company="OSharp开源团队"> // Copyright (c) 2014-2018 OSharp. All rights reserved. // </copyright> // <site>http://www.osharp.org</site> // <last-editor>郭明锋</last-editor> // <last-date>2018-07-09 15:01</last-date> // ----------------------------------------------------------------------- using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.IdentityModel.Tokens; using OSharp.Collections; using OSharp.Dependency; using OSharp.Identity; using OSharp.Secutiry.Claims; namespace CanDoo.Test.Identity { /// <summary> /// 使用在线用户信息和JwtToken生成在线ClaimsIdentity /// </summary> public class OnlineUserJwtSecurityTokenHandler : JwtSecurityTokenHandler { /// <summary> /// Creates a <see cref="T:System.Security.Claims.ClaimsIdentity" /> from a <see cref="T:System.IdentityModel.Tokens.Jwt.JwtSecurityToken" />. /// </summary> /// <param name="jwtToken">The <see cref="T:System.IdentityModel.Tokens.Jwt.JwtSecurityToken" /> to use as a <see cref="T:System.Security.Claims.Claim" /> source.</param> /// <param name="issuer">The value to set <see cref="P:System.Security.Claims.Claim.Issuer" /></param> /// <param name="validationParameters"> Contains parameters for validating the token.</param> /// <returns>A <see cref="T:System.Security.Claims.ClaimsIdentity" /> containing the <see cref="P:System.IdentityModel.Tokens.Jwt.JwtSecurityToken.Claims" />.</returns> protected override ClaimsIdentity CreateClaimsIdentity(JwtSecurityToken jwtToken, string issuer, TokenValidationParameters validationParameters) { ClaimsIdentity identity = base.CreateClaimsIdentity(jwtToken, issuer, validationParameters); var extendRoles = identity.GetClaimValueFirstOrDefault("ExtendRoles");//从Claim中获取强制给定的角色 if (identity.IsAuthenticated) { //由在线缓存获取用户信息赋给IIdentity IOnlineUserCache onlineUserCache = ServiceLocator.Instance.GetService<IOnlineUserCache>(); OnlineUser user = onlineUserCache.GetOrRefresh(identity.Name); if (user == null) { return null; } if (!string.IsNullOrEmpty(user.NickName)) { identity.AddClaim(new Claim(ClaimTypes.GivenName, user.NickName)); } if (!string.IsNullOrEmpty(user.Email)) { identity.AddClaim(new Claim(ClaimTypes.Email, user.Email)); } //这部分将从Claim中获取的角色进行赋值 string roles = ""; if (user.Roles.Length > 0) roles = user.Roles.ExpandAndToString() + "," + extendRoles; else roles = extendRoles; if (roles != "") identity.AddClaim(new Claim(ClaimTypes.Role, user.Roles.ExpandAndToString())); } ScopedDictionary dict = ServiceLocator.Instance.GetService<ScopedDictionary>(); dict.Identity = identity; return identity; } } }
对`CanDoo.Test.Core`中`IdentityPack`中的代码进行调整
jwt.SecurityTokenValidators.Clear(); jwt.SecurityTokenValidators.Add(new OnlineUserJwtSecurityTokenHandler());//这里要使用本文中创建的OnlineUserJwtSecurityTokenHandler jwt.Events = new JwtBearerEvents() { // 生成SignalR的用户信息 OnMessageReceived = context => { string token = context.Request.Query["access_token"]; string path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(token) && path.Contains("hub")) { context.Token = token; } return Task.CompletedTask; } };
3.2 通过替换现有OnlineUserProvider缓存方法实现
在`CanDoo.Test.Core`中新建`MyOnlineUserProvider`
// ----------------------------------------------------------------------- // <copyright file="OnlineUserProvider.cs" company="OSharp开源团队"> // Copyright (c) 2014-2018 OSharp. All rights reserved. // </copyright> // <site>http://www.osharp.org</site> // <last-editor>郭明锋</last-editor> // <last-date>2018-08-17 22:36</last-date> // ----------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using OSharp.Identity; namespace CanDoo.Test.Identity { /// <summary> /// 在线用户信息提供者 /// </summary> public class MyOnlineUserProvider<TUser, TUserKey, TRole, TRoleKey> : IOnlineUserProvider where TUser : UserBase<TUserKey> where TUserKey : IEquatable<TUserKey> where TRole : RoleBase<TRoleKey> where TRoleKey : IEquatable<TRoleKey> { /// <summary> /// 创建在线用户信息 /// </summary> /// <param name="provider">服务提供器</param> /// <param name="userName">用户名</param> /// <returns>在线用户信息</returns> public virtual async Task<OnlineUser> Create(IServiceProvider provider, string userName) { UserManager<TUser> userManager = provider.GetService<UserManager<TUser>>(); TUser user = await userManager.FindByNameAsync(userName); if (user == null) { return null; } IList<string> roles = await userManager.GetRolesAsync(user); roles.Add("学生");//这样就强制给用户赋值了,当然可以对其他东西也做手脚 RoleManager<TRole> roleManager = provider.GetService<RoleManager<TRole>>(); bool isAdmin = roleManager.Roles.Any(m => roles.Contains(m.Name) && m.IsAdmin); return new OnlineUser() { Id = user.Id.ToString(), UserName = user.UserName, NickName = user.NickName, Email = user.Email, HeadImg = user.HeadImg, IsAdmin = isAdmin, Roles = roles.ToArray() }; } } }
对`CanDoo.Test.Core`中的`IdentityPack`修改代码,将原有的`OnlineUserProvider`换为`MyOnlineUserProvider`
/// <summary> /// 将模块服务添加到依赖注入服务容器中 /// </summary> /// <param name="services">依赖注入服务容器</param> /// <returns></returns> public override IServiceCollection AddServices(IServiceCollection services) { services.AddScoped<IIdentityContract, IdentityService>(); base.AddServices(services); services.Replace(new ServiceDescriptor(typeof(IOnlineUserProvider), typeof(MyOnlineUserProvider<User, int, Role, int>), ServiceLifetime.Scoped)); return services; }