Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了

Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架

Asp.Net Core 2.0 项目实战(3)NCMVC角色权限管理前端UI预览及下载

Asp.Net Core 2.0 项目实战(4)ADO.NET操作数据库封装、 EF Core操作及实例

Asp.Net Core 2.0 项目实战(5)Memcached踩坑,基于EnyimMemcachedCore整理MemcachedHelper帮助类。

Asp.Net Core 2.0 项目实战(6)Redis配置、封装帮助类RedisHelper及使用实例

Asp.Net Core 2.0 项目实战(7)MD5加密、AES&DES对称加解密

Asp.Net Core 2.0 项目实战(8)Core下缓存操作、序列化操作、JSON操作等Helper集合类

Asp.Net Core 2.0 项目实战(9) 日志记录,基于Nlog或Microsoft.Extensions.Logging的实现及调用实例

Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录

Asp.Net Core 2.0 项目实战(11) 基于OnActionExecuting全局过滤器,页面操作权限过滤控制到按钮级

1.权限管理

  权限管理的基本定义:百度百科

  基于《Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录》我们做过了登录认证,登录是权限的最基础的认证,没有登录就没有接下来的各种操作权限管理,以及数据权限管理(暂不探讨),这里我们把登录当作全局权限,进入系统后再根据不同的角色或者人员,固定基本功能的展示,当不同的角色要对功能操作时,就需要验证操作权限,如:查看/添加/修改/删除,也就是我们常说的控制到按钮级。下面让我们一步一步来操作实现一下,本篇提供一种权限过滤思路,欢迎讨论指正,全局过滤代码基类已经实现,相关控制页面还在紧急编码中,时间少任务重,希望大家多体谅。

内容略长:请耐心浏览。

2.约定大于配置

  约定优于配置,也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。与之对应的就是mvc下控制器和视图的关系。

  本质是说,开发人员仅需规定应用中不符约定的部分。例如,如果模型中有个名为Sale的类,那么数据库中对应的表就会默认命名为sales。只有在偏离这一约定时,例如将该表命名为”products_sold”,才需写有关这个名字的配置。

  为了方便项目快速构建,数据库我们这里先使用dtcms 5.0的数据库相关表navigation。EF Core生成Model备用。

a)  首先约定后台Controller和Action命名约定,以及属性Attribute类定义

  ##菜单约定##

  1.nav_name尽量使用controller

  2.所有英文小写

  3.最后一级url不能为空

  ##方法定义约定##

  1.属性全nav_name,action_type

  2.属性只有nav_name,判断Action和参数是否为空

  3.属性只有action_type,控制器名做nav_name

  4.根据控制器+Action判断

  5.不是标准方法必须加属性nav_name

  6.控制器标准,保存Action方法不标准,需要传标准参数

b)   定义操作枚举Enum

using System;
using System.Collections.Generic;
using System.Text; namespace NC.Common
{
public class JHEnums
{ /// <summary>
/// 统一管理操作枚举
/// </summary>
public enum ActionEnum
{
/// <summary>
/// 所有
/// </summary>
All,
/// <summary>
/// 显示
/// </summary>
Show,
/// <summary>
/// 查看
/// </summary>
View,
/// <summary>
/// 添加
/// </summary>
Add,
/// <summary>
/// 修改
/// </summary>
Edit,
/// <summary>
/// 删除
/// </summary>
Delete,
/// <summary>
/// 审核
/// </summary>
Audit,
/// <summary>
/// 回复
/// </summary>
Reply,
/// <summary>
/// 确认
/// </summary>
Confirm,
/// <summary>
/// 取消
/// </summary>
Cancel,
/// <summary>
/// 作废
/// </summary>
Invalid,
/// <summary>
/// 生成
/// </summary>
Build,
/// <summary>
/// 安装
/// </summary>
Instal,
/// <summary>
/// 卸载
/// </summary>
UnLoad,
/// <summary>
/// 登录
/// </summary>
Login,
/// <summary>
/// 备份
/// </summary>
Back,
/// <summary>
/// 还原
/// </summary>
Restore,
/// <summary>
/// 替换
/// </summary>
Replace,
/// <summary>
/// 复制
/// </summary>
Copy
}
}

JHEnums

c)  获取操作权限

#region 操作权限菜单
/// <summary>
/// 获取操作权限
/// </summary>
/// <returns>Dictionary</returns>
public static Dictionary<string, string> ActionType()
{
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("Show", "显示");
dic.Add("View", "查看");
dic.Add("Add", "添加");
dic.Add("Edit", "修改");
dic.Add("Delete", "删除");
dic.Add("Audit", "审核");
dic.Add("Reply", "回复");
dic.Add("Confirm", "确认");
dic.Add("Cancel", "取消");
dic.Add("Invalid", "作废");
dic.Add("Build", "生成");
dic.Add("Instal", "安装");
dic.Add("Unload", "卸载");
dic.Add("Back", "备份");
dic.Add("Restore", "还原");
dic.Add("Replace", "替换");
return dic;
}
#endregion

Utils.ActionType()

d)  Action属性类定义

using Microsoft.AspNetCore.Mvc.Filters;
using System; namespace NC.Lib
{
/// <summary>
/// nav_name
/// </summary>
public class NavAttr : Attribute, IFilterMetadata
{
public NavAttr() { }
public NavAttr(string navName, string actionType)
{
this.NavName = navName;
this.ActionType = actionType;
}
public string NavName { set; get; }//菜单名称
public string ActionType { set; get; } //操作类型
}
}

3. 全局过滤实现

3.1 首先定义一个基Controller

  定义好基AdminBase控制器后,所有的后台域Controller都继承此类

3.2 首先验证用户是否登录

Session相关参考3.5 Session操作

 

 /// <summary>
/// 判断管理员是否已经登录
/// </summary>
public bool IsAdminLogin()
{
var bSession = HttpContext.Session.Get(AdminAuthorizeAttribute.AdminAuthenticationScheme);
if (bSession == null)
{
return false;
}
siteAdminInfo = ByteConvertHelper.Bytes2Object<JhManager>(bSession);
//如果Session为Null
if (siteAdminInfo != null)
{
return true;
}
else
{
//检查Cookies
var cookieAdmin = HttpContext.AuthenticateAsync(AdminAuthorizeAttribute.AdminAuthenticationScheme);
cookieAdmin.Wait();
var adminname = cookieAdmin.Result.Principal.Claims.FirstOrDefault(x => x.Type == "AdminName")?.Value;
var adminpwd = cookieAdmin.Result.Principal.Claims.FirstOrDefault(x => x.Type == "AdminPwd")?.Value; if (adminname != "" && adminpwd != "")
{
JhManager model = dblEf.JhManager.Where(m => m.UserName == adminname && m.Password == adminpwd).FirstOrDefault();
if (model != null)
{
HttpContext.Session.Set(AdminAuthorizeAttribute.AdminAuthenticationScheme, ByteConvertHelper.Object2Bytes(model));//存储session
bSession = HttpContext.Session.Get(AdminAuthorizeAttribute.AdminAuthenticationScheme);
siteAdminInfo = ByteConvertHelper.Bytes2Object<JhManager>(bSession);
return true;
}
}
}
return false;
}

3.3 OnActionExecuting重载方法实现过滤

  首先验证登录,然后判断需要过滤的area,判断是否有跳过属性(SkipAdminAuthorizeAttribute,做登录的时候定义过),最后判断菜单和按钮的权限。

  这里需要注意的是,OnActionExecuting和AdminAuthorizeAttribute. OnAuthorization的执行顺序,有的网友博客看到的是OnActionExcuting先执行,我这里测试的是先验证属性OnAuthorization,再执行OnActionExecuting;可能附加条件不同,这里不做过多探讨,调试的时候大家可试试。

 

/// <summary>
/// 创建过滤器:***全局过滤器*** 过滤除登录登出等操作权限验证
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
//1.验证是否登录
//2.验证菜单权限
//3.验证按钮权限
//在action执行之前 //判断是否加有SkipAdmin标签
var skipAuthorize = filterContext.ActionDescriptor.FilterDescriptors.Where(a => a.Filter is SkipAdminAuthorizeAttribute).Any();
if (!skipAuthorize)
{
//是否系统管理文件夹里文件,Areas》ad_min
var isPermission = false;
//获取controller和action
var route = filterContext.RouteData.Values; string strArea = route["area"].ToString();//获取区域的名字,ad_min区域下的都需要权限验证
if (strArea != null && strArea.Equals("ad_min"))
{
isPermission = true;
}
//需要验证权限
if (isPermission) {
var currController = route["controller"].ToString();
var curraction = route["action"].ToString();
var exceptCtr = UtilConf.Configuration["Site:exceptCtr"].Replace(",", ",");//防止中文逗号
var exceptAction = UtilConf.Configuration["Site:exceptAction"].Replace(",", ",");//防止中文逗号
//判断是否有例外控制器或Action校验是否例外,跳过验证
if (!exceptCtr.Contains(currController.ToLower()) && !exceptAction.Contains(curraction.ToLower()))
{
//验证是否登录
if (!IsAdminLogin())
{
string msg = string.Format("未登录或登录超时,请重新登录!");
filterContext.Result = new RedirectResult("~/ad_min/login?msg=" + WebUtility.UrlEncode(msg));
return;
}
//验证菜单权限
//验证按钮权限
//自定义方法属性
try
{
//获取属性
NavAttr actionAttr = filterContext.ActionDescriptor.FilterDescriptors.Where(a => a.Filter is NavAttr).Select(a => a.Filter).FirstOrDefault() as NavAttr;
string strNavName = string.Empty;
string strActionType = string.Empty;
if (actionAttr == null)
{
actionAttr = filterContext.ActionDescriptor.FilterDescriptors.GetType().GetCustomAttributes<NavAttr>().FirstOrDefault() as NavAttr;
}
if (actionAttr != null)
{
strNavName = actionAttr.NavName;
strActionType = actionAttr.ActionType;
}
//获取参数,由于action在mvc中属于关键词,所以使用act当作操作方式参数
string paramAction = "";
//paramAction = Request.Query["action"].ToString();
if (string.IsNullOrEmpty(paramAction))
{
if (route["act"] != null)
{
paramAction = route["act"].ToString();
}
}
if (siteAdminInfo.RoleType != )//超管拥有所有权限
{
if (!ChkPermission(siteAdminInfo.RoleId, currController, curraction, strNavName, strActionType, paramAction))
{
TempData["Permission"] = "您没有管理该页面的权限,请联系管理员!";
filterContext.Result = new RedirectResult("~/ad_min/Home/Index");
return;
//返回固定错误json
}
else
{
TempData["Permission"] = null;
}
}
}
catch (System.Exception ex)
{
throw ex;
}
}
}
}
}

  页面权限验证,首先获取到页面的Controller和Action以及Action上面是否包含相关属性NavAttr,校验数据库中是否包含对此属性的定义。

/// <summary>
/// 判断页面
/// </summary>
/// <param name="role_id">角色id</param>
/// <param name="currController">当前控制器</param>
/// <param name="currAction">当前</param>
/// <param name="navName">方法上的属性</param>
/// <param name="actionType">操作类型</param>
/// <param name="paramAction">当为操作方法是传递的参数</param>
/// <returns>没有权限返回false</returns>
public bool ChkPermission(int? role_id, string currController, string currAction, string navName, string actionType, string paramAction)
{
//1.未配置页面,在方法上加属性/ad_min/Settings/SysConfigSave
//2.控制器+Action /admin/sys_config/index,/admin/sys_config/add,/admin/sys_config/edit
//3.先判断已配置页面/admin/settings/sys_config
bool result = true;
var url = HttpContext.Request.Path.Value;
if (url.Contains("/ad_min/home/index"))//后台首页不验证
{
return result;
}
DataTable dt = chkPermission(role_id);
var action_type = actionType;
//属性不为空
if (!string.IsNullOrEmpty(navName) && !string.IsNullOrEmpty(actionType))//属性全
{
DataRow[] dr = dt.Select("nav_name='" + navName + "' and action_type='" + action_type + "'");
result = (dr.Count() > );
}
else if (!string.IsNullOrEmpty(navName) && string.IsNullOrEmpty(actionType))//属性只有nav_name
{
action_type = getActionType(currAction, paramAction);
DataRow[] dr = dt.Select("nav_name='" + navName + "' and action_type='" + action_type + "'");
result = (dr.Count() > );
}
else if (string.IsNullOrEmpty(currController) && !string.IsNullOrEmpty(actionType))//控制器名:nav_name,属性只有action_type
{
DataRow[] dr = dt.Select("nav_name='" + currController + "' and action_type='" + action_type + "'");
result = (dr.Count() > );
}
else
{
//约定大于配置
//控制器名:nav_name
//Action:action_type
if (!string.IsNullOrEmpty(currController) && !string.IsNullOrEmpty(currAction))
{
//控制器+action
if (currAction.ToLower() == "index")//首页为展示
{
currAction = "View";
}
DataRow[] dr = dt.Select("nav_name='" + currController + "' and (action_type='" + currAction + "')");
result = (dr.Count() > ); }
//属性全空,控制器+Action验证不通过,参数不空
if (!result && !string.IsNullOrEmpty(currController) && !string.IsNullOrEmpty(paramAction))//(控制器)+参数判断
{
//参数可为Edit,Add,Del...
DataRow[] dr = dt.Select("nav_name='" + currController + "' and action_type='" + paramAction + "'");
result = (dr.Count() > );
}
if (!result)//控制器+Action验证未通过
{
//配置页面处理
DataTable dtNav = GetNavCacheList("link_url='" + url + "'");//根据菜单URL,从缓存中检索调用ID
if (dtNav.Rows.Count > )
{
DataRow drNav = dtNav.Rows[];
string nav_name = drNav["name"].ToString();//nav_name
action_type = getActionType(currAction, paramAction); DataRow[] dr = dt.Select("nav_name='" + nav_name + "' and action_type='" + action_type + "'");
result = (dr.Count() > );
}
}
} return result;
}
/// <summary>
/// 判断是否有权限
/// </summary>
private DataTable chkPermission(int? role_id)
{
DataTable dt = CacheHelper.Get("permisson" + role_id) as DataTable;
if (dt == null)
{
string strSql = "SELECT mrv.nav_name,mrv.action_type FROM manager_role mr LEFT JOIN manager_role_value mrv ON mr.id=mrv.role_id WHERE mr.id=@role_id";
DbParameters p = new DbParameters();
p.Add("@role_id", role_id);
dt = Dbl.JHCMS.CreateSqlDataTable(strSql, p);
CacheHelper.Set("permisson" + role_id, dt);
}
return dt;
}
/// <summary>
/// 1.验证action是否标准约定
/// 2.根据action=''参数获取操作类型
/// </summary>
private string getActionType(string currAction, string paramAction)
{
if (currAction.ToLower().Contains("index") || currAction.ToLower().Contains("list"))//首先判断是否首页/列表等展示
{
return "View";
}
if (currAction.ToLower().Contains("save"))//如果包含保存save关键字,默认返回add
{
return string.IsNullOrEmpty(paramAction) ? "Add" : paramAction;
}
else if (currAction.ToLower().Contains("edit") || currAction.ToLower().Contains("update"))
{
return string.IsNullOrEmpty(paramAction) ? "Edit" : paramAction;
}
else if (currAction.ToLower().Contains("del"))
{
return string.IsNullOrEmpty(paramAction) ? "Delete" : paramAction;
}
//判断Action
if (!string.IsNullOrEmpty(currAction))
{
if (Utils.ActionType().ContainsKey(currAction))//首字母要大写,约定
return currAction;
}
return string.IsNullOrEmpty(paramAction) ? "View" : paramAction;
}

3.4 Controller中的约定

  1.NavAttr属性全部定义

/// <summary>
/// 更新字典排序
/// </summary>
[NavAttr(NavName = "sys_navigation", ActionType = "Edit")]
public JsonResult UpdateNav(string id, string nav)
{}

  2.NavAttr属性之定义NavName(对应数据库中的name)

[NavAttr(NavName = "sys_navigation"]
public JsonResult UpdateNav_Edit(string id, string nav)
{}

  3.未定义Action属性,必须传递一个参数以确定操作类型

//(控制器)+参数判断

    public class sys_navigationController : AdminBase
{
public JsonResult UpdateNav_Edit(string id, string nav)
{}
}

3.5 Session相关操作

  Session使用需要先在startup.cs中进行配置注入,找到方法ConfigureServices注入Session

  Configure中启用

  在控制器中的操作,存储:

JhManager bUser = getUserInfoByNameAndPwd(AdminName, adminpwd, true);
HttpContext.Session.Set(AdminAuthorizeAttribute.AdminAuthenticationScheme, ByteConvertHelper.Object2Bytes(bUser));//存储session

  读取:

var bSession =
HttpContext.Session.Get(AdminAuthorizeAttribute.AdminAuthenticationScheme);
if (bSession == null)
{
return false;
}
bUser= ByteConvertHelper.Bytes2Object<JhManager>(bSession);

  ByteConvertHelper是byte转换帮助类

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text; namespace NC.Common
{
/// <summary>
/// byte转换操作类,主要用于Session存储
/// </summary>
public class ByteConvertHelper
{
/// <summary>
/// 将对象转换为byte数组
/// </summary>
/// <param name="obj">被转换对象</param>
/// <returns>转换后byte数组</returns>
public static byte[] Object2Bytes(object obj)
{
byte[] serializedResult = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj));
return serializedResult;
} /// <summary>
/// 将byte数组转换成对象
/// </summary>
/// <param name="buff">被转换byte数组</param>
/// <returns>转换完成后的对象</returns>
public static object Bytes2Object(byte[] buff)
{
return JsonConvert.DeserializeObject<object>(Encoding.UTF8.GetString(buff));
} /// <summary>
/// 将byte数组转换成对象
/// </summary>
/// <param name="buff">被转换byte数组</param>
/// <returns>转换完成后的对象</returns>
public static T Bytes2Object<T>(byte[] buff)
{
return JsonConvert.DeserializeObject<T>(Encoding.UTF8.GetString(buff));
}
}
}

4.总结

  实战项目还在一点点开发中,碰到很多坑点,时间也很有限。工作越来越忙,总是抽时间兼顾学习联系,很累。NET技术更新换代很快,公司里还在沿用比较老的技术,可能大多数公司都是这样,程序不得不学新技术,企业不得不用成熟的技术。

  虽然不知道会做到哪一步,碰到的问题积累的点,在这里先记录下来,备查。项目如果成型或能够运行起来看到效果,到时候开源出来。有时候毕竟代码片段或者写博的时候有些地方不容易连贯起来,现在让我们先一起学习吧。

Asp.Net Core 2.0 项目实战(11) 基于OnActionExecuting全局过滤器,页面操作权限过滤控制到按钮级的更多相关文章

  1. net core体系-web应用程序-4asp.net core2.0 项目实战(1)-13基于OnActionExecuting全局过滤器,页面操作权限过滤控制到按钮级

    1.权限管理 权限管理的基本定义:百度百科. 基于<Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员.后台管理员同时登录>我们做过了登录认证, ...

  2. Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录

    1.登录的实现 登录功能实现起来有哪些常用的方式,大家首先想到的肯定是cookie或session或cookie+session,当然还有其他模式,今天主要探讨一下在Asp.net core 2.0下 ...

  3. Asp.Net Core 2.0 项目实战(9) 日志记录,基于Nlog或Microsoft.Extensions.Logging的实现及调用实例

    本文目录 1. Net下日志记录 2. NLog的使用     2.1 添加nuget引用NLog.Web.AspNetCore     2.2 配置文件设置     2.3 依赖配置及调用     ...

  4. Asp.Net Core 2.0 项目实战(8)Core下缓存操作、序列化操作、JSON操作等Helper集合类

    本文目录 1.  前沿 2.CacheHelper基于Microsoft.Extensions.Caching.Memory封装 3.XmlHelper快速操作xml文档 4.Serializatio ...

  5. Asp.Net Core 2.0 项目实战(7)MD5加密、AES&DES对称加解密

    本文目录 1. 摘要 2. MD5加密封装 3. AES的加密.解密 4. DES加密/解密 5. 总结 1.  摘要 C#中常用的一些加密和解密方案,如:md5加密.RSA加密与解密和DES加密等, ...

  6. Asp.Net Core 2.0 项目实战(6)Redis配置、封装帮助类RedisHelper及使用实例

    本文目录 1. 摘要 2. Redis配置 3. RedisHelper 4.使用实例 5. 总结 1.  摘要 由于內存存取速度远高于磁盘读取的特性,为了程序效率提高性能,通常会把常用的不常变动的数 ...

  7. Asp.Net Core 2.0 项目实战(4)ADO.NET操作数据库封装、 EF Core操作及实例

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

  8. Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

  9. Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

随机推荐

  1. 03 Spring的父子容器

    1.概念理解和知识铺垫 在Spring整体框架的核心概念中,容器是核心思想,就是用来管理Bean的整个生命周期的,而在一个项目中,容器不一定只有一个,Spring中可以包括多个容器,而且容器有上下层关 ...

  2. easywechat--在thinkPHP5中的使用

    1. 安装 1.1 v-4.0 版本要求 PHP版本在7.0以上 1.2 在项目目录下运行以下命令 若未安装composer,则先安装composer -> http://docs.phpcom ...

  3. mysql varchar vs oracle varchar2

    1.错误提示: mysql的Data truncation: Data too long for column 'path' at row 1 错误原因: 1.字段过长而导致出错的, 2. 可能是因为 ...

  4. R︱shiny实现交互式界面布置与搭建(案例讲解+学习笔记)

    要学的东西太多,无笔记不能学~~ 欢迎关注公众号,一起分享学习笔记,记录每一颗"贝壳"~ --------------------------- 看了看往期的博客,这个话题竟然是第 ...

  5. PHP openssl函数库

    php openssl 函数库中.提供了大量的函数.但是我们一般用的最多的,就是 openssl_encrypt string openssl_encrypt ( string $data , str ...

  6. R语言︱文本(字符串)处理与正则表达式

    处理文本是每一种计算机语言都应该具备的功能,但不是每一种语言都侧重于处理文本.R语言是统计的语言,处理文本不是它的强项,perl语言这方面的功能比R不知要强多少倍.幸运的是R语言的可扩展能力很强,DN ...

  7. Java中的“&”和“&&”的区别

    Java中的"&"和"&&"的区别 1."&"是位运算符,"&&"是逻辑 ...

  8. pat1121-1131

    1121 #include<cmath> #include<map> #include<iostream> #include<cstring> #inc ...

  9. Halcon的应用程序 打开后 弹出没有帮助文件错误提示

    问题: Halcon的应用程序 打开后 弹出没有帮助文件错误提示 解决方法: 建立(C:\ProgramFiles\MVTec\halcon)目录,将halcon安装目录下的help文件夹复制过来即可 ...

  10. ubuntu安装latex

    1 终端中输入"sudo apt-get install texlive-full",输入root密码. 若不想安装所有文件,可以选择"sudo apt-get inst ...