从零开始编写自己的C#框架(16)——Web层后端父类

时间:2023-03-09 15:50:03
从零开始编写自己的C#框架(16)——Web层后端父类

  本章节讲述的各个类是后端系统的核心之一,涉及到系统安全验证、操作日志记录、页面与按键权限控制、后端页面功能封装等内容,希望学习本系列的朋友认真查看新增的类与函数,这对以后使用本框架进行开发时非常重要。

  1、父类(基类)的实现

  在开发后端首页与相关功能页面前,我们首先要实现的是所有页面的基类(父类),将常用的功能都预先实现出来,而后面的相关UI类则直接继承它,这样就能简单的自动实现了相关页面功能,不用再每个页面去编写某些按键功能或其他一些功能,如果有特殊的需要,再重写对应的功能类就可以了,对于常用功能,由于之前的逻辑层与数据层已使用模板生成好了,所以直接调用,这样的话比如实现一个列表页面的一些功能(如下图),只需要简单的在页面控件使用指定名称,那么一些实现代码就不用再编写了,这些控件自动拥有对应的功能,比如刷新、自动排序、保存排序(直接修改下图中排序列的输入框后点击保存排序就可以了,这个功能不用编写任何一个代码,只需要将按键放到下图位置,然后使用指定名称就可以了)等功能。这样操作将使我们后面的开发工作更加轻松。而对于列表的话,也只需要调用逻辑层函数直接绑定(bll.BindGrid(this, Grid1, Grid1.PageIndex + 1, Grid1.PageSize, InquiryCondition(), _order);)就可以实现列表、分页、翻页、排序等功能。当然列表点击审核的√与×就会同步更改数据库对应记录的字段与图标,也只需要在列表控件对应函数复制进简单的几行代码就可以实现,这些会在后面相应章节中具体讲述。

  从零开始编写自己的C#框架(16)——Web层后端父类

  先上父类与接口代码

 using System;
using System.Collections.Generic;
using System.Web.UI;
using DotNet.Utilities;
using FineUI;
using Solution.Logic.Managers;
using Solution.Logic.Managers.Application; namespace Solution.Web.Managers.WebManage.Application
{
/// <summary>
/// Web层页面父类
/// 本基类封装了各种常用函数,c减少重复代码的编写
/// </summary>
public abstract class PageBase : System.Web.UI.Page, IPageBase
{
#region 定义对象
//逻辑层接口对象
protected ILogicBase bll = null;
//定义列表对象
private FineUI.Grid grid = null;
//页面排序容器
List<string> sortList = null;
#endregion #region 初始化函数
protected override void OnInit(EventArgs e)
{
base.OnInit(e); //检测用户是否超时退出
OnlineUsersBll.GetInstence().IsTimeOut(); if (!IsPostBack)
{
//检测当前页面是否有访问权限
MenuInfoBll.GetInstence().CheckPagePower(this); #region 设置页面按键权限
//定义按键控件
Control btnControl = null;
try
{
//找到页面放置按键控件的位置
ControlCollection controls = MenuInfoBll.GetInstence().GetToolBarControls(this.Controls);
//逐个读取出来
for (int i = ; i < controls.Count; i++)
{
//取出控件
btnControl = controls[i];
//判断是否除了刷新、查询和关闭以外的按键
if (btnControl.ID != "ButtonRefresh" && btnControl.ID != "ButtonSearch" && btnControl.ID != "ButtonClose" && btnControl.ID != "btnReset")
{
//是的话检查该按键当前用户是否有控件权限,没有的话则禁用该按键
((FineUI.Button)btnControl).Enabled = MenuInfoBll.GetInstence().CheckControlPower(this, btnControl.ID);
}
}
}
catch (Exception) { }
#endregion //记录用户当前所在的页面位置
CommonBll.UserRecord(this);
} //运行UI页面初始化函数,子类继承后需要重写本函数,以提供给本初始化函数调用
Init();
}
#endregion #region 接口函数,用于UI页面初始化,给逻辑层对象、列表等对象赋值 /// <summary>
/// 接口函数,用于UI页面初始化,给逻辑层对象、列表等对象赋值
/// </summary>
public abstract void Init(); #endregion #region 页面各种按键事件 /// <summary>
/// 刷新按钮事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonRefresh_Click(object sender, EventArgs e)
{
FineUI.PageContext.RegisterStartupScript("window.location.reload()");
} /// <summary>
/// 关闭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonClose_Click(object sender, EventArgs e)
{
PageContext.RegisterStartupScript(ActiveWindow.GetHideRefreshReference()); }
/// <summary>
/// 查询
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonSearch_Click(object sender, EventArgs e)
{
LoadData();
} /// <summary>
/// 新增
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonAdd_Click(object sender, EventArgs e)
{
Add();
} /// <summary>
/// 编辑
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonEdit_Click(object sender, EventArgs e)
{
Edit();
} /// <summary>
/// 删除
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonDelete_Click(object sender, EventArgs e)
{
//执行删除操作,返回删除结果
string result = Delete(); if (string.IsNullOrEmpty(result))
return;
//弹出提示框
FineUI.Alert.ShowInParent(result, FineUI.MessageBoxIcon.Information); //重新加载页面表格
LoadData();
} /// <summary>
/// 保存数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonSave_Click(object sender, EventArgs e)
{
//执行保存操作,返回保存结果
string result = Save(); if (string.IsNullOrEmpty(result))
{
PageContext.RegisterStartupScript(ActiveWindow.GetHidePostBackReference());
FineUI.Alert.ShowInParent("保存成功", FineUI.MessageBoxIcon.Information);
}
else
{
//by july,部分页面保存后,必须刷新原页面的,把返回的值用 "{url}" + 跳转地址的方式传过来
if (StringHelper.Left(result, ) == "{url}")
{
string url = result.Trim().Substring();
FineUI.Alert.ShowInParent("保存成功", "", FineUI.MessageBoxIcon.Information, "self.location='" + url + "'");
}
else
{
FineUI.Alert.ShowInParent(result, FineUI.MessageBoxIcon.Information);
}
}
} /// <summary>保存排序</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonSaveSort_Click(object sender, EventArgs e)
{
SaveSort();
} /// <summary>自动排序</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ButtonSaveAutoSort_Click(object sender, EventArgs e)
{
//默认使用多级分类
SaveAutoSort();
} /// <summary>
/// 排序
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Grid1_Sort(object sender, FineUI.GridSortEventArgs e)
{
//生成排序关键字
Sort(e);
//刷新列表
LoadData();
} /// <summary>
/// 分页
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Grid1_PageIndexChange(object sender, FineUI.GridPageEventArgs e)
{
if (grid != null)
{
grid.PageIndex = e.NewPageIndex; LoadData();
}
} /// <summary>
/// 关闭子窗口事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void Window1_Close(object sender, WindowCloseEventArgs e)
{
LoadData();
} /// <summary>
/// 关闭子窗口事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void Window2_Close(object sender, WindowCloseEventArgs e)
{
LoadData();
} /// <summary>
/// 关闭子窗口事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void Window3_Close(object sender, WindowCloseEventArgs e)
{
LoadData();
}
#endregion #region 虚函数,主要给页面各种按键事件调用,子类需要使用到相关功能时必须将它实现 /// <summary>
/// 加载事件
/// </summary>
public virtual void LoadData() { } /// <summary>
/// 添加记录
/// </summary>
public virtual void Add() { } /// <summary>
/// 修改记录
/// </summary>
public virtual void Edit() { } /// <summary>
/// 删除记录
/// </summary>
/// <returns>返回删除结果</returns>
public virtual string Delete()
{
return null;
} /// <summary>
/// 保存数据
/// </summary>
/// <returns>返回保存结果</returns>
public virtual string Save()
{
return "";
} /// <summary>
/// 保存排序
/// </summary>
/// <returns>返回保存结果</returns>
public virtual void SaveSort()
{
//保存排序
if (grid != null && bll != null)
{
//更新排序
if (bll.UpdateSort(this, grid, "tbxOrderID"))
{
//重新加载列表
LoadData(); Alert.ShowInParent("操作成功", "保存排序成功", "window.location.reload();");
}
else
{
Alert.ShowInParent("操作成失败", "保存排序失败", "window.location.reload();");
}
}
} /// <summary>
/// 保存自动排序
/// </summary>
public virtual void SaveAutoSort()
{
if (bll == null)
{
Alert.ShowInParent("保存失败", "逻辑层对象为null,请联系开发人员给当前页面的逻辑层对象赋值");
return;
} if (bll.UpdateAutoSort(this, "", true))
{
Alert.ShowInParent("保存成功", "保存自动排序成功", "window.location.reload();");
}
else
{
Alert.ShowInParent("保存失败", "保存自动排序失败", "window.location.reload();");
}
} /// <summary>
/// 生成排序关键字
/// </summary>
/// <param name="e"></param>
public virtual void Sort(FineUI.GridSortEventArgs e)
{
//处理排序
sortList = null;
sortList = new List<string>();
//排序列字段名称
string sortName = ""; if (e.SortField.Length > )
{
//判断是升序还是降序
if (e.SortDirection != null && e.SortDirection.ToUpper() == "DESC")
{
sortList.Add(e.SortField + " desc");
}
else
{
sortList.Add(e.SortField + " asc");
}
sortName = e.SortField;
}
else
{
//使用默认排序——主键列降序排序
sortList.Add("Id desc");
sortName = "Id";
} //利用反射的方式给页面控件赋值
//查找指定名称控件
var control = Page.FindControl("SortColumn");
if (control != null)
{
//判断是否是TextBox类型
var type = control.GetType();
if (type.FullName == "System.Web.UI.WebControls.TextBox")
{
//存储排序列字段名称
((TextBox)control).Text = sortName;
}
}
} #endregion }
}
 namespace Solution.Web.Managers.WebManage.Application
{
/// <summary>
/// UI页面接口类——主要用于列表(Grid)页
/// </summary>
public interface IPageBase
{
#region 用于UI页面初始化,给逻辑层对象、列表等对象赋值,主要是列表(Grid)页面使用 void Init(); #endregion
}
}

  有朋友可能会有疑问,为什么本类要用abstract修饰成抽象类,而实现类中只有接口Init()函数是抽象函数,这样设置主要是强制要求Init()函数在子类中必须实现,因为在开发过程中,不这样强制的话,一些页面开发时很容易忘记去给父类的相关对象赋值,那么父类中的一些功能在调用时就无法正常运行。

  代码中的OnInit()函数,在页面启动的时候会调用到,主要用于检查用户登陆情况,用户是否有当前页面的访问权限和页面按键的使用权限,记录用户访问记录,以及运行UI页面初始化函数,方便父类相关函数功能的调用(比如保存排序、分页等功能)。

  另外,预先定义好了页面各种常用按键事件,只要在页面里放置这些按键,就可以自动调用这些事件功能,部分按键功能已实现的就不用再编写代码,未实现的则直接重写对应的虚函数,就可以达到想要的效果。

  而虚函数,大多都没有实现其功能的代码,可能有朋友会说,为什么不用接口呢?呃......在这里再重新说明一下,定义成接口的话就必须要去实现,而对于多数页面来说,并不一定要用到这个功能,那么这些页面代码看起来就很罗嗦,充斥大量无用代码,可读性就会非常的差,而用虚方法只是放在那里就可以了,需要使用时才去重写,这样操作会比较灵活,页面代码看起来也非常整洁干净。

  由于下面不少功能都是重新沟思后编写的,所以可能会存在一些不合理的地方,且未经过运行测试,以后会根据情况进行增删。

  从零开始编写自己的C#框架(16)——Web层后端父类

  2、逻辑层添加了接口,且抽象类实现这个接口并增加对应的抽象函数

  为了方便上面父类的调用,减少重复代码的编写,在逻辑层增加了接口类ILogicBase,而原来的逻辑层抽象类LogicBase(所有模板都必须继承的类)也实现了该接口

 using System.Collections.Generic;
using System.Web.UI;
using Solution.DataAccess.DbHelper; namespace Solution.Logic.Managers.Application
{
public interface ILogicBase
{
#region 绑定表格 void BindGrid(FineUI.Grid grid, int pageIndex = , int pageSize = ,
List<ConditionFun.SqlqueryCondition> wheres = null, List<string> orders = null); void BindGrid(FineUI.Grid grid, int parentValue,
List<ConditionFun.SqlqueryCondition> wheres = null, List<string> orders = null, string parentId = "ParentId"); void BindGrid(FineUI.Grid grid, int parentValue, List<string> orders = null,
string parentId = "ParentId"); #endregion #region 删除记录 void Delete(Page page, int id); void Delete(Page page, int[] id); #endregion #region 保存排序 bool UpdateSort(Page page, FineUI.Grid grid1, string tbxOrderId, string orderIdName = "OrderId"); #endregion #region 自动排序 bool UpdateAutoSort(Page page, string strWhere = "", bool isExistsMoreLv = false, int pid = ,
string fieldName = "OrderId", string fieldParentId = "ParentId"); #endregion
}
}
 using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Web.UI;
using FineUI;
using Solution.DataAccess.DbHelper;
using Solution.Logic.Managers.Application; namespace Solution.Logic.Managers
{
/// <summary>
/// 逻辑层抽象类
/// </summary>
public abstract class LogicBase : ILogicBase
{
#region 清空缓存
/// <summary>清空缓存</summary>
public virtual void DelCache()
{ }
#endregion #region 全表缓存加载条件
/// <summary>
/// 全表缓存加载条件——对于有些表并不用所有记录都加载到缓存当中,这个时候就可以重写本函数来实现每次加载时只加载指定的记录到缓存中
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual Expression<Func<T, bool>> GetExpression<T>()
{
return null;
}
#endregion #region 绑定表格
public abstract void BindGrid(FineUI.Grid grid, int pageIndex = , int pageSize = , List<ConditionFun.SqlqueryCondition> wheres = null, List<string> orders = null); public abstract void BindGrid(FineUI.Grid grid, int parentValue, List<ConditionFun.SqlqueryCondition> wheres = null, List<string> orders = null,
string parentId = "ParentId"); public abstract void BindGrid(FineUI.Grid grid, int parentValue, List<string> orders = null, string parentId = "ParentId");
#endregion #region 删除记录
public abstract void Delete(Page page, int id);
public abstract void Delete(Page page, int[] id);
#endregion #region 保存排序
public abstract bool UpdateSort(Page page, Grid grid1, string tbxOrderId, string orderIdName = "OrderId");
#endregion #region 自动排序
public abstract bool UpdateAutoSort(Page page, string strWhere = "", bool isExistsMoreLv = false, int pid = ,
string fieldName = "OrderId", string fieldParentId = "ParentId");
#endregion
}
}

  原来的模板根据需要也对应做出了相应调整

  我们在第1点时不是强制要求实现Init()函数吗,这里就是主要为PageBase类(父类)的逻辑层接口赋值用的。

  比如我们要实现一个信息列表的功能,由于我们的逻辑层都继承了ILogicBase类,所以我们只要在Init()函数中给PageBase类的protected ILogicBase bll = null;这个定义重新赋值就可以了,那么在执行下面代码时就会通过这个接口的调用来执行对应类的功能

  在Init()初始化函数中,给逻辑层对象赋值例子

 using System;
using Solution.Logic.Managers;
using Solution.Web.Managers.WebManage.Application; namespace Solution.Web.Managers
{
public partial class WebForm1 : PageBase
{
protected void Page_Load(object sender, EventArgs e)
{ } #region 接口函数,用于UI页面初始化,给逻辑层对象、列表等对象赋值
/// <summary>
/// 接口函数,用于UI页面初始化,给逻辑层对象、列表等对象赋值
/// </summary>
public override void Init()
{
//给逻辑层对象赋值
bll = InformationBll.GetInstence();
//给表格赋值
grid = Grid1;
}
#endregion
}
}

  PageBase类执行逻辑层程序代码——实际上这些代码可以再次进行封装成一个公共函数,不过都是用模板生成的,有变动不用一个个改就懒得再重构了

         /// <summary>
/// 保存排序
/// </summary>
/// <returns>返回保存结果</returns>
public virtual void SaveSort()
{
//保存排序
if (grid != null && bll != null)
{
//更新排序
if (bll.UpdateSort(this, grid, "tbxOrderID"))
{
//重新加载列表
LoadData(); Alert.ShowInParent("操作成功", "保存排序成功", "window.location.reload();");
}
else
{
Alert.ShowInParent("操作成失败", "保存排序失败", "window.location.reload();");
}
}
} /// <summary>
/// 保存自动排序
/// </summary>
public virtual void SaveAutoSort()
{
if (bll == null)
{
Alert.ShowInParent("保存失败", "逻辑层对象为null,请联系开发人员给当前页面的逻辑层对象赋值");
return;
} if (bll.UpdateAutoSort(this, "", true))
{
Alert.ShowInParent("保存成功", "保存自动排序成功", "window.location.reload();");
}
else
{
Alert.ShowInParent("保存失败", "保存自动排序失败", "window.location.reload();");
}
}

 

  3、MenuInfoBll逻辑类

  它是后端所有页面权限、页面控件权限,以及页面访问链接加密处理的功能类

  它会将后端所有菜单与页面记录全部加载到缓存当中,优化后端检查权限时的性能;

  用户对后端页面的所有访问,都会经过CheckPagePower函数的处理,只要在系统中绑定了菜单与页面,后端页面开发时,就不必去考虑页面权限,即不用对每个页面的权限进行赋值,CheckPagePower函数会自动检查并判断用户是否有访问权限并做出相应处理;

  后端所有页面的加密解密处理函数也在这里,所有访问链接都需要加上对应的加密函数,不然该页面无法正常访问,这种功能可以使我们的后端在管理相关业务时,用户不能通过复制页面链接后修改链接参数中的Id访问,以达到查看当前用户查看无权限访问的信息,防止业务信息被非常查看(当然也不是不能破解,这种算法将加大其难度),另外该链接只有当前浏览器上有效,通过复制发送给他人马上失效。

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using DotNet.Utilities;
using Solution.DataAccess.DataModel; namespace Solution.Logic.Managers
{
/// <summary>
/// MenuInfoBll逻辑类
/// </summary>
public partial class MenuInfoBll : LogicBase
{
/***********************************************************************
* 自定义函数 *
***********************************************************************/ #region 自定义函数 private const string const_CacheKey_Model = "Cache_MenuInfo_AllModel"; #region 获取MenuInfo全表内容并放到缓存中 /// <summary>
/// 取得MenuInfo全表内容——使用菜单地址做为KEY
/// </summary>
/// <returns>返回Hashtable</returns>
public Hashtable GetHashtable()
{
//读取记录
object obj = CacheHelper.GetCache(const_CacheKey_Model);
//如果记录不存在,则重新加载
if (obj == null)
{
//初始化全局菜单内容缓存
var ht = new Hashtable();
//获取菜单表全部内容
var all = MenuInfo.All();
//遍历读取
foreach (var model in all)
{
//创建菜单实体
var menuinfo = new MenuInfo();
menuinfo.Id = model.Id;
menuinfo.Name = model.Name;
menuinfo.Url = model.Url;
menuinfo.ParentId = model.ParentId;
menuinfo.Depth = model.Depth; try
{
//将菜单实体存入容器中
//使用页面地址做为KEY
ht.Add(menuinfo.Url, menuinfo);
}
catch (Exception)
{
}
} if (ht.Count > )
{
CacheHelper.SetCache(const_CacheKey_Model, ht);
} return ht;
}
else
{
return (Hashtable) obj;
}
} #endregion #region 清空缓存 /// <summary>清空缓存</summary>
public override void DelCache()
{
CacheHelper.RemoveOneCache(const_CacheKey_Model);
} #endregion #region 根据菜单Url地址,获取菜单相应内容 /// <summary>
/// 根据菜单Url地址,获取菜单相应内容
/// </summary>
/// <param name="menuInfoUrl">页面地址</param>
/// <returns>返回菜单实体</returns>
public MenuInfo GetMenuInfo(string menuInfoUrl)
{
try
{
//从全局缓存中读取菜单内容
//获取菜单实体
return (MenuInfo) (MenuInfoBll.GetInstence().GetHashtable()[menuInfoUrl]);
}
catch (Exception)
{
return new MenuInfo();
}
} #endregion #region 检查页面权限 /// <summary>
/// 检查当前菜单或页面是否有权限访问
/// </summary>
/// <param name="menuId">菜单ID</param>
/// <returns>真或假</returns>
public bool CheckPagePower(string menuId)
{
var pagePower = OnlineUsersBll.GetInstence().GetPagePower();
if (string.IsNullOrEmpty(pagePower) || menuId == "")
{
return false;
}
//检查是否有权限
if (
pagePower.IndexOf("," + menuId + ",") >= )
{
return true;
}
else
{
return false;
}
} #endregion #region 检查当前页面控件是否有权限访问 /// <summary>
/// 检查当前页面控件是否有权限访问
/// </summary>
/// <param name="page">页面指针</param>
/// <param name="controlName">控件名称</param>
/// <returns>真或假</returns>
public bool CheckControlPower(Page page, string controlName)
{
//获取当前访问页面的URL
var currentPage = page.Request.Url.AbsolutePath;
//获取当前用户所有可以访问的页面ID
var menuId = GetMenuInfo(currentPage).Id;
//判断全局缓存中是否存储了该控件ID,否的话表示该控件没有权限
if (PagePowerSignPublicBll.GetInstence().GetHashtable()[controlName] == null)
{
return false;
}
else
{
var controlPower = OnlineUsersBll.GetInstence().GetControlPower();
if (string.IsNullOrEmpty(controlPower))
{
return false;
}
//获取当前控件ID
string ppsID = PagePowerSignPublicBll.GetInstence().GetHashtable()[controlName].ToString(); //检查是否有权限
if (controlPower.IndexOf("," + menuId + "|" + ppsID + ",") >= )
{
return true;
}
else
{
return false;
} } } #endregion #region 判断当前用户是否有当前页面操作权限 /// <summary>
/// 判断当前用户是否有本页面的操作权限
/// </summary>
/// <returns></returns>
public void CheckPagePower(Page page)
{
try
{
//获取当前访问页面的名称
var currentPage = page.Request.Url.AbsolutePath;
if (currentPage.Equals("/WebManage/Main.aspx"))
return; //检查是否从正确路径进入
ChichPageEncrypt(page); //获取当前用户所有可以访问的页面ID
var menuId = GetMenuInfo(currentPage).Id + ""; if (!CheckPagePower(menuId))
{
//添加用户访问记录
UseLogBll.GetInstence().Save(page, "{0}没有权限访问【{1}】页面"); page.Response.Write("您没有访问该页面的权限!");
page.Response.End();
return;
} }
catch (Exception)
{
//添加用户访问记录
UseLogBll.GetInstence().Save(page, "{0}没有权限访问【{1}】页面"); page.Response.Write("您没有访问该页面的权限!");
page.Response.End();
return;
}
} #endregion #region 页面访问加密--检查用户是否从正确的路径进入本页面 /// <summary>
/// 设置页面加密--用于检查用户是否从正确的路径进入本页面
/// </summary>
/// <param name="key">页面加密的Key</param>
/// <returns>加密后的字符串</returns>
public string SetPageEncrypt(string key)
{
//当前用户md5
var md5 = OnlineUsersBll.GetInstence().GetMd5();
//加密:md5+Key
var encrypt = DotNet.Utilities.Encrypt.Md5(md5 + key);
//再次加密:Key + Encrypt
return Encrypt.Md5(key + encrypt);
} /// <summary>
/// 检查用户是否从正确的路径进入本页面,默认KEY为ID
/// </summary>
public void CheckPageEncrypt(Page page)
{
//当前用户md5
var md5 = OnlineUsersBll.GetInstence().GetMd5();
//Key,如果没有传递Key这个变量过来的,就读取id或ParentID做为Key使用
var key = HttpContext.Current.Request["Id"];
if (string.IsNullOrEmpty(key))
{
key = HttpContext.Current.Request["pid"];
}
if (string.IsNullOrEmpty(key))
{
key = HttpContext.Current.Request["ParentId"];
}
if (string.IsNullOrEmpty(key))
{
key = HttpContext.Current.Request["Key"];
}
//上一链接传过来的加密数据
var keyEncrypt = HttpContext.Current.Request["KeyEncrypt"]; //加密:md5+Key
var encrypt = Encrypt.Md5(md5 + key);
//再次加密:Key + Encrypt
encrypt = Encrypt.Md5(key + encrypt); //检查是否有权限,没有权限的直接终止当前页面的运行
if (keyEncrypt != encrypt || string.IsNullOrEmpty(key))
{
try
{
//添加用户访问记录
UseLogBll.GetInstence().Save(page, "{0}没有权限访问【{1}】页面");
}
catch (Exception)
{
} HttpContext.Current.Response.Write("你从错误的路径进入当前页面!");
HttpContext.Current.Response.End();
}
} /// <summary>
/// 组成URL加密参数字符串
/// </summary>
/// <param name="key">页面加密的Key</param>
/// <returns>组成URL加密参数字符串</returns>
public string PageUrlEncryptString(string key)
{
return "KeyEncrypt=" + SetPageEncrypt(key) + "&Key=" + key;
} /// <summary>
/// 组成URL加密参数字符串,使用随机生成的Key,如果页面传的参数中包含有ID这个名称的,则不能使用本函数
/// </summary>
/// <returns>组成URL加密参数字符串</returns>
public string PageUrlEncryptString()
{
var key = RandomHelper.GetRandomCode(null, );
return "KeyEncrypt=" + SetPageEncrypt(key) + "&Id=" + key;
} /// <summary>
/// 组成URL加密参数字符串——返回不带Key的字符串
/// </summary>
/// <param name="key">页面加密的Key</param>
/// <returns>组成URL加密参数字符串——不带Key</returns>
public string PageUrlEncryptStringNoKey(string key)
{
return "KeyEncrypt=" + SetPageEncrypt(key);
} /// <summary>和 PageBase.BtnSave_Click 对应,部分页面刷新后不关闭原页面,并要刷新的情况下使用</summary>
/// <param name="url">跳转的url</param>
/// <returns></returns>
public string PageSaveReturnUrlFlash(string url)
{
url = DirFileHelper.GetFilePath(HttpContext.Current.Request.Path) + "/" + url;
return "{url}" + url;
} #endregion #region 从页面中找到放置按键控件的位置 /// <summary>
/// 从页面中找到放置按键控件的位置
/// </summary>
/// <param name="controls"></param>
/// <returns></returns>
public ControlCollection GetToolBarControls(ControlCollection controls)
{
if (controls == null)
return null; ControlCollection c = null;
try
{
for (int i = ; i < controls.Count; i++)
{
if (controls[i].ID == "toolBar")
{
return controls[i].Controls;
}
c = GetToolBarControls(controls[i].Controls);
if (c != null)
return c;
}
}
catch (Exception)
{
} return c;
} #endregion #endregion 自定义函数
}
}

  看看效果:

  这个是编辑页面的路径,KeyEncrypt参数值就是加密后的字串

  从零开始编写自己的C#框架(16)——Web层后端父类

  将地址复制出来在同一个浏览器换个标签访问,修改Id参数,加密串不变时,显示没权限访问

  从零开始编写自己的C#框架(16)——Web层后端父类

  换个浏览器使用同样的链接访问效果

  从零开始编写自己的C#框架(16)——Web层后端父类

  4、OnlineUsersBll逻辑类

  它是后端所有在线缓存、列表数据读写操作的功能类

  主要功能大家自己研究吧,由于未经过运行测试,后面开发时可能会对一些函数进行比较大的修改。

 using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using DotNet.Utilities;
using FineUI;
using Solution.DataAccess.DataModel; namespace Solution.Logic.Managers
{ /// <summary>
/// OnlineUsersBll逻辑类
/// </summary>
public partial class OnlineUsersBll : LogicBase
{
/***********************************************************************
* 自定义函数 *
***********************************************************************/
#region 自定义函数 #region 加载数据库记录到缓存
private static readonly object LockObject = new object();
/// <summary>
/// 加载数据库记录到缓存
/// </summary>
public void Load()
{
lock (LockObject)
{
//判断缓存中["OnlineUsers"]是否存在,不存在则尝试加载数据库中的在线表
if (CacheHelper.GetCache("OnlineUsers") == null)
{
//将当前用户信息添加到Hashtable中
var onlineUsers = new Hashtable(); //不存在则尝试加载数据库中的在线表到缓存中
var list = GetList();
if (list != null && list.Count > )
{
foreach (var model in list)
{
try
{
onlineUsers.Add(model.UserHashKey, model);
}
catch
{
}
} //将在线列表(Hashtable)添中进系统缓存中
CacheHelper.SetCache("OnlineUsers", onlineUsers);
}
}
}
}
#endregion #region 获取在线用户表内容
/// <summary>
/// 根据字段名称,获取当前用户在线表中的内容
/// </summary>
/// <param name="colName">字段名<para/>
/// 可选参数<para/>
/// UserId : 当前用户的ID编号<para/>
/// LoginDate : 登录时间<para/>
/// OnlineTime : 在线时长<para/>
/// LoginIp : 当前用户IP<para/>
/// LoginName : 当前用户登陆名<para/>
/// CName : 当前用户中文名<para/>
/// Sex : 当前用户的性别<para/>
/// BranchId : 部门自动ID<para/>
/// BranchCode : 部门编码<para/>
/// BranchName : 部门名称<para/>
/// PositionInfoId : 职位ID<para/>
/// PositionInfoName : 职位名称<para/>
/// </param>
/// <returns></returns>
public object GetUserOnlineInfo(string colName)
{
return GetUserOnlineInfo(null, colName);
}
/// <summary>
/// 根据字段名称,获取指定用户在线表中的内容
/// </summary>
/// <param name="userHashKey">用户在线列表的Key</param>
/// <param name="colName">字段名<para/>
/// userId : 当前用户的ID编号<para/>
/// LoginDate : 登录时间<para/>
/// OnlineTime : 在线时长<para/>
/// LoginIp : 当前用户IP<para/>
/// LoginName : 当前用户登陆名<para/>
/// CName : 当前用户中文名<para/>
/// Sex : 当前用户的性别<para/>
/// BranchId : 部门自动ID<para/>
/// BranchCode : 部门编码<para/>
/// BranchName : 部门名称<para/>
/// PositionInfoId : 职位ID<para/>
/// PositionInfoName : 职位名称<para/>
/// </param>
/// <returns></returns>
public object GetUserOnlineInfo(string userHashKey, string colName)
{
//由于GetUserHashKey有可能从 try
{
if (colName == "")
{
return null;
} if (userHashKey == null)
{
userHashKey = GetUserHashKey() +"";
} //如果不存在在线表则退出
if (HttpRuntime.Cache["OnlineUsers"] == null || userHashKey == "")
return null; //返回指定字段的内容
var model = (DataAccess.Model.OnlineUsers)((Hashtable) CacheHelper.GetCache("OnlineUsers"))[userHashKey]; return GetFieldValue(model, colName);
}
catch (Exception e)
{
CommonBll.WriteLog("", e);
} return null;
}
#endregion #region 更新在线用户信息
/// <summary>
/// 更新在线用户信息
/// </summary>
/// <param name="userId">用户ID,即在线用户表中的KEY</param>
/// <param name="colName">要更新的字段名称</param>
/// <param name="value">将要赋的值</param>
public void UpdateUserOnlineInfo(string userId, string colName, object value)
{
try
{
((Dictionary<String, object>)((Hashtable)CacheHelper.GetCache("OnlineUsers"))[userId])[colName] = value;
}
catch (Exception) { }
}
#endregion #region 获取在线人数
/// <summary>
/// 获取在线人数
/// </summary>
/// <returns></returns>
public int GetUserOnlineCount()
{
return ((Hashtable)CacheHelper.GetCache("OnlineUsers")).Count;
}
#endregion #region 更新在线人数
/// <summary>
/// 更新在线人数,将不在线人员删除
/// </summary>
public void UpdateUserOnlineCount()
{
//获取在线列表
var onlineUsers = (Hashtable)CacheHelper.GetCache("OnlineUsers"); //如果不存在在线表则退出
if (onlineUsers == null)
return; //初始化下线用户KEY存储数组
var users = new string[onlineUsers.Count];
int i = ; //循环读取在线信息
foreach (DictionaryEntry de in onlineUsers)
{
//判断该用户是否在线,不在线的话则将它暂停存储起来
if (CacheHelper.GetCache("OnlineUsers_" + de.Key) == null)
{
users[i] = de.Key + "";
i++;
}
} //移除在线数据
for (int j = ; j < users.Length; j++)
{
if (users[j] == null)
break; //添加用户下线记录
LoginLogBll.GetInstence().Save(users[j], "用户【{0}】退出系统!在线时间【{1}】");
//将HashTable里存储的前一登陆帐户移除
Delete(null, x => x.UserHashKey == users[j]);
//移除在线用户
RemoveUser(users[j]);
}
}
#endregion #region 删除在线缓存中的用户
/// <summary>
/// 删除在线缓存中的用户
/// </summary>
/// <param name="userHashKey"></param>
public void RemoveUser(string userHashKey)
{
//获取在线列表
var onlineUsers = (Hashtable)CacheHelper.GetCache("OnlineUsers"); //如果不存在在线表则退出
if (onlineUsers == null)
return; //移除在线用户
onlineUsers.Remove(userHashKey);
}
#endregion #region 判断用户是否被强迫离线
/// <summary>
/// 判断用户是否被强迫离线[true是;false否](由于用户长时间没有操作系统自动剔除用户节省资源,管理人员剔除下线)
/// </summary>
public bool IsOffline(Page page)
{
try
{
//获取当前用户Id
var userinfoId = GetManagerId();
//获取用户Key
var userHashKey = GetUserHashKey(); //判断当前用户是否已经被系统清出
if (userinfoId == || HttpRuntime.Cache.Get("OnlineUsers_" + userHashKey) == null)
{
//通知用户
FineUI.Alert.Show("您太久没有操作已退出系统,请重新登录!", "检测通知", MessageBoxIcon.Information, "window.location.href='/WebManage/Login.aspx';");
return true;
}
else
{
//判断在线用户的Md5与当前用户存储的Md5是否一致
if (GenerateMd5() != CookieHelper.GetCookieValue(OnlineUsersTable.Md5))
{
CommonBll.WriteLog("当前帐号已经下线,用户Id【" + userinfoId + "】"); //通知用户
FineUI.Alert.Show("您的账号已经在另一处登录,当前账号已经下线!", "检测通知", MessageBoxIcon.Information, "window.location.href='/WebManage/Login.aspx';");
return true;
}
}
return false;
}
catch (Exception ex)
{
CommonBll.WriteLog("Logic.Systems.Manager.CheckIsOffline出现异常", ex); FineUI.Alert.Show("系统已经开始更新维护,请稍后重新登录!", "检测通知", MessageBoxIcon.Information, "window.location.href='/WebManage/Login.aspx';");
return true;
} }
#endregion #region 判断用户是否超时退出 /// <summary>
/// 判断用户是否超时退出(退出情况:1.系统更新,2.用户自动退出)
/// </summary>
public void IsTimeOut()
{
if (HttpContext.Current.Session == null || GetUserHashKey() == null)
{
try
{
//不存在则表示Session失效了,重新从Cookies中加载
var userHashKey = CookieHelper.GetCookieValue(OnlineUsersTable.UserHashKey);
var md5 = CookieHelper.GetCookieValue(OnlineUsersTable.Md5); //判断Cookies是否存在,存在则查询在线列表,重新获取用户信息
if (userHashKey.Length > && md5.Length == )
{
//读取当前用户在线实体
var model = GetModelForCache(x => x.UserHashKey == userHashKey);
//当前用户存在在线列表中
if (model != null)
{
//计算用户md5值
var key = GenerateMd5(model); //判断用户的md5值是否正确
if (md5 == key)
{
//将UserHashKey存储到缓存中
HttpContext.Current.Session[OnlineUsersTable.UserHashKey] = userHashKey;
//获取用户权限并存储到用户Session里
PositionBll.GetInstence().SetUserPower(model.Position_Id);
//更新用户当前SessionId到在线表中
//UpdateUserOnlineInfo(model.Id + "", OnlineUsersTable.SessionId, HttpContext.Current.Session.SessionID); //加载用户相关信息完毕,退出超时检查
return;
}
}
}
}
catch (Exception e)
{
//出现异常,保存出错日志信息
CommonBll.WriteLog("", e);
} //用户不存在,直接退出
FineUI.Alert.Show("当前用户登录已经过时或系统已更新,请重新登录!", "检测通知", MessageBoxIcon.Information, "window.location.href='/WebManage/Login.aspx';");
HttpContext.Current.Response.End();
}
}
#endregion #region 生成加密串——用户加密密钥计算
/// <summary>
/// 生成加密串——用户加密密钥计算
/// </summary>
/// <param name="model">在线实体</param>
/// <returns></returns>
public string GenerateMd5(DataAccess.Model.OnlineUsers model)
{
if (model == null)
{
return RandomHelper.GetRndKey();
}
else
{
//Md5(密钥+登陆帐号+密码+IP+密钥.Substring(6,8))
return Encrypt.Md5(model.UserKey + model.Manager_LoginName + model.Manager_LoginPass +
IpHelper.GetUserIp() + model.UserKey.Substring(, ));
}
} /// <summary>
/// 生成加密串——用户加密密钥计算,直接读取当前用户实体
/// </summary>
/// <returns></returns>
public string GenerateMd5()
{
//读取当前用户实体
var model = GetModelForCache(GetManagerId());
return GenerateMd5(model);
}
#endregion #region 获取用户ID
/// <summary>
/// 从Session中读取用户ID,如果Session为Null时,返回0
/// </summary>
/// <returns></returns>
public int GetManagerId()
{
return ConvertHelper.Cint0(GetUserOnlineInfo(OnlineUsersTable.Manager_Id));
}
#endregion #region 获取用户中文名称
/// <summary>
/// 从Session中读取用户中文名称,如果Session为Null时,返回""
/// </summary>
/// <returns></returns>
public string GetManagerCName()
{
return GetUserOnlineInfo(OnlineUsersTable.Manager_CName) + "";
}
#endregion #region 获取用户UserHashKey
/// <summary>
/// 获取用户UserHashKey
/// </summary>
/// <returns></returns>
public object GetUserHashKey()
{
//读取Session中存储的UserHashKey值
var userHashKey = HttpContext.Current.Session[OnlineUsersTable.UserHashKey];
//如果为null
if (userHashKey == null)
{
//为null则表示用户Session过期了,所以要检查用户登陆,避免用户权限问题
IsTimeOut(); //从Cookies中读取
userHashKey = CookieHelper.GetCookieValue(OnlineUsersTable.UserHashKey);
//不为null时,重新存储到Session中
if (userHashKey != null)
{
HttpContext.Current.Session[OnlineUsersTable.UserHashKey] = userHashKey;
}
}
return userHashKey;
}
#endregion #region 获取用户加密串——用户加密密钥Md5值
/// <summary>
/// 获取用户加密串——用户加密密钥Md5值
/// </summary>
/// <returns></returns>
public object GetMd5()
{
//读取Session中存储的Md5值
var md5 = HttpContext.Current.Session[OnlineUsersTable.Md5];
//如果为null
if (md5 == null)
{
//为null则表示用户Session过期了,所以要检查用户登陆,避免用户权限问题
IsTimeOut(); //从Cookies中读取
md5 = CookieHelper.GetCookieValue(OnlineUsersTable.Md5);
//不为null时,重新存储到Session中
if (md5 != null)
{
HttpContext.Current.Session[OnlineUsersTable.Md5] = md5;
}
}
return md5;
}
#endregion #region 获取用户页面操作权限
/// <summary>
/// 获取用户页面操作权限
/// </summary>
/// <returns></returns>
public string GetPagePower()
{
//读取Session中存储的PagePower值
var pagePower = HttpContext.Current.Session[PositionTable.PagePower];
//如果为null
if (pagePower == null)
{
//获取用户权限并存储到用户Session里
PositionBll.GetInstence().SetUserPower(GetUserOnlineInfo(OnlineUsersTable.Position_Id) + "");
}
pagePower = HttpContext.Current.Session[PositionTable.PagePower];
return pagePower + "";
}
#endregion #region 获取用户页面控件(按键)操作权限
/// <summary>
/// 获取用户页面控件(按键)操作权限
/// </summary>
/// <returns></returns>
public string GetControlPower()
{
//读取Session中存储的ControlPower值
var controlPower = HttpContext.Current.Session[PositionTable.ControlPower];
//如果为null
if (controlPower == null)
{
//获取用户权限并存储到用户Session里
PositionBll.GetInstence().SetUserPower(GetUserOnlineInfo(OnlineUsersTable.Position_Id) + "");
}
controlPower = HttpContext.Current.Session[PositionTable.ControlPower];
return controlPower + "";
}
#endregion #endregion 自定义函数
}
}

点击下载:

由于框架不是非常成熟,很多朋友不是用来学习而是直接用到项目中,但不熟悉框架引起不少小问题,所以停止提供下载,有需要学习的可以到群共享里下,不便之处敬请谅解。

 由于本解决方案开发时没有经过调试,所以存在一些小BUG,这些会在下一章节发布的代码中解决,大家先自行熟悉一下代码与结构就可以了

 版权声明:

  本文由AllEmpty原创并发布于博客园,欢迎转载,未经本人同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如有问题,可以通过1654937@qq.com 联系我,非常感谢。

  发表本编内容,只要主为了和大家共同学习共同进步,有兴趣的朋友可以加加Q群:327360708 ,大家一起探讨。

  更多内容,敬请观注博客:http://www.cnblogs.com/EmptyFS/