[置顶] spring MVC中的CAS实现

概念:


SSO:Single Sign On 单点登录。SSO是多个应用系统中,用户只需要登录一次,就可以访问所有相互信任的应用系统。


原理:



CAS的系统原理图就是这的,上图只是第一次系统进行验证的时候。之后的验证就会清楚很多了。


实现:


如果我们要知道CAS是如何在MVC中实现的,我们就要对MVC的框架能够了解,其中,MVC中有两个对象,我觉得是非常需要知道的:


1、Membership对象:验证用户凭据并管理用户设置。

2、FormsAuthentication对象:为 Web 应用程序管理 Forms 身份验证服务。


这样我们就很清楚了,Membership对象,就是我们在CAS Server中的需要验证的信息,它包含了用户的数据库。FormsAuthentication对象,就是帮助我们生成Cookie和ST等的,spring MVC中的架构封装的很好,我们看到不到人家是怎么实现的,但是我们可以知道怎么用它们的就可以了。


我们创建一个MVC的Controller:


<span style="font-size:18px;">namespace MvcApplication1.Controllers
{
    public class AccountController : Controller
    {

        //
        // GET: /Account/LogOn

        public ActionResult LogOn()
        {
            return View();
        }

        //
        // POST: /Account/LogOn

        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (Membership.ValidateUser(model.UserName, model.Password))
                {
                    //中的两个参数第一个为用户的信息,第二个为是否Cookie会持久化
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);


                    //存储Cookie信息在本地
                    HttpCookie cookie = FormsAuthentication.GetAuthCookie(model.UserName, model.RememberMe);
                    cookie.Name = "selfUserInfo";
                    cookie.Expires = DateTime.Now.AddDays(1);
                    Response.Cookies.Add(cookie);

                    //MVC 封装的,帮助我们进行验证url信息。
                    if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "提供的用户名或密码不正确。");
                }
            }

            // 如果我们进行到这一步时某个地方出错,则重新显示表单
            return View(model);
          
        }


//    FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
//  1, // 版本号。
//  model.UserName, // 与身份验证票关联的用户名。
//  DateTime.Now, // Cookie 的发出时间。
//  DateTime.Now.AddMinutes(20),// Cookie 的到期日期。
//  false, // 如果 Cookie 是持久的,为 true;否则为 false。
//  roles ); // 将存储在 Cookie 中的用户定义数据。roles是一个角色字符串数组
//  string encryptedTicket = FormsAuthentication.Encrypt(authTicket); //加密 

//                    //存入cookie
//  HttpCookie authCookie =
//new HttpCookie(FormsAuthentication.FormsCookieName,
//encryptedTicket);

//  Response.Cookies.Add(authCookie); 

        //
        // GET: /Account/LogOff

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();


            //string subkeyName;
            //subkeyName = "selfUserInfo";
            //HttpCookie aCookie = Request.Cookies["selfUserInfo"];
            //aCookie.Values.Remove(subkeyName);
            //aCookie.Expires = DateTime.Now.AddDays(1);
            ////Response.Cookies.Add(aCookie);
            //var memberValidation = HttpContext.Request.Cookies.Get("selfUserInfo");

            //从本地的数据库中我们查询到Cookie,然后我们将Cookie进行销毁
            if (Request.Cookies["selfUserInfo"] != null)
            {
                HttpCookie mycookie;
                mycookie = Request.Cookies["selfUserInfo"];
                Response.Cookies["selfUserInfo"].Expires = System.DateTime.Now.AddMonths(-1);
                Response.Cookies.Remove("selfUserInfo");//清除 
                Response.Cookies.Add(mycookie);//写入立即过期的*/
                Response.Cookies["selfUserInfo"].Expires = DateTime.Now.AddDays(-1);
            }
            var memberValidation = HttpContext.Request.Cookies.Get("selfUserInfo");

            return RedirectToAction("Index", "Home");
        }

        //
        // GET: /Account/Register

        public ActionResult Register()
        {
            return View();
        }

        //
        // POST: /Account/Register

        [HttpPost]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // 尝试注册用户
                MembershipCreateStatus createStatus;
                Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);

                if (createStatus == MembershipCreateStatus.Success)
                {
                    //注册新的成员
                    FormsAuthentication.SetAuthCookie("login", false /* createPersistentCookie */);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    ModelState.AddModelError("", ErrorCodeToString(createStatus));
                }
            }

            // 如果我们进行到这一步时某个地方出错,则重新显示表单
            return View(model);
        }

        //
        // GET: /Account/ChangePassword

        [MemberValidation]
        public ActionResult ChangePassword()
        {
            return View();
        }

        //
        // POST: /Account/ChangePassword

        [MemberValidation]
        [HttpPost]
        public ActionResult ChangePassword(ChangePasswordModel model)
        {
            if (ModelState.IsValid)
            {

                // 在某些出错情况下,ChangePassword 将引发异常,
                // 而不是返回 false。
                bool changePasswordSucceeded;
                try
                {
                    MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
                    changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
                }
                catch (Exception)
                {
                    changePasswordSucceeded = false;
                }

                if (changePasswordSucceeded)
                {
                    return RedirectToAction("ChangePasswordSuccess");
                }
                else
                {
                    ModelState.AddModelError("", "当前密码不正确或新密码无效。");
                }
            }

            // 如果我们进行到这一步时某个地方出错,则重新显示表单
            return View(model);
        }

        //
        // GET: /Account/ChangePasswordSuccess

        public ActionResult ChangePasswordSuccess()
        {
            return View();
        }

        #region Status Codes
        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // 请参见 http://go.microsoft.com/fwlink/?LinkID=177550 以查看
            // 状态代码的完整列表。
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "用户名已存在。请输入不同的用户名。";

                case MembershipCreateStatus.DuplicateEmail:
                    return "该电子邮件地址的用户名已存在。请输入不同的电子邮件地址。";

                case MembershipCreateStatus.InvalidPassword:
                    return "提供的密码无效。请输入有效的密码值。";

                case MembershipCreateStatus.InvalidEmail:
                    return "提供的电子邮件地址无效。请检查该值并重试。";

                case MembershipCreateStatus.InvalidAnswer:
                    return "提供的密码取回答案无效。请检查该值并重试。";

                case MembershipCreateStatus.InvalidQuestion:
                    return "提供的密码取回问题无效。请检查该值并重试。";

                case MembershipCreateStatus.InvalidUserName:
                    return "提供的用户名无效。请检查该值并重试。";

                case MembershipCreateStatus.ProviderError:
                    return "身份验证提供程序返回了错误。请验证您的输入并重试。如果问题仍然存在,请与系统管理员联系。";

                case MembershipCreateStatus.UserRejected:
                    return "已取消用户创建请求。请验证您的输入并重试。如果问题仍然存在,请与系统管理员联系。";

                default:
                    return "发生未知错误。请验证您的输入并重试。如果问题仍然存在,请与系统管理员联系。";
            }
        }
        #endregion



        public ActionResult ValidateCode()
        {
            ValidateCodeHelper helper = new ValidateCodeHelper();
            string strCode = helper.CreateValidateCode(4);
            Session["validateCode"] = strCode;

            var byteData = helper.CreateValidateGraphic(strCode);
            return File(byteData, "image/jpeg");
        }
    }
}</span>

我们可以看到其中在需要添加权限的Action上面打有这样的标签 [MemberValidation]。


然后我们需要写一个这样的一种特性AuthorizeAttribute:


我们创建一个类集成这个特性就行了:


<span style="font-size:18px;"> public class MemberValidationAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            //获取Cookies中的Login
            //var memberValidation = System.Web.HttpContext.Current.Request.Cookies.Get("login");
            //var memberValidation = filterContext.HttpContext.User.Identity.Name;
            var memberValidation = filterContext.HttpContext.Request.Cookies.Get("selfUserInfo");
            //如果memberValidation为null  或者 memberValidation不等于Success
            if (memberValidation == null)
            {
                //页面跳转到 登录页面
                filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Account", action = "LogOn" }));
                return;
            }
            //通过验证
            return;       
        }
    }</span>

大概的意思其实很简单,我就要看看本地上是不是有Cookie,有的话,我们就验证通过,没有的话,就要从新登陆界面进行验证。


我们创建一个访问页面HomePage:


<span style="font-size:18px;">    public class HomePageController : Controller
    {
        //
        // GET: /HomePage/
        [MemberValidation]
        public ActionResult HomePage()
        {
            return View();
        }

    }</span>

在这个页面上,我们加载所有的应用系统,我们将这个页面成为门户。


看一下HomePage.cshtml:


<span style="font-size:18px;">@{
    ViewBag.Title = "Index";
}


<html>
<head>
    <title>HomePage</title>
    @*<script src="../../Scripts/addPage.js"></script>*@
    @*<script src="../../Scripts/EasyuiLayout.js"></script>*@
    <link rel="stylesheet" type="text/css" href="../../Content/jquery-easyui-1.3.2/themes/default/easyui.css">
    <link rel="stylesheet" type="text/css" href="../../Content/jquery-easyui-1.3.2/themes/icon.css">
    <link rel="stylesheet" type="text/css" href="../../Content/jquery-easyui-1.3.2/demo/demo.css">
    <script type="text/javascript" src="../../Content/jquery-easyui-1.3.2/jquery-1.8.0.min.js"></script>
    <script type="text/javascript" src="../../Content/jquery-easyui-1.3.2/jquery.easyui.min.js"></script>
    <script type="text/javascript" src="../../Content/jquery.balloon.js"></script>
    <script src="../../Content/jquery-easyui-1.3.2/locale/easyui-lang-zh_CN.js"></script>
    <link href ="../../CSS/index.css" rel ="stylesheet"/>  
    <script src="../../Scripts/MyScript/ITOO_Common.js"></script>
</head>
<body>
   
        <input type="button" value="权限字典" onclick="addTab('字典表设置', 'http://192.168.24.247:7089/DictionaryType/index?ticket=123')" />
        <input type="button" value="新生报到" onclick="addTab('新生报到', 'http://192.168.24.183:8076/FreshStudentReport/FreshStudentReport')" />

        <div id="tt" class="easyui-tabs" style="width: 1100px; height: 800px; margin: 0; margin-left: auto; margin-right: auto; border: 1px; overflow: hidden;">           
        </div>

</body>
</html>

<script>
    function addTab(tableName, controlerAddress) {
        $("#tt").tabs('add', {
            title: tableName,
            content: '<div style="width:100%;height:99%;">'
                    + '<iframe id="abc" name="PageFrame" frameBorder="0" width="100%" height="100%" src=' + controlerAddress + '>'
                    + '</iframe>'
                    + '</div>',
            closable: true
        });
    }
</script>
</span>

大家可以看到,我们访问的新生报到界面,用的是ip端口号和controller/Action,就可以进行访问了,在FreshStudentReportController下的FreshStudentReport的action中我们要打上标签[MemberValidation],才能继续判断在本地是否有Cookie,可以让它进行验证。


总结:


spring MVC框架本身是封装的很好的,我们要学会如何去用它,就要清楚里面涉及到的对象。其实,我们先前一直想要实现能够在各个系统中进行验证,没有弄明白一个问题,就是Cookie是存储在本地浏览器中,只有在本地中,我们才可以做各种的判断和验证,否则,就没有办法可以看出该用户是否有权限,或者就是一个用户登陆了之后,所有的用户都可以不需要验证就可以访问所有的系统。







你可能感兴趣的:(mvc,String,cas)