diff --git a/Infrastructure/Auth/CacheKey.cs b/Infrastructure/Auth/CacheKey.cs new file mode 100644 index 00000000..5018e5f3 --- /dev/null +++ b/Infrastructure/Auth/CacheKey.cs @@ -0,0 +1,20 @@ +using System.Web; + +namespace Infrastructure.Auth +{ + public class CacheKey + { + public static string SessionName = "OpenAuth"; + public static string UserSessionName = "Session_"; + private static string GetSessionId() + { + HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(SessionName); + string remoteBrowserIp = WebUtility.GetIP(); + return UserSessionName + remoteBrowserIp + ":" + cookie.Value; + } + public static string UserID + { + get { return GetSessionId(); } + } + } +} diff --git a/Infrastructure/Auth/CacheSession.cs b/Infrastructure/Auth/CacheSession.cs new file mode 100644 index 00000000..e3c46428 --- /dev/null +++ b/Infrastructure/Auth/CacheSession.cs @@ -0,0 +1,79 @@ +using Infrastructure.Cache; +using System; +using System.Web; + +namespace Infrastructure.Auth +{ + public class CacheSession + { + ICache cache = DIContainer.Resolve(); + private HttpContext context; + public CacheSession(bool IsReadOnly) + { + this.IsReadOnly = IsReadOnly; + } + public CacheSession(HttpContext context, bool IsReadOnly, TimeSpan TimeOut, ICache cacheService) + { + this.context = context; + this.IsReadOnly = IsReadOnly; + this.TimeOut = TimeOut; + } + public CacheSession(HttpContext context, bool IsReadOnly) + { + this.context = context; + this.IsReadOnly = IsReadOnly; + GetSessionId(); + if (CacheKey.UserID != null) + { + var userInfo = cache.Get(CacheKey.UserID); + } + } + //获取会话是否已经验证通过 + public bool IsAuthenticated + { + get + { + if (cache.Get(SessionId) == null) + { + return false; + } + else + { + return true; + } + } + } + + //会话唯一Id + public string SessionId + { + get + { + return GetSessionId(); + } + } + public static string SessionName = CacheKey.SessionName; + public static string UserSessionName = CacheKey.UserSessionName; + //指示会话是否为只读,true为只读 + public bool IsReadOnly { get; set; } + //超时期限 + public TimeSpan TimeOut { get; set; } + private string GetSessionId() + { + HttpCookie cookie = context.Request.Cookies.Get(SessionName); + string remoteBrowserIp = WebUtility.GetIP(); + if (cookie == null || string.IsNullOrEmpty(cookie.Value)) + { + string newSessionId = Guid.NewGuid().ToString(); + HttpCookie newCookie = new HttpCookie(SessionName, newSessionId); + newCookie.HttpOnly = IsReadOnly; + context.Response.Cookies.Add(newCookie); + return UserSessionName + remoteBrowserIp + ":" + newSessionId; + } + else + { + return UserSessionName + remoteBrowserIp + ":" + cookie.Value; + } + } + } +} diff --git a/Infrastructure/Auth/FormsAuthenticationService.cs b/Infrastructure/Auth/FormsAuthenticationService.cs new file mode 100644 index 00000000..f018f946 --- /dev/null +++ b/Infrastructure/Auth/FormsAuthenticationService.cs @@ -0,0 +1,48 @@ +using Infrastructure.Cache; +using System; +using System.Web; + +namespace Infrastructure.Auth +{ + /// + /// 身份认证服务实现(缓存可分布式部署) + /// + public class FormsAuthenticationService : IAuthenticationService + { + ICache cacheService; + CacheSession cacheSession; + HttpContext httpContext = HttpContext.Current; + //hpf 缓存相关 + public FormsAuthenticationService() + { + cacheService = DIContainer.Resolve(); + cacheSession = new CacheSession(httpContext, true); + } + /// + /// 获取当前认证的用户 + /// + /// 当前用户未通过认证则返回null + public dynamic GetAuthenticatedUser() + { + if (httpContext == null || !cacheSession.IsAuthenticated) + { + return null;//hpf未登录 + } + return cacheService.Get(cacheSession.SessionId); + } + + public void SignIn(string loginName, dynamic userInfo, TimeSpan expiration) + { + var sessionId = cacheSession.SessionId; + cacheService.Set(sessionId, userInfo, expiration); + } + + public void SignOut() + { + if (!string.IsNullOrEmpty(CacheKey.UserID)) + { + cacheService.Remove(CacheKey.UserID); + } + } + } +} diff --git a/Infrastructure/Auth/IAuthenticationService.cs b/Infrastructure/Auth/IAuthenticationService.cs new file mode 100644 index 00000000..9ef3b2dd --- /dev/null +++ b/Infrastructure/Auth/IAuthenticationService.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Auth +{ + /// + /// 用于身份认证接口 + /// + public interface IAuthenticationService + { + /// + /// 登录 + /// + /// 登录名 + /// 与登录名相关的用户信息 + /// 登录Cookie的过期时间,单位:分钟。 + void SignIn(string loginName, dynamic userInfo, TimeSpan expiration); + /// + /// 注销 + /// + void SignOut(); + /// + /// 获取当前登录的用户 + /// + /// 当前用户未通过认证则返回null + dynamic GetAuthenticatedUser(); + } +} diff --git a/Infrastructure/Auth/UserContext.cs b/Infrastructure/Auth/UserContext.cs new file mode 100644 index 00000000..7e623768 --- /dev/null +++ b/Infrastructure/Auth/UserContext.cs @@ -0,0 +1,20 @@ +namespace Infrastructure.Auth +{ + /// + /// 当前登录用户相关 + /// + public class UserContext + { + public static dynamic CurrentUser + { + get + { + IAuthenticationService authenticationService = DIContainer.ResolvePerHttpRequest(); + var currentUser = authenticationService.GetAuthenticatedUser(); + if (currentUser != null) + return currentUser; + return null; + } + } + } +} diff --git a/Infrastructure/Cache/ICache.cs b/Infrastructure/Cache/ICache.cs new file mode 100644 index 00000000..7ade6edb --- /dev/null +++ b/Infrastructure/Cache/ICache.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Cache +{ + public interface ICache + { + /// + /// 加入缓存项 + /// + /// 缓存项标识 + /// 缓存项 + /// 缓存失效时间 + void Add(string key, object value, TimeSpan timeSpan); + /// + /// 加入依赖物理文件的缓存项 + /// + /// 缓存项标识 + /// 缓存项 + /// 依赖的文件全路径 + void AddWithFileDependency(string key, object value, string fullFileNameOfFileDependency); + /// + /// 获取缓存项 + /// + /// + /// + object Get(string cacheKey); + T Get(string cacheKey) where T : class; + void Remove(string cacheKey); + /// + /// 如果不存在缓存项则添加,否则更新(相对过期) + /// + /// 缓存项标识 + /// 缓存项 + /// 缓存失效时间 + void Set(string key, object value, TimeSpan timeSpan); + /// + /// 设置绝对过期时间 + /// + /// 缓存项标识 + /// 缓存项 + /// 缓存失效时间 + void SetAbsoluteExpiration(string key, object value, TimeSpan timeSpan); + } +} diff --git a/Infrastructure/Cache/RuntimeMemoryCache.cs b/Infrastructure/Cache/RuntimeMemoryCache.cs new file mode 100644 index 00000000..e067c237 --- /dev/null +++ b/Infrastructure/Cache/RuntimeMemoryCache.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Caching; +namespace Infrastructure.Cache +{ + public class RuntimeMemoryCache : ICache + { + private readonly MemoryCache memoryCache = MemoryCache.Default; + /// + /// 加入缓存项(绝对过期时间) + /// + /// 缓存项标识 + /// 缓存项 + /// 缓存失效时间 + public void Add(string key, object value, TimeSpan timeSpan) + { + if (!string.IsNullOrEmpty(key) && (value != null)) + { + CacheItemPolicy cip = new CacheItemPolicy() + { + AbsoluteExpiration = DateTime.Now.Add(timeSpan) + }; + this.memoryCache.Add(key, value, cip, null); + } + } + /// + /// 加入依赖物理文件的缓存项 + /// + /// 缓存项标识 + /// 缓存项 + /// 依赖的文件全路径 + public void AddWithFileDependency(string key, object value, string fullFileNameOfFileDependency) + { + if (!string.IsNullOrEmpty(key) && (value != null)) + { + CacheItemPolicy policy = new CacheItemPolicy + { + AbsoluteExpiration = DateTimeOffset.Now.AddMonths(1) + }; + policy.ChangeMonitors.Add(new HostFileChangeMonitor(new List { fullFileNameOfFileDependency })); + this.memoryCache.Add(key, value, policy, null); + } + } + + public object Get(string cacheKey) + { + return this.memoryCache[cacheKey]; + } + + public T Get(string cacheKey) where T : class + { + object obj = this.Get(cacheKey); + if (obj != null) + { + return (obj as T); + } + return default(T); + } + + public void Remove(string cacheKey) + { + this.memoryCache.Remove(cacheKey, null); + } + /// + /// 如果不存在缓存项则添加,否则更新(相对过期) + /// + /// 缓存项标识 + /// 缓存项 + /// 缓存失效时间 + public void Set(string key, object value, TimeSpan timeSpan) + { + CacheItemPolicy cip = new CacheItemPolicy() + { + SlidingExpiration = timeSpan, + }; + this.memoryCache.Set(key, value, cip, null); + } + /// + /// 设置绝对过期时间 + /// + /// 缓存项标识 + /// 缓存项 + /// 缓存失效时间 + public void SetAbsoluteExpiration(string key, object value, TimeSpan timeSpan) + { + CacheItemPolicy cip = new CacheItemPolicy() + { + AbsoluteExpiration = DateTime.Now.Add(timeSpan), + }; + this.memoryCache.Set(key, value, cip, null); + } + } +} diff --git a/Infrastructure/DIContainer.cs b/Infrastructure/DIContainer.cs new file mode 100644 index 00000000..f3673c40 --- /dev/null +++ b/Infrastructure/DIContainer.cs @@ -0,0 +1,75 @@ +using Autofac; +using Autofac.Core; +using System.Web.Mvc; + +namespace Infrastructure +{ + /// + /// 依赖注入 + /// + public class DIContainer + { + private static IContainer iContainer; + public static void RegisterContainer(IContainer container) + { + iContainer = container; + } + /// + /// 按类型获取组件 + /// + /// + /// + public static Tservice Resolve() + { + return ResolutionExtensions.Resolve(iContainer); + } + /// + /// 按参数获取组件 + /// + /// + /// + /// + public static Tservice Resolve(params Parameter[] parameters) + { + return ResolutionExtensions.Resolve(iContainer, parameters); + } + /// + /// 按key获取组件 + /// + /// 组件类型 + /// 枚举类型的key + /// 返回获取的组件 + public static Tservice ResolveKeyed(object serviceKey) + { + return ResolutionExtensions.ResolveKeyed(iContainer, serviceKey); + } + /// + /// 按名称获取组件 + /// + /// + /// + /// + public static Tservice ResolveNamed(string serviceName) + { + return ResolutionExtensions.ResolveNamed(iContainer, serviceName); + } + /// + /// 获取InstancePerHttpRequest的组件 mvc + /// + /// + /// + public static Tservice ResolvePerHttpRequest() + { + IDependencyResolver current = DependencyResolver.Current; + if (current != null) + { + Tservice service = (Tservice)current.GetService(typeof(Tservice)); + if (service != null) + { + return service; + } + } + return ResolutionExtensions.Resolve(iContainer); + } + } +} diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj index 278fe132..477b5984 100644 --- a/Infrastructure/Infrastructure.csproj +++ b/Infrastructure/Infrastructure.csproj @@ -32,6 +32,10 @@ 4 + + ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll + True + ..\packages\AutoMapper.4.1.0\lib\net45\AutoMapper.dll @@ -58,7 +62,16 @@ ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll True + + + ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll + True + + + ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll + True + @@ -66,11 +79,19 @@ + + + + + + + + @@ -79,6 +100,11 @@ + + + + + @@ -86,6 +112,7 @@ + diff --git a/Infrastructure/MVC/AuthenticationAttribute.cs b/Infrastructure/MVC/AuthenticationAttribute.cs new file mode 100644 index 00000000..fe420cc8 --- /dev/null +++ b/Infrastructure/MVC/AuthenticationAttribute.cs @@ -0,0 +1,12 @@ +using System.Web; +using System.Web.Mvc; + +namespace Infrastructure.MVC +{ + /// + /// 登录验证 + /// + public class AuthenticationAttribute: AuthorizeAttribute + { + } +} diff --git a/Infrastructure/MVC/JsonExceptionAttribute.cs b/Infrastructure/MVC/JsonExceptionAttribute.cs new file mode 100644 index 00000000..36f91484 --- /dev/null +++ b/Infrastructure/MVC/JsonExceptionAttribute.cs @@ -0,0 +1,24 @@ +using System; +using System.Web.Mvc; + +namespace Infrastructure.MVC +{ + /// + /// 加入action级ajax请求发生500内部错误时返回给浏览器json提示 + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class JsonExceptionAttribute : HandleErrorAttribute + { + public override void OnException(ExceptionContext filterContext) + { + if (!filterContext.ExceptionHandled) + { + //返回异常json + filterContext.Result = new JsonResult + { + Data = new UiResponse { statusCode = "300", message = filterContext.Exception.Message } + }; + } + } + } +} diff --git a/Infrastructure/MVC/LogExceptionAttribute.cs b/Infrastructure/MVC/LogExceptionAttribute.cs new file mode 100644 index 00000000..c308761b --- /dev/null +++ b/Infrastructure/MVC/LogExceptionAttribute.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; + +namespace Infrastructure.MVC +{ + /// + /// 加入全局异常处理500内部错误 + /// + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] + public class LogExceptionAttribute : HandleErrorAttribute + { + public override void OnException(ExceptionContext filterContext) + { + if (!filterContext.ExceptionHandled) + { + string controllerName = filterContext.RouteData.Values["controller"].ToString(); + string actionName = filterContext.RouteData.Values["action"].ToString(); + string msgTemp = WebUtility.GetIP() + "在执行controller" + controllerName + "的" + actionName + "时产生异常:" + filterContext.Exception.Message; + //hpf此处写入异常日志 + LogHelper.Fatal(msgTemp); + } + if (filterContext.Result is JsonResult) + { + filterContext.ExceptionHandled = true;//异常已处理 + } + else + { + //通过base返回系统默认异常处理上向错误页面跳转 + base.OnException(filterContext); + } + } + } +} diff --git a/Infrastructure/MVC/NoFilterAttribute.cs b/Infrastructure/MVC/NoFilterAttribute.cs new file mode 100644 index 00000000..f5d6141e --- /dev/null +++ b/Infrastructure/MVC/NoFilterAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace Infrastructure.MVC +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public class NoFilterAttribute : Attribute + { + public NoFilterAttribute() { } + } +} diff --git a/Infrastructure/MVC/UiResponse.cs b/Infrastructure/MVC/UiResponse.cs new file mode 100644 index 00000000..085d5bf9 --- /dev/null +++ b/Infrastructure/MVC/UiResponse.cs @@ -0,0 +1,44 @@ + +namespace Infrastructure.MVC +{ + /// + /// 前端框架ajax错误返回 + /// + public class UiResponse + { + public string statusCode + { + get; set; + + } + + public string message + { + get; set; + + } + + public string tabid + { + get; set; + + } + + public bool closeCurrent + { + get; set; + + } + public string forward { get; set; } + public string forwardConfirm { get; set; } + public UiResponse() + { + statusCode = "200"; + message = "操作成功"; + tabid = ""; + closeCurrent = false; + forward = ""; + forwardConfirm = ""; + } + } +} diff --git a/Infrastructure/WebUtility.cs b/Infrastructure/WebUtility.cs new file mode 100644 index 00000000..2c811feb --- /dev/null +++ b/Infrastructure/WebUtility.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace Infrastructure +{ + public static class WebUtility + { + /// + /// 获取IP地址 + /// + /// 返回获取的ip地址 + public static string GetIP() + { + return GetIP(HttpContext.Current); + } + /// + /// 透过代理获取真实IP + /// + /// HttpContext + /// 返回获取的ip地址 + public static string GetIP(HttpContext httpContext) + { + string userHostAddress = string.Empty; + if (httpContext != null) + { + userHostAddress = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; + if (string.IsNullOrEmpty(userHostAddress)) + { + userHostAddress = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"]; + } + if (string.IsNullOrEmpty(userHostAddress)) + { + userHostAddress = HttpContext.Current.Request.UserHostAddress; + } + } + return userHostAddress; + } + } +} diff --git a/Infrastructure/packages.config b/Infrastructure/packages.config index 51f427f2..a82dceca 100644 --- a/Infrastructure/packages.config +++ b/Infrastructure/packages.config @@ -1,9 +1,9 @@  + - - + \ No newline at end of file diff --git a/OpenAuth.Mvc/Content/BJUI/js/bjui-all.js b/OpenAuth.Mvc/Content/BJUI/js/bjui-all.js index 66c77eed..64eb4692 100644 --- a/OpenAuth.Mvc/Content/BJUI/js/bjui-all.js +++ b/OpenAuth.Mvc/Content/BJUI/js/bjui-all.js @@ -132,7 +132,7 @@ $('#bjui-container').height(iContentH) $('#bjui-navtab').width(iContentW) $('#bjui-leftside, #bjui-sidebar, #bjui-sidebar-s, #bjui-splitBar, #bjui-splitBarProxy').css({height:'100%'}) - $('#bjui-navtab .tabsPageContent').height(iContentH - navtabH) + $('#bjui-navtab .tabsPageContent').height(iContentH - navtabH - 1)//下边框遮盖问题 /* fixed pageFooter */ setTimeout(function() {