1.Framework.Core》Authorization》PermissionNames.cs 这里新增权限项
namespace Framework.Authorization { public static class PermissionNames { public const string Pages_Tenants = "Pages.Tenants"; public const string Pages_Users = "Pages.Users"; public const string Pages_Roles = "Pages.Roles"; /// <summary> /// 产品-浏览 /// </summary> public const string Pages_Products = "Pages.Products"; /// <summary> /// 产品-编辑 /// </summary> public const string Pages_Products_Edit = "Pages.Products.Edit"; /// <summary> /// 产品-删除 /// </summary> public const string Pages_Products_Delete = "Pages.Products.Delete"; } }
2.Framework.Core》Authorization》FrameworkAuthorizationProvider.cs 这里创建权限,这样在页面中添加角色的时候才能显示出来
using Abp.Authorization; using Abp.Localization; using Abp.MultiTenancy; namespace Framework.Authorization { public class FrameworkAuthorizationProvider : AuthorizationProvider { public override void SetPermissions(IPermissionDefinitionContext context) { context.CreatePermission(PermissionNames.Pages_Users, L("Users")); context.CreatePermission(PermissionNames.Pages_Roles, L("Roles")); context.CreatePermission(PermissionNames.Pages_Tenants, L("Tenants"), multiTenancySides: MultiTenancySides.Host); #region 创建权限 var per = context.CreatePermission(PermissionNames.Pages_Products, L("Products")); per.CreateChildPermission(PermissionNames.Pages_Products_Edit, L("EditProducts")); per.CreateChildPermission(PermissionNames.Pages_Products_Delete, L("DeleteProducts")); #endregion } private static ILocalizableString L(string name) { return new LocalizableString(name, FrameworkConsts.LocalizationSourceName); } } }
3.其中 Products、EditProducts、DeleteProducts 需要配置本地语言包文件:Framework.Core》Localization》Source》Framework-zh-CN.xml 这是中文的。
<!-- 自定义 --> <text name="Roles" value="角色" /> <text name="Products" value="商品" /> <text name="EditProducts" value="编辑商品" /> <text name="DeleteProducts" value="删除商品" />
4.左侧菜单 Framework.Web》App_Start》FrameworkNavigationProvider.cs
requiresAuthentication: true 需要登录;requiredPermissionName: PermissionNames.Pages_Products 需要权限
注册菜单在:Framework.Web》App_Start》FrameworkWebModule.cs 的 public override void PreInitialize(){...}里
5.配置Action需要授权,在Action上面加上特性:[AbpAuthorize(PermissionNames.Pages_Products_Delete)]
或者在Action里使用 PermissionChecker,
PermissionChecker.IsGranted(PermissionNames.Pages_Products_Delete) 返回true或者false,
PermissionChecker.Authorize(PermissionNames.Pages_Products_Delete) 如果没有授权,会抛出异常。
在Controller上使用[AbpMvcAuthorize],在AppService上面使用[AbpAuthorize],访问必须先登录;不需要登录[DisableAuditing]。
using Abp.Application.Services; using Abp.Application.Services.Dto; using Abp.Auditing; using Abp.Domain.Repositories; using Abp.Linq.Expressions; using Abp.Linq.Extensions; using Abp.ObjectMapping; using Abp.UI; using Framework.Common; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using System.Linq.Dynamic.Core; using Abp.Authorization; using Abp.Events.Bus; using Framework.Events; using Framework.Products; using Framework.Products.Dto; namespace Framework.Products { [AbpAuthorize] public class ProductAppService : AsyncCrudAppService<Product, ProductInputDto, long, PagedResultRequestDto, ProductInputDto, ProductInputDto>, IProductAppService { private readonly IRepository<Product, long> _productRepository; private readonly IObjectMapper _objectMapper; public IEventBus EventBus { get; set; } public ProductAppService(IRepository<Product, long> repository, IObjectMapper objectMapper ) : base(repository) { _objectMapper = objectMapper; _productRepository = repository; EventBus = NullEventBus.Instance; } [DisableAuditing] public async Task<PagedResultDto<ProductInputDto>> GetAllByNameAsync(PagedResultByNameDto input) { CheckGetAllPermission(); IQueryable<Product> query = CreateGetAllFilteredQuery(input); int totalCount = await AsyncQueryableExecuter.CountAsync(query); query = ApplySorting(query, input); query = ApplyPaging(query, input); var res = new PagedResultDto<ProductInputDto>(totalCount, (await AsyncQueryableExecuter.ToListAsync(query)).Select(MapToEntityDto).ToList()); return res; } protected IQueryable<Product> CreateGetAllFilteredQuery(PagedResultByNameDto input) { Expression<Func<Product, bool>> whereFilter = w => true; if (!string.IsNullOrEmpty(input.NameOrCode)) { whereFilter = whereFilter.And(w => w.Name.Contains(input.NameOrCode)); } return Repository.GetAll().Where(whereFilter); } } }
左侧菜单示例:
using Abp.Application.Navigation; using Abp.Authorization; using Abp.Localization; using Framework.Authorization; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace Framework.Web.App_Start { public class FrameworkVueNavigationProvider : NavigationProvider { /// <summary> /// /// </summary> /// <param name="context"></param> public override void SetNavigation(INavigationProviderContext context) { Welcome(context); Product(context); SystemNav(context); } /// <summary> ///欢迎使用 /// </summary> /// <param name="context"></param> private void Welcome(INavigationProviderContext context) { var welcome = GetMenuItemDefinition( name: "", localizableName: "Menu.Welcome", path: "/", isAuth: false, hasMeta: false, redirect: "/welcome", hasTitle: false ); var welcomeChild = GetMenuItemDefinition( name: "Welcome", localizableName: "Menu.Welcome", icon: "list", path: "welcome", isAuth: false, hasMeta: true, componet: "'@/views/welcome/index'", affix: true ); welcome.AddItem(welcomeChild); context.Manager.MainMenu.AddItem(welcome); } /// <summary> /// 产品管理 /// </summary> /// <param name="context"></param> private void Product(INavigationProviderContext context) { var product = GetMenuItemDefinition( name: "", localizableName: "Menu.Products", path: "/product",//一级路径 isAuth: false, hasMeta: false, hasTitle: false ); var productChild1 = GetMenuItemDefinition( name: "Products",//名称 localizableName: "Menu.Products",//多语言配置key icon: "list",//icon图标 path: "index",//二级路径:/product/index isAuth: false, hasMeta: true,//显示tab标签名称 componet: "'@/views/product/index'",//二级路径对应的视图 affix: false//设置tab可以关闭 ); var productChild2 = GetMenuItemDefinition( name: "ProductsEdit", localizableName: "Menu.ProductsEdit", icon: "list", path: "edit",//二级路径:/product/edit isAuth: false, hasMeta: true, componet: "'@/views/product/edit'",//二级路径对应的视图 affix: false,//设置tab可以关闭 hasTitle: true, isHidden: true ); product.AddItem(productChild1); product.AddItem(productChild2); context.Manager.MainMenu.AddItem(product); } /// <summary> /// 系统管理 /// </summary> /// <param name="context"></param> private void SystemNav(INavigationProviderContext context) { var systemRoot = GetMenuItemDefinition( name: "", localizableName: "Menu.Systems", path: "/system", icon: "example", isAuth: true, hasMeta: true, hasTitle: true, permissionDependency: new SimplePermissionDependency(PermissionNames.Pages_Systems) ); var user = GetMenuItemDefinition( name: "user", localizableName: "Menu.Users", icon: "list", path: "user", isAuth: true, hasMeta: true, componet: "'@/views/system/user'", permissionDependency: new SimplePermissionDependency(PermissionNames.Pages_Systems_Users) ); systemRoot.AddItem(user); var role = GetMenuItemDefinition( name: "role", localizableName: "Menu.Roles", icon: "list", path: "role", isAuth: true, hasMeta: true, componet: "'@/views/system/role'", permissionDependency: new SimplePermissionDependency(PermissionNames.Pages_Systems_Roles) ); systemRoot.AddItem(role); var organizationunit = GetMenuItemDefinition( name: "organizationunit", localizableName: "Menu.OrgUnits", icon: "list", path: "organizationunit", isAuth: true, hasMeta: true, componet: "'@/views/system/organizationUnit'", permissionDependency: new SimplePermissionDependency(PermissionNames.Pages_Systems_OrgUnits) ); systemRoot.AddItem(organizationunit); context.Manager.MainMenu.AddItem(systemRoot); } /// <summary> /// 返回 Vue.js 动态路由 /// </summary> /// <param name="name">路由名称</param> /// <param name="path">Url 路径</param> /// <param name="hasTitle">菜单显示名称,只是占位符,具体的值从displayName(LocalizableString)获取</param> /// <param name="icon">菜单项字体图标名称</param> /// <param name="activeMenu"> /// 当菜的 hidden 属性设置为 true 不显示在菜单栏时,可以指定菜单栏中的某个菜单项为激活状态 /// 例如:'/Home' 路由的 hidden 属性为 true,当将 activeMenu 属性设置为 '/dashboard' 时, /// 当打开 Home 页时,菜单栏中的 Dashboard 项将显示选中状态 /// </param> /// <param name="noCache">如果设置为true,则不会被 <keep-alive> 缓存(默认 false)</param> /// <param name="affix">如果设置为true,则显示在顶部横向的 tabel 导航中,当刷新新时,不会从顶部的 tabel 导航中消失(并且不可被删除)</param> /// <param name="breadcrumb">如果设置为false,则不会在breadcrumb面包屑中显示</param> /// <param name="redirect">当设置 noRedirect 的时候该路由在面包屑导航中不可被点击</param> /// <param name="componet">路由所对应的组件</param> /// <param name="isHidden">是否显示在菜单栏中</param> /// <param name="alwaysShow"> /// 是否一直显示在菜单栏中 /// 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 /// 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 /// 若你想不管路由下面的 children 声明的个数都显示你的根路由 /// 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 /// </param> /// <returns></returns> private MenuItemDefinition GetMenuItemDefinition(string name, string localizableName, string path, bool isAuth, bool hasMeta, string icon = "", string activeMenu = "", bool hasTitle = true, bool? noCache = null, bool? affix = null, bool? breadcrumb = null, string redirect = "", string componet = "'Layout'", bool isHidden = false, bool? alwaysShow = null, IPermissionDependency permissionDependency = null, object customerData = null) { return new MenuItemDefinition( name, new LocalizableString(localizableName, FrameworkConsts.LocalizationSourceName), // 系统设置 url: path, isVisible: isHidden, requiresAuthentication: isAuth, permissionDependency: permissionDependency, customData: GetVueRouter(hasMeta, icon, activeMenu, hasTitle, alwaysShow, noCache, affix, breadcrumb, redirect, componet, customerData) ); } /// <summary> /// 返回 Vue.js 动态路由 /// </summary> /// <param name="hasTitle">菜单显示名称,只是占位符,具体的值从displayName(LocalizableString)获取</param> /// <param name="icon">菜单项字体图标名称</param> /// <param name="activeMenu"> /// 当菜的 hidden 属性设置为 true 不显示在菜单栏时,可以指定菜单栏中的某个菜单项为激活状态 /// 例如:'/Home' 路由的 hidden 属性为 true,当将 activeMenu 属性设置为 '/dashboard' 时, /// 当打开 Home 页时,菜单栏中的 Dashboard 项将显示选中状态 /// </param> /// <param name="noCache">如果设置为true,则不会被 <keep-alive> 缓存(默认 false)</param> /// <param name="affix">如果设置为true,则显示在顶部横向的 tabel 导航中,当刷新新时,不会从顶部的 tabel 导航中消失(并且不可被删除)</param> /// <param name="breadcrumb">如果设置为false,则不会在breadcrumb面包屑中显示</param> /// <param name="redirect">当设置 noRedirect 的时候该路由在面包屑导航中不可被点击</param> /// <param name="componet">路由所对应的组件</param> /// <param name="isHidden">是否显示在菜单栏中</param> /// <param name="alwaysShow"> /// 是否一直显示在菜单栏中 /// 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 /// 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 /// 若你想不管路由下面的 children 声明的个数都显示你的根路由 /// 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 /// 如果没有 children 属性必须设置为 false 否则会出下下拉图示 /// </param> /// <returns></returns> private JObject GetVueRouter(bool hasMeta, string icon = "", string activeMenu = "", bool hasTitle = true, bool? alwaysShow = null, bool? noCache = null, bool? affix = null, bool? breadcrumb = null, string redirect = "", string componet = "", Object customerData = null) { JObject vueRouter = new JObject(); if (alwaysShow.HasValue) { vueRouter["alwaysShow"] = alwaysShow.Value; } if (!string.IsNullOrWhiteSpace(componet)) { vueRouter["component"] = componet; } if (hasMeta) { vueRouter["meta"] = new JObject(); if (hasTitle) { // 只作占位符,实际取值是从 MenuItemDefinition.DisplayName 中获取 vueRouter["meta"]["title"] = string.Empty; } if (!string.IsNullOrWhiteSpace(icon)) { vueRouter["meta"]["icon"] = icon; } if (noCache.HasValue) { vueRouter["meta"]["noCache"] = noCache; } if (affix.HasValue) { vueRouter["meta"]["affix"] = affix; } if (breadcrumb.HasValue) { vueRouter["meta"]["breadcrumb"] = breadcrumb; } if (!string.IsNullOrWhiteSpace(activeMenu)) { vueRouter["meta"]["activeMenu"] = activeMenu; } if (customerData != null) { vueRouter.Merge(JToken.FromObject(customerData)); } } if (!string.IsNullOrWhiteSpace(redirect)) { vueRouter["redirect"] = redirect; } return vueRouter; } } }
AbpFramework.Application》Roles
注入:private readonly ILocalizationContext _localContext;
新增接口:注意更改接口IRoleAppService
/// <summary> /// 获取角色对应的权限 /// </summary> /// <returns></returns> public Task<ListResultDto<RoleDto>> GetAllRoles() { var roleList = _roleManager.Roles.ToList(); return Task.FromResult( new ListResultDto<RoleDto> { Items = roleList.Select(MapToEntityDto).ToList() } ); } /// <summary> /// 获取角色对应的权限 /// </summary> /// <returns></returns> public async Task<ListResultDto<PermissionData>> GetRolePermissionDatas() { //获取所有权限 var permissions = PermissionManager.GetAllPermissions().Select(s => new ProcessPermission { Name = s.Name, DisplayName = s.DisplayName, IsProcess = false }).ToList(); var allPermissions = new List<PermissionData>(); //递归获取数据 for (var i = 0; i < permissions.Count; i++) { var permission = permissions[i]; if (!permission.IsProcess) { var name = permission.Name; var permissionData = new PermissionData() { Name = name, Label = permission.DisplayName.Localize(_localContext), Children = new List<PermissionData>() }; //查询该权限下是否有子权限 var pointCount = name.Split('.').Count(); var processList = permissions.Where(w => w.Name.Split('.').Count() == pointCount + 1 && w.Name.Contains(name)).ToList(); if (processList.Count > 0) { processPermission(permissions, permissionData, processList); } allPermissions.Add(permissionData); } } return new ListResultDto<PermissionData> { Items = allPermissions }; } private void processPermission(List<ProcessPermission> allPermissions, PermissionData permissionData, List<ProcessPermission> permissions) { for (var j = 0; j < permissions.Count; j++) { var childPermission = permissions[j]; var childName = childPermission.Name; var childPermissionData = new PermissionData() { Name = childName, Label = childPermission.DisplayName.Localize(_localContext), Children = new List<PermissionData>() }; permissionData.Children.Add(childPermissionData); childPermission.IsProcess = true; //查询该权限下是否有子权限 var childPointCount = childName.Split('.').Count(); var childProcessList = permissions.Where(w => w.Name.Split('.').Count() == childPointCount + 1 && w.Name.Contains(childName)).ToList(); if (childProcessList.Count > 0) { processPermission(allPermissions, childPermissionData, childProcessList); } } }
附加登录:默认是启用多租户的,所以前端调用接口登录的时候要传一个默认的租户名称,要修改登录接口AbpFramework.Web》AccountController》Login:
[HttpPost] [DisableAuditing] public async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "", string returnUrlHash = "") { CheckModelState(); //前端传一个默认的 Default 租户名称,如果没有传再自动获取。 string tenancyName = string.IsNullOrEmpty(loginModel.TenancyName) ? GetTenancyNameOrNull() : loginModel.TenancyName; var loginResult = await GetLoginResultAsync( loginModel.UsernameOrEmailAddress, loginModel.Password, tenancyName ); await SignInAsync(loginResult.User, loginResult.Identity, loginModel.RememberMe); if (string.IsNullOrWhiteSpace(returnUrl)) { returnUrl = Request.ApplicationPath; } if (!string.IsNullOrWhiteSpace(returnUrlHash)) { returnUrl = returnUrl + returnUrlHash; } return Json(new AjaxResponse { TargetUrl = returnUrl }); }
参考:https://www.cnblogs.com/defzhu/p/4843292.html
https://www.cnblogs.com/kid1412/p/6006297.html
https://www.cnblogs.com/wendj/p/6812942.html