单点登陆3

实现sso系统的主要难点:
1:不能直接访问数据库,有安全隐患,并且还容易乱套。
2:多个系统须要进行单点登陆,逻辑须要严谨,能支持N多系统、而不仅是少数几个系统。
3:代码不能过于复杂,须要简洁,灵活支持本地部署,单点部署,集群部署,相同的代码能够经过部署配置灵活实现服务段(sso)、本地段(子网站)功能。
4:多系统的权限也能够灵活判断,不能访问数据库,须要进行远程服务调用,并且还须要对外部系统能提供调用接口。
5:须要有必定的安全性,能防止注入攻击等等。
6:权限定义须要很是灵活,能够定义10个权限,也能够定义100个权限,能够根据url进行权限判断,同时能够是n多个业务系统。
7:须要稳定,2周内就搞定,1周后交付测试,而不是须要研究2个月才能搞定,或者研究2年。
 
单点登陆常见需求:
   跨应用、跨域、跨机器的单点登陆。
一、流程:
   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,是引用了
http://www.sso.com/PermissionService.asmx 这个WebService。
须要在配置文件里进行
配置
SystemCode, 主要是来识别,是哪一个子系统的权限?由于当前可能10多个网站,那就是有10多个子系统,那就每一个子系统须要有一个惟一识别的编号。
 
那如何设置某个子系统的用的url权限?看下图(用户权限配置参考,子系统设置菜单、菜单编号、菜单url设置):
 
 
若没登陆,就会提示先登陆
 
登陆后,若没权限访问这个页面,就会提示没权限访问。
能够点网页下面的 有情链接进行测试,不用在url里人工输入。
 
   d) 用户已经登陆,用户直接访问用户有权限的应用url,经过。
与上面的需求一致,有权限的天然能经过调用。
有URL访问权限的,能够显示页面效果以下
 
   E) 跨域的单点登陆、权限判断须要注意的配置部分。
须要在其余须要接入的 其余应用里须要加上这3个配置,认证主服务器上不要进行这个配置,须要删除掉这几行配置信息才能够。

二、验证码的生成,封装成一个插件,方便各类增强版本的验证须要。
这个是一个通用的严正码设置与验证码校验的类,能够按本身的须要进行修改。
 
复制代码

//-----------------------------------------------------------------
// 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
    }
}

复制代码

 


三、提供远程访问接口:用户信息的访问;权限信息的访问。访问方式能够是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 退出功能的深刻优化,能退出子系统也能够退出主系统。sql

相关文章
相关标签/搜索