C# 單點登錄 MVC



C# 單點登錄 MVC

實現sso系統的主要難點:

1:不能直接訪問數據庫,有安全隱患,而且還容易亂套。
2:多個系統需要進行單點登錄,邏輯需要嚴謹,能支持N多系統、而不只是少數幾個系統。
3:代碼不能過於複雜,需要簡潔,靈活支持本地部署,單點部署,集羣部署,相同的代碼可以通過部署配置靈活實現服務段(sso)、本地段(子網站)功能。
4:多系統的權限也可以靈活判斷,不能訪問數據庫,需要進行遠程服務調用,而且還需要對外部系統能提供調用接口。
5:需要有一定的安全性,能防止注入攻擊等等。
6:權限定義需要非常靈活,可以定義10個權限,也可以定義100個權限,可以根據url進行權限判斷,同時可以是n多個業務系統。
7:需要穩定,2周內就搞定,1周後交付測試,而不是需要研究2個月才能搞定,或者研究2年。
 
單點登錄常見需求:
   跨應用、跨域、跨機器的單點登錄。
1、流程:
   a) 用戶直接訪問門戶url,登錄成功後跳轉到默認應用的url
程序裏需要引用2個Dll,通用權限管理系統組件的,這2個dll主要實現權限判斷,登錄功能等。
下面是登錄頁面的位置
這裏是MVC默認的登錄頁面,
上圖是需要登錄的功能定義部分。
在 MVC 里加上 [NeedAuthorize] 就可以默認要求登錄了。不登錄不允許訪問了。

   b) 用戶直接訪問應用url,若未登錄,跳轉到登錄頁面的url,完成登錄後,跳轉回應用url
上圖是跳轉的處理,若沒指定需要跳轉的頁面,那就默認回到首頁,若有指定的 returnUrl,就跳轉回指定的頁面裏。
 

   c) 用戶已經登錄,用戶直接訪問應用的URL,若該用戶無權限,跳轉到指定的url
沒有訪問權限的用戶會被重定向到沒權限訪問的頁面
 
系統中的注意事項,先看下圖
NeedUrlAuthorizedAttribute 是需要進行url驗證用的屬性,在需要控制的Controller上加上,例如

判斷是否有url權限的調用方法如下:
  [NeedAuthorize]   需要進行登錄(這個可以省略)
  [NeedUrlAuthorized] 需要進行Url驗證 (首先會要求需要登錄)
  [HttpPost]
  public ActionResult User(Friend.Models.User model)
 {
 }
 
PermissionWebService 是進行權限驗證中心遠程進行權限判斷的WebService,是引用了
需要在配置文件裏進行
配置
SystemCode, 主要是來識別,是哪個子系統的權限?因爲當前可能10多個網站,那就是有10多個子系統,那就每個子系統需要有一個唯一識別的編號。
 
那如何設置某個子系統的用的url權限?看下圖(用戶權限配置參考,子系統設置菜單、菜單編號、菜單url設置):
 
 
若沒登錄,就會提示先登錄
 
登錄後,若沒權限訪問這個頁面,就會提示沒權限訪問。
可以點網頁下面的 有情連接進行測試,不用在url里人工輸入。
 
   d) 用戶已經登錄,用戶直接訪問用戶有權限的應用url,通過
與上面的需求一致,有權限的自然能通過調用。
有URL訪問權限的,可以顯示頁面效果如下
 
   E) 跨域的單點登錄、權限判斷需要注意的配置部分
需要在其他需要接入的 其他應用裏需要加上這3個配置,認證主服務器上不要進行這個配置,需要刪除掉這幾行配置信息纔可以。

2、驗證碼的生成,封裝成一個插件,方便各種加強版本的驗證需要。
這個是一個通用的嚴正碼設置與驗證碼校驗的類,可以按自己的需要進行修改。
 
複製代碼
//-----------------------------------------------------------------
// All Rights Reserved , Copyright (C) 2013 , Hairihan TECH, Ltd. 
//-----------------------------------------------------------------

using System;
using System.Data;
using System.Data.Common;
using System.Collections.Generic;

namespace DotNet.Business
{
    using DotNet.Utilities;

    /// <summary>
    /// BaseUserManager
    /// 用戶管理
    /// 
    /// 修改紀錄
    /// 
    ///        2013.08.17 版本:1.0 JiRiGaLa    用戶登錄後才設置驗證碼、獲取驗證碼等。
    /// 
    /// <author>
    ///        <name>JiRiGaLa</name>
    ///        <date>2011.10.17</date>
    /// </author> 
    /// </summary>
    public partial class BaseUserManager : BaseManager
    {
        #region public int SetVerificationCode(string userId, string verificationCode) 設置驗證碼
        /// <summary>
        /// 設置驗證碼
        /// </summary>
        /// <param name="userId">用戶主鍵</param>
        /// <param name="verificationCode">驗證碼</param>
        /// <returns>影響行數</returns>
        public int SetVerificationCode(string userId, string verificationCode)
        {
            int result = 0;
            if (string.IsNullOrEmpty(userId) && this.UserInfo != null)
            {
                userId = this.UserInfo.Id;
            }
            string sqlQuery = string.Empty;
            sqlQuery = " UPDATE " + BaseUserLogOnEntity.TableName
                     + "    SET " + BaseUserLogOnEntity.FieldVerificationCode + " = " + DbHelper.GetParameter("VerificationCode")
                     + "  WHERE " + BaseUserLogOnEntity.FieldId + " = " + DbHelper.GetParameter("UserId");

            List<IDbDataParameter> dbParameters = new List<IDbDataParameter>();
            dbParameters.Add(DbHelper.MakeParameter("VerificationCode", verificationCode));
            dbParameters.Add(DbHelper.MakeParameter("UserId", userId));
            result = DbHelper.ExecuteNonQuery(sqlQuery, dbParameters.ToArray());
            return result;
        }
        #endregion

        #region public bool Verify(string userId, string verificationCode)
        /// <summary>
        /// 驗證,驗證碼是否正確
        /// </summary>
        /// <param name="userId">用戶主鍵</param>
        /// <param name="verificationCode">驗證碼</param>
        /// <returns></returns>
        public bool Verify(string userId, string verificationCode)
        {
            bool result = false;
            string sqlQuery = string.Empty;
            // 最後一次登錄時間
            sqlQuery = " SELECT COUNT(1)"
                     + "   FROM " + BaseUserLogOnEntity.TableName
                     + "  WHERE " + BaseUserLogOnEntity.FieldVerificationCode + " = " + DbHelper.GetParameter("VerificationCode")
                     + "        AND " + BaseUserLogOnEntity.FieldId + " = " + DbHelper.GetParameter("UserId");

            List<IDbDataParameter> dbParameters = new List<IDbDataParameter>();
            dbParameters.Add(DbHelper.MakeParameter("VerificationCode", verificationCode));
            dbParameters.Add(DbHelper.MakeParameter("UserId", userId));
            object exist = DbHelper.ExecuteScalar(sqlQuery, dbParameters.ToArray());
            if (exist != null)
            {
                if (BaseSystemInfo.OnLineLimit <= int.Parse(exist.ToString()))
                {
                    result = true;
                }
            }
            return result;
        }
        #endregion
    }
}
複製代碼

 


3、提供遠程訪問接口:用戶信息的訪問;權限信息的訪問。訪問方式可以是Webservice的方式,封裝成一個訪問類,方便別人調用。

PermissionService.asmx 權限的WebService中有方法可以獲取用戶的權限,權限主要注意
1):要判斷哪個子系統的權限?
2):每個權限都有一個不重複的編號來識別的。
複製代碼
//-----------------------------------------------------------------------
// <copyright file="FriendFansManager.Auto.cs" company="Hairihan">
//     Copyright (c) 2013 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using DotNet.Business;

namespace Friend
{
    /// <remarks>
    /// PermissionWebService
    /// 權限檢查的WebService
    /// 
    /// 修改紀錄
    ///
    /// 2013.08.17 版本:1.0 JiRiGaLa 更新審覈意見。
    ///
    /// 版本:1.0
    /// 
    /// <author>
    ///        <name>JiRiGaLa</name>
    ///        <date>2013.08.17</date>
    /// </author> 
    /// </remarks>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // 若要允許使用 ASP.NET AJAX 從腳本中調用此 Web 服務,請取消對下行的註釋。
    // [System.Web.Script.Services.ScriptService]
    public class PermissionService : System.Web.Services.WebService
    {
        // 如果當前用戶沒登錄後臺的權限
        DotNet.Business.PermissionService permissionService = new DotNet.Business.PermissionService();

        // 如果當前用戶登錄
        // DotNet.Utilities.BaseUserInfo userInfo = Utilities.CheckCookie(HttpContext.Current.Request);

        public PermissionService()
        {
            /*
            if (userInfo != null && !string.IsNullOrEmpty(userInfo.Code))
            {
                if (!string.IsNullOrEmpty(permissionItemCode))
                {
                    bool permissionAdmin = permissionService.IsAuthorizedByUser(userInfo, userInfo.Id, permissionItemCode, string.Empty);
                    if (!permissionAdmin)
                    {
                        throw new Exception("沒有權限訪問。");
                    }
                }
            }
            else
            {
                throw new Exception("沒有權限訪問。");
            }
            */
        }


        #region public bool IsUserInRole(string systemCode, string userId, string roleCode)
        /// <summary>
        /// 用戶是否在某個角色裏
        /// </summary>
        /// <param name="systemCode">子系統編號</param>
        /// <param name="userId">用戶主鍵</param>
        /// <param name="roleCode">角色編號</param>
        /// <returns>是否在某個角色裏</returns>
        [WebMethod]
        public bool IsUserInRole(string systemCode, string userId, string roleCode)
        {
            // 需要創建個用戶對象
            DotNet.Utilities.BaseUserInfo userInfo = new DotNet.Utilities.BaseUserInfo();
            // 若沒指定是哪個子系統,默認就是基礎系統
            if (string.IsNullOrEmpty(systemCode))
            {
                systemCode = "Base";
            }
            userInfo.SystemCode = systemCode;
            // 判斷用戶是否在指定的角色裏?
            BaseUserManager userManager = new BaseUserManager(userInfo);
            return userManager.IsInRoleByCode(userId, roleCode);
        }
        #endregion


        #region public bool IsAuthorized(string systemCode, string userId, string permissionItemCode)
        /// <summary>
        /// 對某個模塊、操作是否有權限?
        /// </summary>
        /// <param name="systemCode">子系統編號</param>
        /// <param name="userId">用戶主鍵</param>
        /// <param name="permissionItemCode">操作權限編號、模塊編號</param>
        /// <returns>是否擁有操作權限</returns>
        [WebMethod]
        public bool IsAuthorized(string systemCode, string userId, string permissionItemCode)
        {
            bool returnValue = false;
            // 需要創建個用戶對象
            DotNet.Utilities.BaseUserInfo userInfo = new DotNet.Utilities.BaseUserInfo();
            // 若沒指定是哪個子系統,默認就是基礎系統
            if (string.IsNullOrEmpty(systemCode))
            {
                systemCode = "Base";
            }
            userInfo.SystemCode = systemCode;
            // 實時從數據庫判斷權限的調用方法
            returnValue = permissionService.IsAuthorizedByUser(userInfo, userId, permissionItemCode, string.Empty);
            return returnValue;
        }
        #endregion


        #region public bool IsUrlAuthorized(string systemCode, string userId, string url)
        /// <summary>
        /// 對某個模塊、操作是否有權限?
        /// </summary>
        /// <param name="systemCode">子系統編號</param>
        /// <param name="userId">用戶主鍵</param>
        /// <param name="url">按網址授權</param>
        /// <returns>是否擁有操作權限</returns>
        [WebMethod]
        public bool IsUrlAuthorized(string systemCode, string userId, string url)
        {
            bool returnValue = false;
            // 需要創建個用戶對象
            DotNet.Utilities.BaseUserInfo userInfo = new DotNet.Utilities.BaseUserInfo();
            // 若沒指定是哪個子系統,默認就是基礎系統
            if (string.IsNullOrEmpty(systemCode))
            {
                systemCode = "Base";
            }
            userInfo.SystemCode = systemCode;
            // 實時從數據庫判斷權限的調用方法
            returnValue = permissionService.IsUrlAuthorizedByUser(userInfo, userId, url);
            return returnValue;
        }
        #endregion


        #region public string[] GetUserPermissions(string systemCode, string userId)
        /// <summary>
        /// 獲取用戶的所有權限列表
        /// </summary>
        /// <param name="systemCode">子系統編號</param>
        /// <param name="userId">用戶主鍵</param>
        /// <returns>權限編號數組</returns>
        [WebMethod]
        public string[] GetUserPermissions(string systemCode, string userId)
        {
            List<string> permissions = new List<string>();
            // 需要創建個用戶對象
            DotNet.Utilities.BaseUserInfo userInfo = new DotNet.Utilities.BaseUserInfo();
            // 若沒指定是哪個子系統,默認就是基礎系統
            if (string.IsNullOrEmpty(systemCode))
            {
                systemCode = "Base";
            }
            userInfo.SystemCode = systemCode;
            // 獲取用戶的所有權限列表
            List<BaseModuleEntity> entityList = permissionService.GetModuleListByUser(userInfo, userId);
            foreach (var entity in entityList)
            {
                // 權限編號
                // entity.Code;
                permissions.Add(entity.Code);
                // 能訪問的url列表
                // entity.NavigateUrl;
                // entity.FullName;
            }
            return permissions.ToArray();
        }
        #endregion
    }
}
複製代碼

 

 

A: SSO服務器端配置說明

    1: 附加數據庫,把sql2008數據庫配置好,附加DotNet.CommonV3.9\DotNet.DataBase\SQL2008\UserCenter39

    2: 配置  DotNet.Common\Friend, MVC的單點登錄程序,配置數據庫連接Web.config中的UserCenterDbConnection的數據庫連接。

    3: 刪除單點登錄的SSO,SSOVerify,SSOPermissionService項目,從Web.config中。

    進行以上3個步驟,mvc 的 SSO 服務器端就配置好了,在iis裏設置好MVC網站就可以了。

 

B: SSO 客戶端的配置說明

   1:添加2個dll的引用,DotNet.Business、 DotNet.Utilities,dll在 DotNet.Common\Friend\External 目錄下。

   2:Controller 需要加 [NeedAuthorize] 進行單點登錄控制, [NeedUrlAuthorized]進行ul 權限限制。

   3:Web.config 中加 SSO,SSOVerify,SSOPermissionService 的配置。

   4:添加 PermissionWebService 引用,就是需要引用 上面裏的單點登錄 WebService。

   5:Global.asax 中需要寫一下 Application_Start() 中的代碼複製過去。

 

 
目前通用權限管理系統組件完全滿足以上需求,方便快速開發各種 .Net 應用軟件。
 
主要需要整理的部分如下列表中的問題
01:MVC 單點登錄需求
02:MVC 數據庫連接的配置。
03:MVC 用戶名密碼登錄,有錯誤時需要有錯誤提示信息。
04:MVC 裏保存密碼的方式,Cokies 保存測試。
05:MVC cookies 保存的時間長度設置,是否啓用cookies。
06:MVC 若有登錄自動跳轉地址的方式。
07:MVC 用OpenId(Key)登錄的方式,登錄跳轉的優化。
08:MVC 多系統支持單點登錄的配置注意事項。
09:MVC 權限判斷的例子。
10:MVC URL 權限判斷的例子。
11:MVC 退出功能的深入優化,能退出子系統也可以退出主系統。
 
將權限管理、工作流管理做到我能力的極致,一個人只能做好那麼很少的幾件事情。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章