Asp.net MVC权限设计思考 (二)逻辑部分实现
在我的项目中,我还是使用的LINQ TO SQL ,因为我的项目不会涉及太多很太复杂的数据库操作业务。当然如果设计,我相信LINQ TO SQL的自定义扩展也能满足需求。
使用Repository模式是最近MVC很多项目采用的解决方案,能把原来我们杂乱的LINQ TO SQL统一封装起来。让我们的架构更清晰。
现在来看看具体实现。
IRepository接口:
{
IQueryable<TEntity> FindAll(Expression<Func<TEntity, bool>> exp);
TEntity Find(Expression<Func<TEntity, bool>> exp);
void Add(TEntity entity);
void Delete(TEntity entity);
void Save();
}
Repository实现:
{
protected DAL.CourseCenterData db;
public Repository()
{
db = new DAL.CourseCenterData();
}
/// <summary>
/// 查找所有数据
/// </summary>
/// <returns></returns>
public IQueryable<TEntity> FindAll()
{
return db.GetTable<TEntity>().Where(p => 1==1);
}
/// <summary>
/// 查找所有数据
/// </summary>
/// <param name="exp">条件表达式</param>
/// <returns></returns>
public IQueryable<TEntity> FindAll(Expression<Func<TEntity, bool>> exp)
{
return db.GetTable<TEntity>().Where(exp);
}
/// <summary>
/// 查找一个数据
/// </summary>
/// <param name="exp">条件表达式</param>
/// <returns></returns>
public TEntity Find(Expression<Func<TEntity, bool>> exp)
{
return db.GetTable<TEntity>().FirstOrDefault(exp);
}
/// <summary>
/// 添加数据
/// </summary>
/// <param name="entity">实体</param>
public void Add(TEntity entity)
{
db.GetTable<TEntity>().InsertOnSubmit(entity);
}
/// <summary>
/// 删除数据
/// </summary>
/// <param name="entity">实体</param>
public void Delete(TEntity entity)
{
db.GetTable<TEntity>().DeleteOnSubmit(entity);
}
/// <summary>
/// 批量添加数据
/// </summary>
/// <param name="entity">实体列表</param>
public void AddAll(IEnumerable<TEntity> entity)
{
db.GetTable<TEntity>().InsertAllOnSubmit(entity);
}
/// <summary>
/// 批量删除数据
/// </summary>
/// <param name="entity">实体列表</param>
public void DeleteAll(IEnumerable<TEntity> entity)
{
db.GetTable<TEntity>().DeleteAllOnSubmit(entity);
}
/// <summary>
/// 对数据做插入,更新,删除操作
/// </summary>
public void Save()
{
db.SubmitChanges();
}
网上已经有很多Repository的例子,但是请注意Find中的条件必须是Expression Tree的扩展,否则在数据查询的SQL语句中,你会发现捕获到的将是select一个表之后再来做数据的查询,这在我们海量数据查询时不允许的。
现在我们已经有了自己的Repository,下面来建立一个数据库视图把我们上一讲需要的数据拿出来。
新建视图ViewRoleGroup,选定一下表和字段
我们通过VS2008新建一个LINQ TO SQL类,拖入这个视图。
然后我们需要思考,这个权限分组视图是系统频繁读取的,需要为他创建一个缓存。
新建一个缓存类Caches:
/// 缓存操作基类
/// </summary>
public class Caches
{
/// <summary>
/// 建立缓存
/// </summary>
public static object TryAddCache(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration,
TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemovedCallback)
{
if (HttpRuntime.Cache[key] == null && value != null)
return HttpRuntime.Cache.Add(key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemovedCallback);
else
return null;
}
/// <summary>
/// 移除缓存
/// </summary>
public static object TryRemoveCache(string key)
{
if (HttpRuntime.Cache[key] != null)
return HttpRuntime.Cache.Remove(key);
else
return null;
}
/// <summary>
/// 移除键中带某关键字的缓存
/// </summary>
public static void RemoveMultiCache(string keyInclude)
{
IDictionaryEnumerator CacheEnum = HttpRuntime.Cache.GetEnumerator();
while (CacheEnum.MoveNext())
{
if (CacheEnum.Key.ToString().IndexOf(keyInclude.ToString()) >= 0)
HttpRuntime.Cache.Remove(CacheEnum.Key.ToString());
}
}
/// <summary>
/// 移除所有缓存
/// </summary>
public static void RemoveAllCache()
{
IDictionaryEnumerator CacheEnum = HttpRuntime.Cache.GetEnumerator();
while (CacheEnum.MoveNext())
{
HttpRuntime.Cache.Remove(CacheEnum.Key.ToString());
}
}
来看看我们的ViewRoleGroupRepository怎么写。
新建类:ViewRoleGroupRepository
{
/// <summary>
/// 获取权限视图缓存列表,Key:ViewGroupList
/// </summary>
/// <returns></returns>
public List<Models.Database.ViewRoleGroup> GetCacheAll()
{
List<Models.Database.ViewRoleGroup> viewRoleGroup;
string key = "ViewGroupList";
if (HttpRuntime.Cache[key] != null)
viewRoleGroup = (List<Models.Database.ViewRoleGroup>)HttpRuntime.Cache[key];
else
{
viewRoleGroup = FindAll().ToList();
Caches.TryAddCache(key, viewRoleGroup, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(20), System.Web.Caching.CacheItemPriority.Normal, null);
}
return viewRoleGroup;
}
}
接下来我们将自定义自己的AuthorizeAttribute,
新建类CenterAuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
string action = (string)filterContext.RouteData.Values["Action"];
string controller = (string)filterContext.RouteData.Values["Controller"];
string fullName = filterContext.HttpContext.User.Identity.Name;
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
filterContext.HttpContext.Response.Redirect(string.Format("~/Account/LogOn?returnUrl={0}", filterContext.HttpContext.Request.Url.PathAndQuery));
else
{
if (new Login().IsLock(fullName))
throw new Exception(string.Format("【用户:{0}】对不起,该用户已被锁定。", fullName));
var r = new BLL.RoleGroupRepository().GetUserRoleGroup(fullName);
var q = new BLL.ViewRoleGroupRepository().GetCacheAll().FindAll(c =>
c.RoleID == r.RoleID && c.SysAppController == controller && c.SysAppAction == action &&
(c.StartTime.Equals("Anytime", StringComparison.CurrentCultureIgnoreCase) ? true : (DateTime.Parse(c.StartTime) <= DateTime.Now)) &&
(c.EndTime.Equals("Anytime", StringComparison.CurrentCultureIgnoreCase) ? true : (DateTime.Parse(c.EndTime) >= DateTime.Now)));
if (q.Count == 0)
throw new Exception(string.Format("【用户:{0}】对不起,您没有访问该页面的权限。", fullName));
}
}
}
请注意var r = new BLL.RoleGroupRepository().GetUserRoleGroup(fullName);这个是我项目中通过用户名获得用户权限分组的实现,大家参考自己项目修改。
大功告成,那怎么用呢,很简单。创建了需要的权限组,例如
超级管理员 然后把对应的Controller和Action权限加入到组后,在需要的Action上进行标注,例如
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
是不是很简单就通过数据库控制到每个需要权限控制的Action咯?当然很多朋友可能还很迷糊,第一次写这类文章。部分代码也没调试。因为从项目剥离出来,所以大家见谅。
UI部分和数据库的操作这里想滤过啦。这些比较简单的东西,大家应该很容易就能实现咯。