全局权限过滤器

//-----------------------------------------------------------------------
// <copyright file="PermissionFilter.cs" company="STO EXPRESS, Ltd.">
// Copyright (c) 2015 , All rights reserved.
// </copyright>
//----------------------------------------------------------------------- using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc; namespace DotNet.MVCInfrastructure.Filter
{
using DotNet.Model;
using DotNet.MVCInfrastructure.Attributes;
using DotNet.MVCInfrastructure.Common;
using DotNet.MVCInfrastructure.Enumerations;
using DotNet.MVCInfrastructure.Models;
using DotNet.Utilities;
using DotNet.Business; /// <summary>
/// 身份验证过滤器
///
/// 1、匿名访问
/// 2、登录就可以访问
/// 3、需要验证是否有菜单或按钮或资源的权限
///
///
/// 修改纪录
///
/// 2015-10-11 版本:1.0 SongBiao 创建文件。
///
/// <author>
/// <name>SongBiao</name>
/// <date>2015-10-11</date>
/// </author>
/// </summary>
public class CheckPermissionFilter : IAuthorizationFilter
{
/// <summary>
/// 认证和授权是两个方面
/// </summary>
/// <param name="filterContext"></param>
public void OnAuthorization(AuthorizationContext filterContext)
{
string pageUrl = filterContext.HttpContext.Request.Url == null ? "$$#$$" : filterContext.HttpContext.Request.Url.AbsolutePath; //OperateContext.GetThisPageUrl(false); //NLogHelper.Debug("CheckPermissionFilter:" + DateTime.Now + ",pageUrl=" + pageUrl); if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.HttpContext.Request.Url == null)
{
throw new ArgumentNullException("filterContext");
} // 是否是Ajax请求
var bAjax = filterContext.HttpContext.Request.IsAjaxRequest(); // 注意 所有允许匿名访问的Controller,Action 都要设置匿名访问标签
// 只对 Action 做判断 // 判断 控制器 是否可以匿名访问
var controllerAnonymous = filterContext.Controller.GetType().GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;
// 先判断控制器是否可以匿名访问
if ((controllerAnonymous != null && controllerAnonymous.Any()))
{
// 再检查Action 是否有登录检查标签
var checkLoginActionAttr = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CheckLoginAttribute), true) as IEnumerable<CheckLoginAttribute>;
if (checkLoginActionAttr != null && checkLoginActionAttr.Any())
{
}
else
{
// 如果没有 让他可以继续访问 因为把Controller 设置为 AllowAnonymous 了
return;
}
// 匿名就可以访问 无需验证登录状态
//return;
} // 判断 Action 是否可以匿名访问
var actionAnonymous = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;
if (actionAnonymous != null && actionAnonymous.Any())
{
// 匿名就可以访问 无需验证登录状态
return;
} // 1、允许匿名访问 用于标记在授权期间要跳过 AuthorizeAttribute 的控制器和操作的特性
// 2017-03-06 检查OpenId 只需传来openId
if (!string.IsNullOrWhiteSpace(filterContext.HttpContext.Request["openId"]))
{
string openId = filterContext.HttpContext.Request["openId"];
UserLogOnResult userLogOnResult = Business.Utilities.LogOnByOpenId(openId, true);
if (userLogOnResult != null && !string.IsNullOrEmpty(userLogOnResult.StatusCode) &&
userLogOnResult.StatusCode == Status.OK.ToString())
{
// 用户状态存储
OperateContext.Current.AddCurrent(userLogOnResult.UserInfo);
}
}
else if (!string.IsNullOrWhiteSpace(filterContext.HttpContext.Request["AuthorizationCode"]))
{
// 2017-05-23 检查传过来的AuthorizationCode 支持code跳转登录
string authorizationCode = filterContext.HttpContext.Request["AuthorizationCode"];
string openId;
if (BaseUserManager.VerifyAuthorizationCode(null, authorizationCode, out openId))
{
// 用户状态存储
UserLogOnResult userLogOnResult = Business.Utilities.LogOnByOpenId(openId, true);
if (userLogOnResult != null && !string.IsNullOrEmpty(userLogOnResult.StatusCode) &&
userLogOnResult.StatusCode == Status.OK.ToString())
{
// 用户状态存储
OperateContext.Current.AddCurrent(userLogOnResult.UserInfo);
}
}
} // 2、判断是否登录或登录已超时 需要重新登录
if (OperateContext.Current.UserInfo == null)
{
// 用户状态已过期
// 判断请求是否是Ajax请求
if (bAjax)
{
BusinessResultBase result = new BusinessResultBase();
result.Title = "未登录或登录已超时";
result.Status = false;
result.StatusCode = BusinessStatusCode.LoginTimeOut.ToString();
result.StatusMessage = "请重新登录系统。"; var jsonResult = new JsonResult();
jsonResult.Data = result;
jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
filterContext.Result = jsonResult; }
else
{
//filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area="",Controller = "Account", action = "Login" }));
filterContext.Result = new RedirectResult(BusinessSystemInfo.LoginUrl + "?returnUrl=" + pageUrl);
}
}
else
{
// 用户状态未过期
// 3、拒绝某个账号登录当前系统 判断用户是否在拒绝登录的子系统中
//if (OperateContext.Current.IsDenyVisit())
//{
// if (bAjax)
// {
// BusinessResultBase result = new BusinessResultBase();
// result.Title = "拒绝访问当前系统";
// result.Status = false;
// result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
// result.StatusMessage = "您的账号不允许访问当前系统。";
// var jsonResult = new JsonResult();
// jsonResult.Data = result;
// filterContext.Result = jsonResult;
// }
// else
// {
// filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Controller = "Prompt", action = "DenyAccess", bAjaxReq = false, message = "没有获取您拥有的权限菜单,请尝试重新登录。" }));
// }
//}
//else
if (OperateContext.Current.UserInfo.IsAdministrator)
{
// 超级管理员不检查权限了
return;
}
else
{
// 4、判断用户是否能够访问当前的controller,action // 5、判断登录状态 根据Controller或Action上的标签 某些功能只需判断是否登录
// 判断Controller上是否有CheckLoginAttribute标签 只需要登录就可以访问 // 实际上检查登录也不好,应该检查改Action是否是公开的 上面做为临时用 某些系统没有菜单或者没有配置的Action可以这样做 否则都必须配置菜单 // 判断Controller上是否有CheckLoginAttribute标签 只需要登录就可以访问的
var checkLoginControllerAttr = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(CheckLoginAttribute), true) as IEnumerable<CheckLoginAttribute>;
if (checkLoginControllerAttr != null && checkLoginControllerAttr.Any())
{
// 否则 没有设置 CheckLogin 标签的 就要验证 向下执行 到这里 除了设置 CheckLogin 的action 都要检查菜单访问权限
return;
} // 判断action上是否有CheckLoginAttribute标签 只需要登录就可以访问的
var checkLoginActionAttr = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CheckLoginAttribute), true) as IEnumerable<CheckLoginAttribute>;
if (checkLoginActionAttr != null && checkLoginActionAttr.Any())
{
return;
} // 如果有 CheckActionPermission 就过 让CheckActionPermission处理
var checkPermissionActionActionAttr = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CheckActionPermissionAttribute), true) as IEnumerable<CheckActionPermissionAttribute>;
if (checkPermissionActionActionAttr != null && checkPermissionActionActionAttr.Any())
{
return;
} // 6、有些要判断是否有某个controller或 action的权限 通过获取的权限菜单进行比较
// 用户具有的菜单
var moduleList = OperateContext.Current.UserPermission;//.GetPermissionList(false);
if (moduleList == null || !moduleList.Any())
{
// 没有获取到任何菜单
if (bAjax)
{
BusinessResultBase result = new BusinessResultBase();
result.Title = "没有访问权限";
result.Status = false;
result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
result.StatusMessage = "没有获取到任何菜单,请尝试重新登录或咨询系统开发人员。";
var jsonResult = new JsonResult();
jsonResult.Data = result;
jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
filterContext.Result = jsonResult;
//var jsonResult = new JsonResult { Data = new BaseModels { IsError = true, ErrMsg = "请先登录!", ErrCode = "unlogin" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
//filterContext.RequestContext.HttpContext.Response.Write(JsonConvert.SerializeObject(result));
//filterContext.RequestContext.HttpContext.Response.End();
}
else
{
//filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "没有获取您拥有的权限菜单,请尝试重新登录。" }));
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "没有获取到任何菜单,您没有权限访问当前内容:" + pageUrl + "。" }));
}
}
else
{
// 获取到菜单,进行比较
//var controllerName = (filterContext.RouteData.Values["controller"]).ToString().ToLower();
//var actionName = (filterContext.RouteData.Values["action"]).ToString().ToLower();
//var areaName = (filterContext.RouteData.DataTokens["area"] ?? "").ToString().ToLower();
/* 这个方式需要在controller或action上人为添加一个Attribute,没有必要,直接可以取到当前的controller和actionName
// 用于标记在授权期间需要CustomerResourceAttribute 的操作的特性
var attNames = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CustomerResourceAttribute), true) as IEnumerable<CustomerResourceAttribute>;
// 判断用户的权限菜单中的code是否与控制器上标示的资源的code一致
var joinResult = (from aclEntity in moduleList
join attName in attNames on aclEntity.Code equals attName.ResourceName
select attName).Any();
if (!joinResult)
*/
// 子系统菜单配置时,子系统的菜单code不能重复
// 同时支持通过访问地址,Code来判断
// 主要是要在全局过滤器里添加 filters.Add(new CheckPermissionFilter()); // 检查地址是否与当前Action的地址一致
var modules = from module in moduleList
where
string.Equals(module.NavigateUrl, pageUrl, StringComparison.OrdinalIgnoreCase)
//|| string.Equals(module.Code, controllerName, StringComparison.OrdinalIgnoreCase)
//|| string.Equals(module.Code, actionName, StringComparison.OrdinalIgnoreCase)
select module;
//string results = JsonHelper.SerializeObject(moduleList);
if (!modules.Any())
{
// 菜单没有配置或者没有权限
if (bAjax)
{
BusinessResultBase result = new BusinessResultBase();
result.Title = "没有访问权限";
result.Status = false;
result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
result.StatusMessage = "在您的权限中,您没有权限访问当前内容:" + pageUrl + "。";
var jsonResult = new JsonResult();
jsonResult.Data = result;
jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
filterContext.Result = jsonResult;
}
else
{
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "在您的权限中,您没有权限访问当前内容:" + pageUrl + "。" }));
}
}
else
{
return;
}
}
}
}
}
}
}

放在全局过滤器中即可,实现全部Action的访问控制

    public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
// filters.Add(new HandleErrorAttribute());
filters.Add(new ElmahHandleErrorAttribute());
// 全局身份验证过滤器 更方便
filters.Add(new CheckPermissionFilter());
}
}

继承权限的使用场景:

在某些情况下,只要具有其中一个Action的权限,那么跟他关联的Action权限可以不用配置,
如在查询场景中,配置了用户访问 Public ActionResult Index()主页面的权限,查询时请求的是 Public ActionResult List(......)
那么在List上加上标签
[CheckActionPermission("/XXXArea/XXXController/Index")]
Public ActionResult List(......)
这样就可以了,
CheckActionPermission里的参数是要继续的Action的菜单路径。
避免为List再配置菜单,授权。
另外象添加页面,保存的action Save可以不必配置菜单授权,只要在上面配置[CheckActionPermission("/XXXArea/XXXController/Add")]即可。既然能看到添加界面,那么保存操作就应该是允许的。

//-----------------------------------------------------------------------
// <copyright file="CheckActionPermissionAttribute" company="STO, Ltd.">
// Copyright (c) 2017 , All rights reserved.
// </copyright>
//----------------------------------------------------------------------- using System;
using System.Linq;
using System.Web.Mvc; namespace DotNet.MVCInfrastructure.Attributes
{
using DotNet.MVCInfrastructure.Enumerations;
using DotNet.MVCInfrastructure.Models;
using DotNet.MVCInfrastructure.Common;
using DotNet.Utilities; /// <summary>
/// CheckActionPermissionAttribute
/// 记权限拦截
///
/// 修改纪录
///
/// 2017-07-21 版本:1.0 SongBiao 创建文件。
///
/// <author>
/// <name>SongBiao</name>
/// <date>2017-07-21</date>
/// </author>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class CheckActionPermissionAttribute : ActionFilterAttribute, IActionFilter
{
/// <summary>
/// 检查的Module
/// </summary>
private string Module; /// <summary>
/// 构造函数 module表示检查哪个菜单的权限即可
/// 如某些列表,只需要检查主页面的菜单权限即可
/// CheckActionPermission["/Headquarters/DaTouBiMain/Index"]
/// </summary>
/// <param name="module"></param>
public CheckActionPermissionAttribute(string module)
{
Module = module;
} FilterContextInfo fcinfo;
/// <summary>
/// 在执行操作方法之前由 ASP.NET MVC 框架调用。
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string pageUrl = filterContext.HttpContext.Request.Url == null ? "$$#$$" : filterContext.HttpContext.Request.Url.AbsolutePath; //OperateContext.GetThisPageUrl(false);
var bAjax = filterContext.HttpContext.Request.IsAjaxRequest();
if (string.IsNullOrWhiteSpace(Module))
{
if (bAjax)
{
BusinessResultBase result = new BusinessResultBase();
result.Title = "参数值不可以为空";
result.Status = false;
result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
result.StatusMessage = "CheckActionPermission参数值不可以为空。";
var jsonResult = new JsonResult();
jsonResult.Data = result;
jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
filterContext.Result = jsonResult;
}
else
{
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "CheckActionPermission参数值不可以为空" }));
}
}
else
{
if (OperateContext.Current.UserInfo == null)
{
if (bAjax)
{
BusinessResultBase result = new BusinessResultBase();
result.Title = "未登录或登录已超时";
result.Status = false;
result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
result.StatusMessage = "未登录或登录已超时,请重新登录系统。";
var jsonResult = new JsonResult();
jsonResult.Data = result;
jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
filterContext.Result = jsonResult;
}
else
{
filterContext.Result = new RedirectResult(BusinessSystemInfo.LoginUrl + "?returnUrl=" + pageUrl);
}
}
else
{
fcinfo = new FilterContextInfo(filterContext);
string areaName = fcinfo.AreaName;
string controllerName = fcinfo.ControllerName;
string actionName = fcinfo.ActionName; // 当前访问的
string currentModule = "/";
if (!string.IsNullOrWhiteSpace(areaName))
{
currentModule = currentModule + areaName;
}
if (!string.IsNullOrWhiteSpace(controllerName))
{
currentModule = currentModule + "/" + controllerName;
}
if (!string.IsNullOrWhiteSpace(actionName))
{
currentModule = currentModule + "/" + actionName;
} var moduleList = OperateContext.Current.UserPermission;
// 比较Module在菜单中是否存在 如列表的权限只需要验证主界面的权限即可
var modules = from module in moduleList
where
!string.IsNullOrWhiteSpace(module.NavigateUrl) && string.Equals(module.NavigateUrl.Trim(), Module.Trim(), StringComparison.OrdinalIgnoreCase)
//|| string.Equals(module.Code, controllerName, StringComparison.OrdinalIgnoreCase)
//|| string.Equals(module.Code, actionName, StringComparison.OrdinalIgnoreCase)
select module;
//var modules = moduleList.Where(t => string.Equals(t.NavigateUrl, Module, StringComparison.OrdinalIgnoreCase));
if (modules == null || !modules.Any())
{
// 菜单没有配置或者没有权限
if (bAjax)
{
BusinessResultBase result = new BusinessResultBase();
result.Title = "没有访问权限";
result.Status = false;
result.StatusCode = BusinessStatusCode.AccessDeny.ToString();
result.StatusMessage = "在您的权限中,您没有权限访问当前内容:" + currentModule + ",需要授予用户对:" + Module + "的权限。";
var jsonResult = new JsonResult();
jsonResult.Data = result;
jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
filterContext.Result = jsonResult;
}
else
{
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Area = "", Controller = "Error", action = "NoAccess", bAjaxReq = false, message = "在您的权限中,您没有权限访问当前内容:" + currentModule + ",需要授予用户对:" + Module + "的权限。" }));
}
}
else
{
return;
}
}
}
} /// <summary>
/// 在执行操作方法后由 ASP.NET MVC 框架调用。
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{ base.OnActionExecuted(filterContext);
} /// <summary>
/// OnResultExecuted 在执行操作结果后由 ASP.NET MVC 框架调用。
/// </summary>
/// <param name="filterContext"></param>
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
}
/// <summary>
/// OnResultExecuting 在执行操作结果之前由 ASP.NET MVC 框架调用。
/// </summary>
/// <param name="filterContext"></param>
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
} public class FilterContextInfo
{
public FilterContextInfo(ActionExecutingContext filterContext)
{
#region 获取链接中的字符
DomainName = filterContext.HttpContext.Request.Url.Authority;
AreaName = (filterContext.RouteData.DataTokens["area"] ?? "").ToString();
ControllerName = filterContext.RouteData.Values["controller"].ToString();
ActionName = filterContext.RouteData.Values["action"].ToString();
#endregion
}
/// <summary>
/// 获取域名
/// </summary>
public string DomainName { get; set; } /// <summary>
/// 获取 controllerName 名称
/// </summary>
public string AreaName { get; set; } /// <summary>
/// 获取 controllerName 名称
/// </summary>
public string ControllerName { get; set; } /// <summary>
/// 获取ACTION 名称
/// </summary>
public string ActionName { get; set; }
}
}
}

asp.net mvc 全局权限过滤器及继成权限方法的更多相关文章

  1. Asp.Net MVC<五>:过滤器

    ControllerActionInvoker在执行过程中除了利用ActionDescriptor完成对目标Action方法本身的执行外,还会执行相关过滤器(Filter).过滤器采用AOP的设计,它 ...

  2. ASP.NET MVC学习之过滤器篇(2)

    下面我们继续之前的ASP.NET MVC学习之过滤器篇(1)进行学习. 3.动作过滤器 顾名思义,这个过滤器就是在动作方法调用前与调用后响应的.我们可以在调用前更改实际调用的动作,也可以在动作调用完成 ...

  3. 在ASP.NET MVC中实现基于URL的权限控制

    本示例演示了在ASP.NET MVC中进行基于URL的权限控制,由于是基于URL进行控制的,所以只能精确到页.这种权限控制的优点是可以在已有的项目上改动极少的代码来增加权限控制功能,和项目本身的耦合度 ...

  4. 【问题】Asp.net MVC 的cshtml页面中调用JS方法传递字符串变量参数

    [问题]Asp.net MVC 的cshtml页面中调用JS方法传递字符串变量参数. [解决]直接对变量加引号,如: <button onclick="deleteProduct('@ ...

  5. asp.net MVC之 自定义过滤器(Filter) - shuaixf

    一.系统过滤器使用说明 1.OutputCache过滤器 OutputCache过滤器用于缓存你查询结果,这样可以提高用户体验,也可以减少查询次数.它有以下属性: Duration :缓存的时间, 以 ...

  6. asp.net MVC之 自定义过滤器(Filter)

    一.系统过滤器使用说明 1.OutputCache过滤器 OutputCache过滤器用于缓存你查询结果,这样可以提高用户体验,也可以减少查询次数.它有以下属性: Duration:缓存的时间,以秒为 ...

  7. ASP.NET MVC 4 (三) 过滤器

    先来看看一个例子演示过滤器有什么用: public class AdminController : Controller { // ... instance variables and constru ...

  8. ASP.NET MVC学习之过滤器篇(1)

    一.前言 继前面四篇ASP.NET MVC的随笔,我们继续向下学习.上一节我们学习了关于控制器的使用,本节我们将要学习如何使用过滤器控制用户访问页面. 二.正文 以下的示例建立在ASP.NET MVC ...

  9. Asp.Net Mvc通用后台管理系统,bootstrap+easyui+权限管理+ORM

    产品清单: 1.整站源码,非编译版,方便进行业务的二次开发 2.通用模块与用户等基础数据的数据库脚本 3.bootstrap3.3.1 AceAdmin模板源码 4.easyui1.3.5源码 5.F ...

随机推荐

  1. 20155325 Exp5 MSF基础应用

    目录 实验内容 遇到的问题 基础问题问答 老师!!!我实验三的C代码已经删除了,请求评分~~~ 实验内容 1.Windows服务渗透攻击--MS08-067 系统 虚拟机 参考博客 Windows X ...

  2. 20155330 《网络对抗》 Exp2 后门原理与实践

    20155330 <网络对抗> 实验二 后门原理与实践 基础问题回答 例举你能想到的一个后门进入到你系统中的可能方式? 在网站上下载非官方软件,所下载的软件中携带伪装过的后门程序. 例举你 ...

  3. flask前端与后端之间传递的两种数据格式:json与FormData

    json格式 双向! 前端 ==>后端:json格式 后端 ==>前端:json格式 html <!-- html部分 --> <form enctype='applic ...

  4. JavaScript快速入门-ECMAScript本地对象(RexExp)

    一.概述 RegExp 对象表示正则表达式,它是对字符串执行模式匹配的强大工具. 正则表达式是由一个字符序列形成的搜索模式. 当你在文本中搜索数据时,你可以用搜索模式来描述你要查询的内容. 正则表达式 ...

  5. UWP简单示例(一):快速合成音乐MV

    说明 本文发布时间较早,内容可能已过时.最新动态请关注 TypeScript 版本.(2019 年 3 月 注) 在线演示: 音频可视化(TypeScript) 准备 IDE:Visual Studi ...

  6. HTML 图像实例

    61.插入图像本例演示如何在网页中显示图像.图像标签(<img>)和源属性(Src)在 HTML 中,图像由 <img> 标签定义. <img> 是空标签,意思是说 ...

  7. JavaWeb项目学习教程(1) 准备阶段

    写在最前面 为什么要写一个这样的教程?作为一个软件工程专业的学生,上课老师讲得飞快,几乎都是在课后自己消化,我知道学习记录的重要性.我自己本身还有很多很多基础的东西都没有学会,比较博客园的人有很大的差 ...

  8. unity物理检测的几种方式

    (由于本人大多做2d游戏,因此以下以2d为主介绍,但是具体和3d相差不大) 在unity中有很多不同的物理检测方式,但是大致可以分为以下几种: 1.Physics2d检测系列 Physics2d.Li ...

  9. Unity之日志管理

    1. 目录结构 1. Plugins --> 存放Log4Net动态库文件 2. Scripts --> 存放写日志的脚本 3. StreamingAssets -->存放Log4N ...

  10. 从两个设计模式到前端MVC-洪宇

    引言 本文将从策略模式和观察者模式两个设计模式讲起,接着过渡到一个经典的复合模式- MVC架构,进而介绍MVC在Web上的适应-Model2架构.之后,我们将视野扩展到前端MVC,看一看前端MVC经典 ...