【SSO单点登录】5.跨子域cookie单点登录(一)

本文章由聚项云平台博客独家原创,转载请注明出处:http://blog.jxcode.com/?p=2880&preview=true

之前我们介绍了一些单点登录的实现方式,俗话说“实践是验证真理的唯一标准”,所以我们加下来要通过编程来逐一将之前介绍的单点登录实现方式来实现一下。

首先我们来实现一个最简单的模式,创建几个简单的JavaWeb工程,并利用cookie实现一个跨子域的单点登录。
虽然现在已经很少有用这种方式实现单点登录,且有安全隐患,但是我们可以很好的从利用cookie实现单点登录来更加理解单点登录的实现方式,为学习后期的复杂协议认证等机制打下基础。

我们首先使用JavaWeb实现下面一种模式的工程:
图5.1

首先我们在MyEclipse创建“业务系统A”:
图5.2 图5.3
然后在WebRoot下除了index.jsp以外,然后再创建一个错误提示页面error.jsp,和成功提示页面success.jsp:
图5.4
它们的代码分别是:
index.jsp:

 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
 <head>
 <title>SsoTestProject_A登录页面</title>
 </head>

<body>
 <form id="form1" name="form1" action="./LoginServlet" method="post">
 用户名:<input type="text" id="username" name="username"/><br/>
 密码:<input type="password" id="password" name="password"/>
 <input type="submit" id="submit" value="登录"/>
 </form>
 </body>
 </html>

error.jsp:

 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
 <head>
 <title>SsoTestProject_A错误</title>
 </head>

<body>
 登录失败!<br>
 </body>
 </html>

success.jsp:

 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
 <head>
 <title>SsoTestProject_A成功</title>
 </head>

<body>
 登录成功!O(∩_∩)O<br>
 </body>
 </html>

其中index.jsp是登录界面,error.jsp是登录失败时跳转的页面,success.jsp是登录成功时跳转的页面。

然后在src中创建一个响应登录请求的Servlet类,并编写相关响应逻辑:
图5.5 图5.6

package app.servlet;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {

private static Map<String,String> userMap = null;
 
 @Override
 public void init() throws ServletException {
     super.init();
     //在Servlet创建时,初始化一些Map作为模拟数据库的数据
     userMap = new HashMap<String,String>();
     userMap.put("jack", "1234");
     userMap.put("tom", "4321");
 }
 
 public void doGet(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
     doPost(request, response);
 }

 public void doPost(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
     String username = request.getParameter("username");
     String password = request.getParameter("password");
 
     //模拟数据库查询,进行用户账号校验
     if(checkUserAccount(username,password)){
         //登录成功
         request.getRequestDispatcher("/success.jsp").forward(request, response);
     }else{
         //登录失败
         request.getRequestDispatcher("/error.jsp").forward(request, response);
     }
 }
 
 private boolean checkUserAccount(String username,String password){
     if(userMap.get(username)!=null&&userMap.get(username).equals(password)){
         return true;
     }else{
         return false;
     }
 }
}

在该Servlet中先是使用Servlet的init方法初始化了一些账号密码,来模拟数据库的信息。然后在doPost方法中编写账号密码的校验逻辑。

之后我们将WebProject部署到Tomcat中:
图5.7 图5.8
其中Tomcat,在浏览器中访问应用:
图5.9
分别输入正确账号密码“jack”和“1234”,发现登录成功:
图5.10 图5.11
然后输入一个错误的密码,登录失败:
图5.12
好的,上面一个能正常处理登录服务的业务系统A就搭建好了。

然后我们创建一个名为“SsoTestProject_B”的工程,结构、逻辑与A完全相同:
图5.14
除了Jsp中的title改为“SsoTestProject_B…”以外,还有一处一不同的是,处理登录的Servlet的校验中,模拟数据库的账号密码是另一组数据:

@Override
public void init() throws ServletException {
    super.init();
    //在Servlet创建时,初始化一些Map作为模拟数据库的数据
    userMap = new HashMap<String,String>();
    userMap.put("jean", "1111");
    userMap.put("Michael", "2222");
}

然后也将其部署至Tomcat中:
图5.15
两个系统同时进入登录页面:
图5.16
这就相当于为客户开发了两套系统,A系统和B系统的用户数据完全独立,没有任何关联。

下面使用单点登录模式来解决统一认证的问题,实现一次登录就可以访问两个不同的系统。
那么最重要的就是要创建一个类似统一认证门户的一个系统,用来进行统一登录。

在MyEclipse创建一个名为“SsoTestProject_Portal”的WebProject,作为单点登录的入口,即认证门户系统:
图5.17

基本的结构和逻辑先复制工程A的:
图5.18
除了Jsp中的title要改成“SsoTestProject_Portal…”以外,在登录成功页面,需要让统一认证成功的用户选择想要进入的业务系统:

 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
 <head>
 <title>SsoTestProject_Portal成功</title>
 </head>

<body>
 登录成功!O(∩_∩)O<br/>
 <a target="_blank" href="http://localhost:8080/SsoTestProject_A/LoginServlet">业务系统A</a><br/>
 <a target="_blank" href="http://localhost:8080/SsoTestProject_B/LoginServlet">业务系统B</a><br/>
 </body>
 </html>

在超链接中,放置的是各个业务系统的登录认证服务链接。

我们下面要做的就是,将在统一认证系统中的用户输入的账号密码传递给各个子系统。所以我们改写复制的LoginServlet,让其在登录完成之后在cookie中记录用户的信息:

package app.servlet;

import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;

import javax.servlet.ServletException;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {

private static Map<String,String> userMap = null;

@Override
 public void init() throws ServletException {
     super.init();
     //在Servlet创建时,初始化一些Map作为模拟数据库的数据
     userMap = new HashMap<String,String>();
     userMap.put("jack", "1234");
     userMap.put("tom", "4321");
     userMap.put("jean", "1111");
     userMap.put("Michael", "2222");
 }

 public void doGet(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
     doPost(request, response);
 }

 public void doPost(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
     String username = request.getParameter("username");
     String password = request.getParameter("password");

     //模拟数据库查询,进行用户账号校验
     if(checkUserAccount(username,password)){
         //登录成功
         request.getSession().setAttribute("user", username);//在同一认证平台中保存用户登录会话
         //将登录认证信息放置在cookie中
         Cookie cookie = new Cookie("sso_01",username);
         //设置cookie的生命周期
         cookie.setMaxAge(60*60);
         //设置cookie作用到整个tomcat包下
         cookie.setPath("/");
         response.addCookie(cookie);
         request.getRequestDispatcher("/success.jsp").forward(request, response);
     }else{
         //登录失败
         request.getRequestDispatcher("/error.jsp").forward(request, response);
     }
 }

 private boolean checkUserAccount(String username,String password){
     if(userMap.get(username)!=null&&userMap.get(username).equals(password)){
         return true;
     }else{
         return false;
     }
 }
}
然后在业务系统A与B的LoginServlet中添加从cookie中获取统一认证平台设置的凭据逻辑:
 package app.servlet;

import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;

import javax.servlet.ServletException;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {

private static Map<String,String> userMap = null;

 @Override
 public void init() throws ServletException {
     super.init();
     //在Servlet创建时,初始化一些Map作为模拟数据库的数据
     userMap = new HashMap<String,String>();
     userMap.put("jack", "1234");
     userMap.put("tom", "4321");
 }

 public void doGet(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
     doPost(request, response);
 }

 public void doPost(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
     String username = request.getParameter("username");
     String password = request.getParameter("password");

     Cookie[] cookies = request.getCookies();
     if(cookies!=null&&cookies.length>0){//如果cookie不为空,则进行凭证获取
         boolean haveKey = false;
         for(Cookie cookie:cookies){
             String name = cookie.getName();
             System.out.println("name:"+name);
             if(name!=null&&name.equals("sso_01")){
                 haveKey = true;
                 break;
             }
         }

         if(haveKey){
             //cookie凭证存在,登录成功
             request.getRequestDispatcher("/success.jsp").forward(request, response);
         }else{
             //如果cookie凭证不存在,则进行正常登录校验
             this.login(request,response,username,password);
         }
     }else{
         //如果cookie为空,则进行正常登录校验
         this.login(request,response,username,password);
     }
 }

 private void login(HttpServletRequest request, HttpServletResponse response,
     String username,String password){
     try {
         //模拟数据库查询,进行用户账号校验
         if(checkUserAccount(username,password)){
             //登录成功
             request.getRequestDispatcher("/success.jsp").forward(request, response);
         }else{
             //登录失败
             request.getRequestDispatcher("/error.jsp").forward(request, response);
         }
      }catch (Exception e) {
         e.printStackTrace();
      }
 }

 private boolean checkUserAccount(String username,String password){
     if(userMap.get(username)!=null&&userMap.get(username).equals(password)){
         return true;
     }else{
         return false;
     }
 }
}

上面添加了一段逻辑,即是在登录时首先进行cookie判断,如果cookie中有name为“sso_01”的凭据,说明用户之前登录过统一认证平台,此时就进行免登录的操作,否则进行正常的校验账号名密码机制。

下面我们将统一认证平台也部署到Tomcat中:
图5.19
然后启动Tomcat,在统一认证平台中登录:
图5.20 图5.21
此时应该在浏览器中生成了一段cookie,我们这里使用的是谷歌浏览器,所以到相应的文件夹“C:\Users\jack\AppData\Local\Google\Chrome\User Data\Default”下去查看(AppData是隐藏的文件夹,用上面的路径即可打开),发现果有一个cookie文件:
图5.22
打开之后的发现有内容为:
图5.23
在谷歌浏览器中,我们在地址栏输入“chrome://settings/cookies/detail?site=localhost”也可以查看本地的cookie详情:
图5.24
所以在我们在认证平台成功生成了cookie。

然后我们分别点击“业务系统A”和“业务系统B”,发现都能登录成功:
图5.25 图5.26

上面就是使用cookie的方式来实现一个简单的跨子域的单点登录。

未经允许不得转载:JX BLOG » 【SSO单点登录】5.跨子域cookie单点登录(一)

赞 (3)

评论 1

评论前必须登录!

登陆 注册
  1. zhangfeng学习了