前言:在应用于集团版客户或SAAS平台服务的业务系统中,流程管理系统需要支持多用户组织模型。其中包括角色数据、流程定义数据和流程实例数据的多用户标识绑定。本文旨在全面描述如何基于SlickOne敏捷开发框架实现上述基础服务功能,形成一个完整的支持多用户查看和维护各自流程数据的管理后台系统。
1. 基础数据的多用户标识
1.1 多用户(公司)数据表
数据库表SysCompany用来存储多用户/多租户的基本信息,字段CompanyID 用来标识后期业务数据的所有者。
1.2 角色/用户数据表
角色用户表统一增加CompanyID字段,用来确定角色和用户属于具体的那一个用户或租户。
1.3. 流程定义数据的多用户标识
数据库表WfProcess增加CompanyID字段,用来标识流程定义属于那一个用户或租户。
1.4. 流程实例数据的多用户标识
所有的流程实例数据,统一增加CompanyID字段,用了标识流程实例数据的拥有者范围。
2. 多站点类型的SSO功能实现
多站点SSO单点登录功能的实现,便于统一整合不同子系统的数据管理和维护;尤其对于平台级别的软件产品,多个子系统的是需要经常频繁操作访问的。所以,一次登录,再次免验证,就非常方便简捷。
2.1 系统环境配置
1) Form 认证方式配置
<authentication mode="Forms"> <forms loginUrl="http://localhost/sfadmin/Account/Login" protection="All" timeout="240" name=".AuthCookie" /> authentication>
2) Session 状态存储配置
<sessionState mode="InProc" customProvider="DefaultSessionProvider" timeout="480"> <providers> <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" /> providers> sessionState>
3) MachineKey 配置
<machineKey validationKey="zsdgfdg3FF1B0F88DDF585BA5D35E7BC87E3F0AB47FBBEBD12240DD3BEA2BEAEC4ABA215478658ugfjnhgfnmj3F22AD27E8FAD77DCFEE306219691434908D193A17C1FC8DCE51B71A4AE54920" decryptionKey="ECB6A3AF9ABBF3F16E80685ED68DC74B0B13CCEE538EBBA97D0B893139683B3B" validation="SHA256" decryption="AES" />
4) 登录页面重定向地址
<add key="FormAuthenticationRedirectUrl" value="http://localhost/sfadmin/Account/Login"/>
2.2 Session 对象操作和访问
用于服务端用户对象的身份信息存储,包括用户ID标识,用户名称,公司ID标识,票据信息和权限数据等。
////// 获取登录用户ID /// /// /// public int GetLogonUserID() { return (int)Get(WEB_LOGON_USER_ID); } public int GetLogonCompanyID() { return (int)Get(WEB_LOGON_COMPANY_ID); } /// /// 获取登录用户Session的GUID /// /// /// public string GetLogonUserSessionGUID() { return Get(WEB_LOGON_SESSION_GUID).ToString(); } /// /// 获取登录用户票据 /// /// /// public string GetLogonUserTicket() { var obj = Get(WEB_LOGON_USER_TICKET); var ticket = obj != null ? obj.ToString() : string.Empty; return ticket; }
2.3 Cookie 对象操作和访问
前端JS脚本访问用户的特定信息,通过Cookie对象获取来实现,大致代码如下:
function getWebLogonUserCookie() { var name = "SlickOneWebLogonUserDataCookie"; var cookie = getCookie(name); if (cookie !== undefined) { var userAccount = $.parseJSON(cookie); return userAccount; } else { return null; } } lsm.getWebLogonUserID = function () { var userAccount = getWebLogonUserCookie(); var userID = userAccount.UserID; return userID; } lsm.getWebLogonCompanyID = function () { var userAccount = getWebLogonUserCookie(); if (userAccount !== null) { var companyID = userAccount.CompanyID; return companyID; } else { return ""; } }
2.4 登录验证后的票据存储
用户登录之后,需要将其基本身份信息和关联的角色或权限数据存储下来。而且作为前后端分离的系统,服务端需要使用这些票据数据,前端也需要通过Cookie对象访问用户信息,作为权限控制的审核来源。
//create form ticket FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, loginName, DateTime.Now, DateTime.Now.AddMinutes(240), true, userDataContent, FormsAuthentication.FormsCookiePath); string ticString = FormsAuthentication.Encrypt(ticket); //write cookies in response //SetAuthCookie mark identity status true HttpContext.Current.Response.Cookies.Add(new HttpCookie("SlickOneWebCookie", ticString));
3. Mvc页面及WebAPI安全访问
3.1 Mvc页面授权访问
页面控制器统一继承于页面基类,基类中重载方法OnActionExecuting(),读取用户身份信息,并存储到Session对象,如果是非授权用户,则跳转到登录页面。代码示例如下:
////// Authentication Verify When Action Executing /// /// protected override void OnActionExecuting(ActionExecutingContext filterContext) { var attr = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true); bool isAnonymous = attr.Any(a => a is AllowAnonymousAttribute); if (isAnonymous == false) { var session = filterContext.HttpContext.Session; this.SessionManager.SetSession(session); var user = this.SessionManager.GetLogonUser() as WebLogonUser; if (user == null) { var webCookie = base.Request.Cookies["SlickOneWebCookie"]; if (webCookie != null && !string.IsNullOrEmpty(webCookie.Value)) { var encryptTicket = webCookie.Value; SaveUserSession(encryptTicket); } else { //Not a Valid Logon User, Need To Be Login Agagin var formRedirectUrl = WebConfigurationManager.AppSettings["FormAuthenticationRedirectUrl"].ToString(); string url = string.Format("{0}?ReturnUrl={1}", formRedirectUrl, Request.RawUrl); filterContext.HttpContext.Response.Redirect(url, true); } } } base.OnActionExecuting(filterContext); }
3.2 WebAPI 接口安全访问
WebAPI控制器增加属性过滤器,用于验证是否是授权访问的接口,其中需要从Cookie中读取票据信息,验证审核用户是否是合法授权用户。
////// check authorizaton information when action executing /// /// public override void OnActionExecuting(HttpActionContext actionContext) { //get authentication cookie from request var authCookie = actionContext.Request.GetCookie("SlickOneWebCookie"); if (!String.IsNullOrEmpty(authCookie)) { //decrypted user ticket information if (ValidateUserTicket(authCookie)) base.OnActionExecuting(actionContext); else actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized); } else { //verify webapi security setting bool isRquired = (WebConfigurationManager.AppSettings["WebApiSecurityEnabled"].ToString() == "true"); if (isRquired) { //check anonymous attribute var attr = actionContext.ActionDescriptor.GetCustomAttributes ().OfType (); bool isAnonymous = attr.Any(a => a is AllowAnonymousAttribute); if (isAnonymous) base.OnActionExecuting(actionContext); else actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized); } else { base.OnActionExecuting(actionContext); } } }
4. 主界面操作说明
主界面是整个后台数据维护的入口页面,集成了用户基础数据、流程数据、表单数据和其它设置页面。其中流程定义,表单定义都链接到不同的WEB应用程序地址,这些WEB应用程序统一实现SSO要求的FORM认证,统一登录地址等特性。保证一次登录,再次免验证就能访问各子系统的简捷操作。
5. 总结
SlickOne敏捷框架的示例项目,主要包括了基础数据的维护,业务系统集成访问,SSO单点登录实现,MVC页面安全和WebAPI安全访问等功能特性。作为企业级应用系统的开发,可以完全担当软件团队的技术统一框架解决方案。在后期的版本中,依然考虑企业用户的需求,增加和构建功能模块,做到框架软件的可扩展和二次开发。
6. DEMO
- 演示地址:http://gc.slickflow.com/sfadmin/
- 用户名和密码:admin/123456
- 流程设计器:http://gc.slickflow.com/sfd/
- 表单设计器:http://gc.slickflow.com/smd/
7. 社区版源代码
SlickOne项目开源地址:
http://github.com/besley/slickone
8. 企业版授权说明
1) Demo仅作为功能演示使用,如需获取产品完整源代码和开发文档,请申请企业版商业授权。
2) QQ群:151650479
3) EMail: [email protected]
9. 参考
1. SlickOne 敏捷开发框架介绍(一)