1:产生原因

 

 

一些网站的背后有许多的子系统,如果每一个都要登录的话那么不仅用户会疯掉,子系统也会因为这些重复的逻辑认证疯掉,单点登录应运而生。

2:定义

在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任

3:实现方式

(1、在同一个域名下):

(我们知道,PHP表单验证是完全依赖于Cookie的)当域名相同时,按照HTTP协议规定,两个站点是可以共享Cookie的,这样Cookie将会分享你的信息,实现单点登录

(2、不同域名):

这种方式需要我们借助一个单独的SSO服务,专门做验证用。而且我们还需要对于不同的站点的用户要有一个统一的用户数据。相对于前一种方式——浏览器需要存储每个站点的cookie——来说,这种方式浏览器只需要存储SSO服务站点的cookie信息。将这个cookie信息用于其他站点从而实现单点登录。我们暂且将这个SSO服务站点成为www.SSOsite.com(以下简称SSOsite)。

在这种模型下,针对任何站点的请求都将会先重定向到SSOsite去验证一个身份验证cookie是否存在。如果存在,则验证过的页面将会发送给浏览器。否则用户将会被重定向到登录页面。

简单说就是:我们设计一个sso服务器,用于存储用户的Cookie信息,把他用来给其他的站点登录,这样的话所有的站点的请求都会发到sso服务器中进行验证

 

实现sso服务器(主要讲原理)

我把整个实现过程分为14步:

1:用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
2:sso认证中心发现用户未登录,将用户引导至登录页面
3:用户输入用户名密码提交登录申请
4:sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌
5:sso认证中心带着令牌跳转会最初的请求地址(系统1)
6:系统1拿到令牌,去sso认证中心校验令牌是否有效
7:sso认证中心校验令牌,返回有效,注册系统1
8:系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
9:用户访问系统2的受保护资源
10:系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
11:sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
12:系统2拿到令牌,去sso认证中心校验令牌是否有效
13:sso认证中心校验令牌,返回有效,注册系统2
14:系统2使用该令牌创建与用户的局部会话,返回受保护资源

这是整个实现的流程我把这个模拟的分为小三个项目:分别是 SSOServer SSOCliect1  SSOCliect2

我们用maven建好项目,搭建好基础环境。

服务器(是两个子系统都需要经过的,主要验证账号密码,生成token和存储token信息,方便验证):

首先是pom

  1 <?xml version="1.0"?>
  2 <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
  3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  4   <modelVersion>4.0.0</modelVersion>
  5   <groupId>com.yzz.ssoserver</groupId>
  6   <artifactId>SSOServer</artifactId>
  7   <version>0.0.1-SNAPSHOT</version>
  8   <packaging>war</packaging>
  9   <name>SSOServer Maven Webapp</name>
 10   <url>http://maven.apache.org</url>
 11   <properties>
 12     <spring.version>4.0.2.RELEASE</spring.version>
 13   </properties>
 14   <dependencies>
 15     <dependency>
 16       <groupId>junit</groupId>
 17       <artifactId>junit</artifactId>
 18       <version>3.8.1</version>
 19       <scope>test</scope>
 20     </dependency>
 21     <dependency>
 22       <groupId>org.codehaus.jackson</groupId>
 23       <artifactId>jackson-mapper-asl</artifactId>
 24       <version>1.9.13</version>
 25       <scope>compile</scope>
 26     </dependency>
 27     <dependency>
 28       <groupId>org.springframework</groupId>
 29       <artifactId>spring-core</artifactId>
 30       <version>4.0.2.RELEASE</version>
 31       <scope>compile</scope>
 32     </dependency>
 33     <dependency>
 34       <groupId>org.springframework</groupId>
 35       <artifactId>spring-web</artifactId>
 36       <version>4.0.2.RELEASE</version>
 37       <scope>compile</scope>
 38     </dependency>
 39     <dependency>
 40       <groupId>org.springframework</groupId>
 41       <artifactId>spring-oxm</artifactId>
 42       <version>4.0.2.RELEASE</version>
 43       <scope>compile</scope>
 44     </dependency>
 45     <dependency>
 46       <groupId>org.springframework</groupId>
 47       <artifactId>spring-tx</artifactId>
 48       <version>4.0.2.RELEASE</version>
 49       <scope>compile</scope>
 50     </dependency>
 51     <dependency>
 52       <groupId>org.springframework</groupId>
 53       <artifactId>spring-jdbc</artifactId>
 54       <version>4.0.2.RELEASE</version>
 55       <scope>compile</scope>
 56     </dependency>
 57     <dependency>
 58       <groupId>org.springframework</groupId>
 59       <artifactId>spring-webmvc</artifactId>
 60       <version>4.0.2.RELEASE</version>
 61       <scope>compile</scope>
 62     </dependency>
 63     <dependency>
 64       <groupId>org.springframework</groupId>
 65       <artifactId>spring-aop</artifactId>
 66       <version>4.0.2.RELEASE</version>
 67       <scope>compile</scope>
 68     </dependency>
 69     <dependency>
 70       <groupId>org.springframework</groupId>
 71       <artifactId>spring-context-support</artifactId>
 72       <version>4.0.2.RELEASE</version>
 73       <scope>compile</scope>
 74     </dependency>
 75     <dependency>
 76       <groupId>org.springframework</groupId>
 77       <artifactId>spring-test</artifactId>
 78       <version>4.0.2.RELEASE</version>
 79       <scope>compile</scope>
 80     </dependency>
 81     <dependency>
 82       <groupId>commons-httpclient</groupId>
 83       <artifactId>commons-httpclient</artifactId>
 84       <version>3.1</version>
 85       <scope>compile</scope>
 86     </dependency>
 87     <dependency>
 88       <groupId>commons-io</groupId>
 89       <artifactId>commons-io</artifactId>
 90       <version>2.4</version>
 91       <scope>compile</scope>
 92     </dependency>
 93     <dependency>
 94       <groupId>commons-codec</groupId>
 95       <artifactId>commons-codec</artifactId>
 96       <version>1.9</version>
 97       <scope>compile</scope>
 98     </dependency>
 99     <dependency>
100       <groupId>commons-dbcp</groupId>
101       <artifactId>commons-dbcp</artifactId>
102       <version>1.4</version>
103       <scope>compile</scope>
104     </dependency>
105     <dependency>
106       <groupId>mysql</groupId>
107       <artifactId>mysql-connector-java</artifactId>
108       <version>5.1.30</version>
109       <scope>compile</scope>
110     </dependency>
111     <dependency>
112       <groupId>javax</groupId>
113       <artifactId>javaee-api</artifactId>
114       <version>7.0</version>
115       <scope>compile</scope>
116     </dependency>
117     <dependency>
118       <groupId>jstl</groupId>
119       <artifactId>jstl</artifactId>
120       <version>1.2</version>
121       <scope>compile</scope>
122     </dependency>
123     <dependency>
124       <groupId>com.alibaba</groupId>
125       <artifactId>fastjson</artifactId>
126       <version>1.2.24</version>
127       <scope>compile</scope>
128     </dependency>
129   </dependencies>
130   <repositories>
131     <repository>
132       <snapshots>
133         <enabled>false</enabled>
134       </snapshots>
135       <id>central</id>
136       <name>Central Repository</name>
137       <url>http://repo.maven.apache.org/maven2</url>
138     </repository>
139   </repositories>
140  
pom

然后是所必要的ssocontroller

package com.yzz.ssoserver.controller;

import java.util.UUID;

import javax.json.JsonObject;
import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yzz.ssoserver.util.TokenUtil;
import com.yzz.ssoserver.util.UrlUtil;

@Controller
public class SSOServerController {

    //判断用户是否登录,偷懒,利用controller代替拦截器
    @RequestMapping("")
    public String loginCheck(String clientUrl,HttpServletRequest request){
        String userName=(String)request.getSession().getAttribute("isLogin");
        //未登录跳转到客户端登录页面(也可以是服务器自身拥有登录界面)
        if(userName==null){
            
            System.out.println("路径:"+clientUrl+" 未登录,跳转登录页面");
            return "redirect:"+clientUrl+"?url=http://localhost:8080/SSOServer/user/login";
        }else{
            //以登录携带令牌原路返回
            String token = UUID.randomUUID().toString();
            System.out.println("已经登录,登录账号:"+userName+"服务端产生的token:"+token);
            //存储
            TokenUtil.put(token, userName);
            return "redirect:"+clientUrl+"?token="+token+"&allSessionId="+request.getSession().getId();
        }    
    }
    
    //令牌验证
    @ResponseBody
    @RequestMapping(value="/tokenCheck",method=RequestMethod.POST)
    public String tokenCheck(String token,String clientUrl,String allSessionId){
        
        JSONObject j=new JSONObject();
        String userName=TokenUtil.get(token);
        
        //token一次性的,用完即毁
        TokenUtil.remove(token);
        if(userName!=null){
            //设置返回消息
            j.put("erroeCode", 0);
            j.put("header", "认证成功!");
            j.put("userName", userName);
            
            //存储地址信息,用于退出时销毁
            
            String url=UrlUtil.get(allSessionId);
            if(url==null){
                url=clientUrl;
            }else{
                url+=","+clientUrl;
            }
            
            UrlUtil.put(allSessionId, url);
            
        }
        return j.toJSONString();
    }
}
sso controller

和usercontroller

  1 package com.yzz.ssoserver.controller;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.io.InputStreamReader;
  7 import java.io.OutputStream;
  8 import java.io.OutputStreamWriter;
  9 import java.net.HttpURLConnection;
 10 import java.net.MalformedURLException;
 11 import java.net.URL;
 12 import java.util.UUID;
 13  
 14 import javax.servlet.http.Cookie;
 15 import javax.servlet.http.HttpServletRequest;
 16 import javax.servlet.http.HttpServletResponse;
 17  
 18 import org.apache.commons.httpclient.HttpClient;
 19 import org.apache.commons.httpclient.HttpException;
 20 import org.apache.commons.httpclient.methods.PostMethod;
 21 import org.springframework.beans.factory.annotation.Autowired;
 22 import org.springframework.stereotype.Controller;
 23 import org.springframework.web.bind.annotation.RequestMapping;
 24 import org.springframework.web.bind.annotation.RequestMethod;
 25 import org.springframework.web.servlet.ModelAndView;
 26  
 27 import com.yzz.ssoserver.bean.User;
 28 import com.yzz.ssoserver.dao.UserDao;
 29 import com.yzz.ssoserver.util.TokenUtil;
 30 import com.yzz.ssoserver.util.UrlUtil;
 31  
 32 /**
 33  * 
 34  * @author Administrator
 35  *
 36  */
 37 @RequestMapping("/user")
 38 @Controller
 39 public class UserController {
 40     @Autowired
 41     private UserDao baseDao;
 42     @RequestMapping("/getName")
 43     public ModelAndView getName(){
 44         ModelAndView model=new ModelAndView("index");
 45         
 46         String userName=baseDao.getName();
 47         model.addObject("userName",userName);
 48         return model;
 49     }
 50     
 51     //登录验证
 52     @RequestMapping(value="/login",method=RequestMethod.POST)
 53     public String login(HttpServletRequest request,HttpServletResponse response){
 54         ModelAndView model=new ModelAndView();
 55         String userName=request.getParameter("userName");
 56         String userPassword=request.getParameter("userPassword");
 57         String redirectUrl=request.getParameter("redirectUrl");
 58         int user=baseDao.login(userName,userPassword);
 59         if(user!=0){
 60             //设置状态(通过session判断该浏览器与认证中心的全局会话是否已经建立),生成令牌
 61             request.getSession().setAttribute("isLogin", userName);
 62             String token = UUID.randomUUID().toString();
 63             
 64             //存储
 65             TokenUtil.put(token, userName);
 66             /*设置cookie到浏览器
 67             Cookie cookie=new Cookie("sso", userName);
 68             cookie.setMaxAge(60);
 69             response.addCookie(cookie);
 70             */
 71             //将token发送给客户端,附带本次全局会话的sessionId
 72             String allSessionId=request.getSession().getId();
 73             System.out.println("全局会话allSessionId:"+allSessionId);
 74             return "redirect:"+redirectUrl+"?token="+token+"&allSessionId="+allSessionId;    
 75         }
 76         return "redirect:http://localhost:8080/SSOServer/redirectUrl?msg=loginError";
 77     }
 78     
 79     @RequestMapping(value="/redirectUrl",method=RequestMethod.POST)
 80     public ModelAndView redirectUrl(HttpServletRequest request){
 81         ModelAndView model=new ModelAndView();
 82         String msg=request.getParameter("msg");
 83         if(msg.equals("loginError")){
 84             msg="账号密码错误";
 85             model.setViewName("error");
 86             model.addObject("msg",msg);
 87         }
 88         return model;
 89     }
 90     
 91     //登出
 92     @RequestMapping(value="/logout")
 93     public String logOut(String allSessionId,String redirectUrl,HttpServletRequest request){
 94         String url=UrlUtil.get(allSessionId);
 95         UrlUtil.remove(allSessionId);
 96         //删除全局会话
 97         request.getSession().removeAttribute("isLogin");
 98         
 99         //通知各个客户端删除局部会话
100         String [] urls=url.split(",");
101         //使用httpClient通知客户端的时候发现是新建立了一个服务器与客户端的会话,导致sessionId和客户建立的局部会话id不相同,无法做到删除局部会话
102         HttpClient httpClient=new HttpClient();
103         PostMethod postMethod=new PostMethod();
104         
105         for (String u : urls) {
106             
107             postMethod.setPath(u+"/logout");
108             postMethod.addParameter("allSessionId", allSessionId);
109             
110             try {
111                 httpClient.executeMethod(postMethod);
112                 postMethod.releaseConnection();
113             
114             } catch (HttpException e) {
115                 // TODO Auto-generated catch block
116                 e.printStackTrace();
117             } catch (IOException e) {
118                 // TODO Auto-generated catch block
119                 e.printStackTrace();
120             }
121         }
122         
123         return "redirect:"+redirectUrl;
124     }
125     
126 }
View Code

然后哦是userdao(本来连接数据库了,但为了做实验用我们就设置固定的值)

 1 package com.yzz.ssoserver.dao;
 2 
 3 import java.sql.ResultSet;
 4 import java.util.ArrayList;
 5 import java.util.List;
 6 
 7 import javax.swing.text.html.HTMLDocument.HTMLReader.ParagraphAction;
 8 import javax.swing.tree.RowMapper;
 9 import javax.swing.tree.TreePath;
10 
11 import org.springframework.beans.factory.annotation.Autowired;
12 import org.springframework.jdbc.core.JdbcTemplate;
13 import org.springframework.stereotype.Repository;
14 
15 import com.yzz.ssoserver.bean.User;
16 import com.yzz.ssoserver.mapping.UserMapping;
17 @Repository
18 public class UserDao {
19 
20     @Autowired
21     private  JdbcTemplate jdbcTemplate;
22 
23 
24     public String getName(){
25         return jdbcTemplate.queryForObject("select user_name from user_info where user_id=1", String.class);
26     }
27     
28     public int login(String userName,String userPassword){
29         if ("admin".equals(userName)&&"123456".equals(userPassword)) {
30             return 1;
31         }
32         //User u=new User();
33         //String sql=" select * from user_info where user_name=? and user_password=? ";
34         
35         //Object[] param= new Object[]{userName,userPassword};
36         
37         //u=jdbcTemplate.queryForObject(sql, new UserMapping(), param);
38         
39          return 0;
40     }
41 
42 }
userdao

然后是我们帮助类TokenUtil,UrlUtil

 1 package com.yzz.ssoserver.util;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 
 7 public class TokenUtil {
 8     
 9     
10     public static Map<String,String> TOKEN_MAP=new HashMap<String, String>();
11     
12     public static void put(String token, String userName) {
13         TOKEN_MAP.put(token, userName);
14 
15     }
16 
17     public static String get(String token) {
18         return TOKEN_MAP.get(token);
19 
20     }
21 
22     public static void remove(String token) {
23         TOKEN_MAP.remove(token);
24 
25     }
26 }
View Code
 1 package com.yzz.ssoserver.util;
 2 
 3 import java.util.HashMap;
 4 
 5 import java.util.Map;
 6 
 7 public class UrlUtil {
 8 
 9 public static Map<String,String> CLIENTURL_MAP=new HashMap<String, String>();
10     
11     public static void put(String sessionId, String url) {
12         CLIENTURL_MAP.put(sessionId, url);
13 
14     }
15 
16     public static String get(String allSessionId) {
17         return CLIENTURL_MAP.get(allSessionId);
18 
19     }
20 
21     public static void remove(String sessionId) {
22         CLIENTURL_MAP.remove(sessionId);
23 
24     }
25     
26 
27 }
View Code

在之后要去设置一下webapp下的web.xml

 1 <?xml version="1.0" encoding="UTF-8"?>  
 2     <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
 3         xmlns="http://java.sun.com/xml/ns/javaee"  
 4         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
 5         version="3.0">  
 6         <display-name>Archetype Created Web Application</display-name>  
 7        <context-param>  
 8         <param-name>contextConfigLocation</param-name>  
 9         <param-value>classpath:applicationContext.xml</param-value>  
10     </context-param>
11         <!-- 编码过滤器 -->  
12         <filter>  
13             <filter-name>encodingFilter</filter-name>  
14             <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
15             <async-supported>true</async-supported>  
16             <init-param>  
17                 <param-name>encoding</param-name>  
18                 <param-value>UTF-8</param-value>  
19             </init-param>  
20         </filter>  
21         <filter-mapping>  
22             <filter-name>encodingFilter</filter-name>  
23             <url-pattern>/*</url-pattern>  
24         </filter-mapping>  
25         <!-- Spring监听器 -->  
26         <listener>  
27             <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
28         </listener>  
29         <!-- 防止Spring内存溢出监听器 -->  
30         <listener>  
31             <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>  
32         </listener>  
33       
34         <!-- Spring MVC servlet -->  
35         <servlet>  
36             <servlet-name>SpringMVC</servlet-name>  
37             <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
38             <init-param>  
39                 <param-name>contextConfigLocation</param-name>  
40                 <param-value>classpath:applicationContext.xml</param-value>  
41             </init-param>  
42             <load-on-startup>1</load-on-startup>  
43             <async-supported>true</async-supported>  
44         </servlet>  
45         <servlet-mapping>  
46             <servlet-name>SpringMVC</servlet-name>  
47             <!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->  
48             <url-pattern>/</url-pattern>  
49         </servlet-mapping>  
50         <welcome-file-list>  
51             <welcome-file>/index.jsp</welcome-file>  
52         </welcome-file-list>  
53       
54     </web-app>  
View Code

这样之后我们的服务器就配置好了,我们可以写一个简单的Demo可以测试一下这个maven项目有没问题

客户端:

和服务器一样先是controller

  1 package com.yzz.ssoclient2.controller;
  2 
  3 import java.io.IOException;
  4 import java.util.Enumeration;
  5 import java.util.HashMap;
  6 import java.util.Map;
  7 import java.util.Set;
  8 
  9 import javax.servlet.http.HttpServletRequest;
 10 import javax.servlet.http.HttpSession;
 11 
 12 import org.apache.commons.httpclient.HttpClient;
 13 import org.apache.commons.httpclient.HttpException;
 14 import org.apache.commons.httpclient.methods.PostMethod;
 15 import org.springframework.stereotype.Controller;
 16 import org.springframework.ui.ModelMap;
 17 import org.springframework.web.bind.annotation.RequestMapping;
 18 import org.springframework.web.servlet.ModelAndView;
 19 
 20 import com.alibaba.fastjson.JSONObject;
 21 import com.yzz.ssoclient2.util.SessionUtil;
 22 
 23 /**
 24  * 
 25  * @author yzz
 26  *客户端部分,本想只用一个session存储局部会话,收到服务端的退出请求后直接调用request.getSession().removeAttribute("token")清空局部会话;
 27  *结果发现,服务端利用httpClient通知客户端的时候是新建立的一个会话,此时的session和我们局部建立的session并不是同一个。
 28  *解决办法:
 29  *自己维护一个session管理类,利用map将局部会话的session对象和id存储起来。收到请求后再销毁该session
 30  */
 31 
 32 @Controller
 33 public class SSOClientController {
 34 
 35     //拦截所有获取资源请求
 36     @RequestMapping("")
 37     public String ssoClient(HttpServletRequest request,ModelMap map){
 38         
 39         //判断请求的链接中是否有token参数
 40         String token=request.getParameter("token");
 41         String url=request.getParameter("url");
 42 
 43         if(token!=null){
 44             //如果有表示是认证服务器返回的
 45             String allSessionId=request.getParameter("allSessionId");
 46             return "redirect:http://localhost:8080/SSOClient2/checkToken?token="+token+"&allSessionId="+allSessionId;
 47         }else if(url!=null){
 48             
 49             return "redirect:http://localhost:8080/SSOClient2/login?url="+url;
 50         }else{
 51             //其他请求,继续判断是否创建了和用户之间的局部会话
 52             JSONObject j=(JSONObject) request.getSession().getAttribute("token");
 53             if(j!=null){
 54                 System.out.println("已经登录,存在局部会话1:"+j);
 55                 System.out.println("本次局部会话的localSessionId:"+request.getSession().getId());
 56                 map.addAttribute("userName", j.getString("userName"));
 57                 map.addAttribute("allSessionId", j.getString("allSessionId"));
 58                 return "index";
 59             }else{
 60                 //未登录
 61                 
 62                 return "redirect:http://localhost:8080/SSOServer?clientUrl=http://localhost:8080/SSOClient2";
 63             }
 64         }    
 65     }
 66     
 67     //客户端接收token并且进行验证
 68     @RequestMapping(value="/checkToken")
 69     public String checkToken(HttpServletRequest request,ModelMap map){
 70         
 71         String token=request.getParameter("token");
 72         String allSessionId=request.getParameter("allSessionId");
 73         
 74         //利用httpClient进行验证
 75         String basePath = request.getScheme() + "://" + request.getServerName() + ":"
 76                 + request.getServerPort() + request.getContextPath();
 77         HttpClient httpClient = new HttpClient();
 78         PostMethod postMethod = new PostMethod("http://localhost:8080/SSOServer/tokenCheck");
 79         postMethod.addParameter("token", token);
 80         postMethod.addParameter("allSessionId", allSessionId);
 81         postMethod.addParameter("clientUrl",basePath);
 82         
 83         try {
 84             httpClient.executeMethod(postMethod);
 85             String resultJson = postMethod.getResponseBodyAsString();
 86             
 87             postMethod.releaseConnection();
 88             //用httpClient得到的json数据默认被转义了两次变成了"{\\"header\\":\\"认证成功!\\",\\"userName\\":\\"admin\\",\\"erroeCode\\":0}"
 89             //需要数据还原 \\" 变成  " 同时去掉前后的双引号 
 90             
 91             resultJson=resultJson.replaceAll("\\\\\"", "\"");
 92             resultJson=resultJson.substring(1, resultJson.length()-1);
 93             JSONObject j=JSONObject.parseObject(resultJson);
 94             j.put("allSessionId", allSessionId);
 95             int errorCode=j.getIntValue("erroeCode");
 96             if(errorCode==0){
 97                 //创建客户端和用户的局部会话
 98                 request.getSession().setAttribute("token", j);
 99                 String localSessionId=request.getSession().getId();
100                 HttpSession localSession=request.getSession();
101                 System.out.println("创建局部会话,localSessionId是:"+request.getSession().getId());
102                 map.addAttribute("userName", j.getString("userName"));
103                 map.addAttribute("allSessionId", j.getString("allSessionId"));
104                 //存储局部会话
105                 
106                 SessionUtil.setSession(localSessionId, localSession);
107                 //存储对应关系
108                 SessionUtil.setLink(allSessionId, localSessionId);
109                 
110             }else{
111                 
112             }
113         } catch (HttpException e) {
114             // TODO Auto-generated catch block
115             e.printStackTrace();
116         } catch (IOException e) {
117             // TODO Auto-generated catch block
118             e.printStackTrace();
119         }
120         return "index";
121     }
122     
123     //客户端登录
124     @RequestMapping(value="/login")
125     public ModelAndView login(HttpServletRequest request){
126         
127         String url=request.getParameter("url");
128         ModelAndView model=new ModelAndView();
129         model.setViewName("login");
130         model.addObject("url", url);
131         return model;
132     }
133     
134     //退出
135     @RequestMapping(value="/logout")
136     public void logout(String allSessionId){
137         
138         System.out.println("客户端2收到退出请求");
139         String localSessionId=SessionUtil.getLocalSessionId(allSessionId);
140         
141         HttpSession localSession=SessionUtil.getSession(localSessionId);
142         
143         localSession.removeAttribute("token");
144         
145         //localSession.invalidate();
146         
147     }
148 }
SSOClientController

一个小的Util包SessionUtil

 1 package com.yzz.ssoclient2.util;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 import javax.servlet.http.HttpSession;
 7 public class SessionUtil {
 8 
 9     private static Map <String, HttpSession> SESSIONMAP=new HashMap<String, HttpSession>();
10     private static Map <String,String> sessionLink=new HashMap<String, String>();
11     public static HttpSession getSession(String localSessionId){
12         return SESSIONMAP.get(localSessionId);
13     }
14     
15     public static void setSession(String localSessionId,HttpSession localSession){
16          SESSIONMAP.put(localSessionId, localSession);
17     }
18     
19     public static void remove(String localSessionId){
20         SESSIONMAP.remove(localSessionId);
21     }
22     
23     public static String getLocalSessionId(String allSessionId){
24         return sessionLink.get(allSessionId);
25     }
26     public static void setLink(String allSessionId,String localSessionId){
27         sessionLink.put(allSessionId, localSessionId);
28     }
29     public static void removeL(String allSessionId,String localSessionId){
30         sessionLink.remove(allSessionId);
31     }
32 }
SessionUtil

然后同样的web设置

 1 <?xml version="1.0" encoding="UTF-8"?>  
 2     <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
 3         xmlns="http://java.sun.com/xml/ns/javaee"  
 4         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
 5         version="3.0">  
 6         <display-name>Archetype Created Web Application</display-name>  
 7         <!-- Spring配置文件 -->  
 8         <context-param>  
 9             <param-name>contextConfigLocation</param-name>  
10             <param-value>classpath:spring-mvc.xml</param-value>  
11         </context-param>  
12         <!-- 编码过滤器 -->  
13         <filter>  
14             <filter-name>encodingFilter</filter-name>  
15             <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
16             <async-supported>true</async-supported>  
17             <init-param>  
18                 <param-name>encoding</param-name>  
19                 <param-value>UTF-8</param-value>  
20             </init-param>  
21         </filter>  
22         <filter-mapping>  
23             <filter-name>encodingFilter</filter-name>  
24             <url-pattern>/*</url-pattern>  
25         </filter-mapping>  
26         <!-- Spring监听器 -->  
27         <listener>  
28             <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
29         </listener>  
30         <!-- 防止Spring内存溢出监听器 -->  
31         <listener>  
32             <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>  
33         </listener>  
34       
35         <!-- Spring MVC servlet -->  
36         <servlet>  
37             <servlet-name>SpringMVC</servlet-name>  
38             <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
39             <init-param>  
40                 <param-name>contextConfigLocation</param-name>  
41                 <param-value>classpath:spring-mvc.xml</param-value>  
42             </init-param>  
43             <load-on-startup>1</load-on-startup>  
44             <async-supported>true</async-supported>  
45         </servlet>  
46         <servlet-mapping>  
47             <servlet-name>SpringMVC</servlet-name>  
48               <url-pattern>/</url-pattern>  
49         </servlet-mapping>  
50         <welcome-file-list>  
51             <welcome-file>index.jsp</welcome-file>  
52         </welcome-file-list>  
53       
54     </web-app>  
View Code

特别说一下,我们设置下两个页面。登录和资源

 1 <%@ page language="java" contentType="text/html; charset=utf-8"
 2     pageEncoding="utf-8"%>
 3     <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">    
 5 <html xmlns='http://www.w3.org/1999/xhtml'>
 6     <head>
 7         <meta http-equiv='Content-Type', content='text/html; charset=utf-8'>
 8         <title>单点登录客户端2</title>
 9     <script type="text/javascript">
10       function logout(){
11           var allSessionId=document.getElementById("allSessionId").value;
12           
13           window.location.href ="http://localhost:8080/SSOServer/user/logout?allSessionId="+allSessionId+"&redirectUrl=http://localhost:8080/SSOClient2";
14       }
15     </script>
16     </head>
17     <body style="padding:60px;padding-bottom:40px;">
18     
19     <h2 style="color:blue">客户端2登入后才能显示的界面</h2>
20     
21     <h3>userName is:  <p style="color:blue">${userName}</p></h3>
22        <input type="hidden" id="allSessionId" value=${allSessionId}>
23        <input type="button" onclick="logout()" value="退出登录">
24   
25      
26     </body>
27 </html>
View Code
 1 <%@ page language="java" contentType="text/html; charset=utf-8"
 2     pageEncoding="utf-8"%>
 3     <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 5 <html>
 6 <head>
 7 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 8 <title>客户端2登录页面</title>
 9 </head>
10 <body style="text-algin:center">
11     <h1 style="color:blue">客户端2的登录界面</h1>
12     <form action=//localhost:8080/SSOServer/user/login method="post">
13         <input type="text" name="userName">账号
14         </br>
15         <input type="password" name="userPassword">密码
16         </br>
17         <input type="hidden" name="redirectUrl" value="http://localhost:8080/SSOClient2">
18         <button type="submit">登录</button>
19     </form>
20     
21 </body>
22 </html>
View Code

 

这样再配置一个相同的客户端资源我们就可以做测试了。

当分别启动两个客户端时都会跳到登录页面,当我们登录了一个之后,能一个无需登录直接进入表示成功。

我测试参考https://blog.csdn.net/qq_31183297/article/details/79419222 大家也可以看看。

 

 posted on 2019-05-22 18:07  bestshiyi  阅读(2562)  评论(0编辑  收藏  举报