SSO单点登录

来源:https://segmentfault.com/a/1190000008933546

第一章:概述

1-1 课程介绍及SSO介绍

课程目标

认识并理解SSO及其应用,并能根据其实现原理自行实现SSO。

学习内容

1.SSO的介绍和应用体验(以新浪为例)
    SSO:一次登录,处处穿梭
2.SSO的分类介绍的实现探讨
    同域SSO:不同的应用位于同一个域名下面
    跨域SSO:不同的应用位于不同的域名下面
3.各种SSO的具体实现介绍的代码示例

同域SSO图示

clipboard.png

1-2 SSO核心技术分析

SSO的实现步骤和原理

以旅游是购买的通票为例:

clipboard.png

SSO特点:

1.必须要登陆一次
2.票据与验票机制

实现SSO的步骤拆解

关键:存储票据(购票与存储),查验票据(是否有票与是否有效)

核心技术点实现原理:

比照旅游进行

第二章:同域SSO

2-1 同域SSO准备工作

教学示例流程图

clipboard.png

个人学习流程图

clipboard.png

项目搭建

说明:教学使用SSH搭建演示项目,我在学习时使用springboot搭建,源码可以在我的github上查看、下载、运行等。因为本节主要是讲SSO单点登录,并不是将项目搭建,所以搭建过程省略。

项目命名为:myssosamedomain

2-2 编写统一登录接口

编写校验工具类

package com.myimooc.sso.web.util;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

/**
 * 登录校验工具类
 * @author ZhangCheng
 * @date 2017-03-22
 * @version V1.0
 */
public class LoginCheck {
    /** 测试用户名 */
    public static final String USERNAME="user";
    /** 测试密码*/
    public static final String PASSWORD="123";
    /** Cookie键 */
    public static final String COOKIE_NAME = "ssocookie";
    /** Cookie值*/
    public static final String COOKIE_VALUE = "sso";
    
    /**
     * 登录用户名和密码校验
     * @param username 用户名
     * @param password 密码
     * @return true用户名和密码正确;false用户名或密码错误
     */
    public static boolean checkLogin(String username,String password){
        if(USERNAME.equalsIgnoreCase(username) && 
            PASSWORD.equalsIgnoreCase(password)){
            return true;
        }
        return false;
    }
    
    /**
     * 校验Cookie
     * @param request
     * @return true正确;false错误
     */
    public static boolean checkCookie(HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        if( cookies == null){
            return false;
        }
        for (Cookie cookie : cookies) {
            if(COOKIE_NAME.equals(cookie.getName()) && 
                COOKIE_VALUE.equals(cookie.getValue())){
                return true;
            }
        }
        return false;
    }
}

编写校验控制器

package com.myimooc.sso.web.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.myimooc.sso.web.util.LoginCheck;

/**
 * SSO登录控制器
 * @author ZhangCheng
 * @date 2017-03-22
 * @version V1.0
 */
@Controller
@RequestMapping("/sso")
public class LoginController {
    
    /**
     * 处理用户登录请求
     * @param username 用户名
     * @param password 密码
     * @param gotoUrl 登录成功后请求路径
     * @param response
     * @return
     */
    @PostMapping("/doLogin")
    public ModelAndView doLogin(String username,String password,
            String gotoUrl,HttpServletResponse response){
        ModelAndView mv = new ModelAndView("login_fail");
        // 校验用户名和密码
        boolean ok = LoginCheck.checkLogin(username, password);
        // 判断是否登录成功
        if(ok){
            Cookie cookie = new Cookie(LoginCheck.COOKIE_NAME,LoginCheck.COOKIE_VALUE);
            // 顶级域名下,所有应用都是可见的
            cookie.setPath("/");
            // 添加Cookie
            response.addCookie(cookie);
            mv.setViewName("redirect:"+gotoUrl);
        }
        return mv;
    }
    
    /**
     * 跳转到登录页面
     * @return
     */
    @GetMapping("/login")
    public ModelAndView login(){
        return new ModelAndView("login");
    }

}

编写登录页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<center>
    <h1>请登录</h1>
    <form action="/sso/doLogin" method="post">
        <input type="hidden" name="gotoUrl" value="${gotoUrl}"/>
        <span>用户名:</span><input type="text" name="username"/>
        <span>密    码:</span><input type="password" name="password"/>
        <input type="submit">
    </form>
</center>
</body>
</html>

编写登录失败页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录失败</title>
</head>
<body>
<center>
    <h1>登录失败</h1>
    <a href="[@common.ctx/]/sso/login">重新登录</a>
</center>
</body>
</html>

2-3 编写登录页面demo1和demo2控制器

demo1控制器

package com.myimooc.sso.demo1;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.myimooc.sso.web.util.LoginCheck;

/**
 * 
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
@Controller
public class DemoOneController {
    
    @RequestMapping("/demo1")
    public ModelAndView main(HttpServletRequest request) {
        
        ModelAndView mv = new ModelAndView();
        
        if (LoginCheck.checkCookie(request)) {
            mv.setViewName("demo1");
            return mv;
        }
        mv.addObject("gotoUrl", "/demo1");
        mv.setViewName("login");
        return mv;
    }
}

demo2控制器

package com.myimooc.sso.demo2;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.myimooc.sso.web.util.LoginCheck;

/**
 * 
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
@Controller
public class DemoTwoController {

    @RequestMapping("/demo2")
    public ModelAndView main(HttpServletRequest request) {
        
        ModelAndView mv = new ModelAndView();
        
        if (LoginCheck.checkCookie(request)) {
            mv.setViewName("demo2");
            return mv;
        }
        mv.addObject("gotoUrl", "/demo2");
        mv.setViewName("login");
        return mv;
    }
}

2-4 编写DEMO1和DEMO2的主页

demo1页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>欢迎访问demo1</title>
</head>
<body>
    <h1>这是demo1的主页</h1>
</body>
</html>

demo2页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>欢迎访问demo2</title>
</head>
<body>
    <h1>这是demo2的主页</h1>
</body>
</html>

2-5 同域SSO最终效果演示

访问demo1页面需要登录

clipboard.png

访问demo2页面需要登录

clipboard.png

在demo1页面处登录

clipboard.png

登录成功,可以访问demo1页面

clipboard.png

再次访问demo2时,不需要登录即可访问

clipboard.png

第三章:同父域SSO

3-1 准备工作

教学示例流程图

clipboard.png

个人学习流程图

clipboard.png

通过修改host文件,来模拟实现父子域名。

文件路径:C:\Windows\System32\drivers\etc\hosts

修改如下

clipboard.png

说明:正常情况下,应该分别建立三个项目,对应demo1.x.com、demo2.x.com、check.x.com。但为了演示讲解方便,我们通过项目里面对应的包名来区分是哪个项目。

clipboard.png

项目搭建
项目命名为:myssosamefather

3-2 统一登录接口

编写消息响应类

package com.myimooc.sso.util;

import java.io.Serializable;
/**
 * 消息响应对象
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
public class RespMessage implements Serializable{
    
    private static final long serialVersionUID = 1L;
    /** 响应编号 */
    private String respCode;
    /** 响应消息 */
    private String respMsg;
    
    public String getRespCode() {
        return respCode;
    }

    public void setRespCode(String respCode) {
        this.respCode = respCode;
    }

    public String getRespMsg() {
        return respMsg;
    }

    public void setRespMsg(String respMsg) {
        this.respMsg = respMsg;
    }
    
}

编写登录校验工具类

package com.myimooc.sso.util;

/**
 * 登录校验工具类
 * 
 * @author ZhangCheng
 * @date 2017-03-22
 * @version V1.0
 */
public class LoginCheck {
    /** 测试用户名 */
    public static final String USERNAME = "user";
    /** 测试密码 */
    public static final String PASSWORD = "123";
    /** Cookie键 */
    public static final String COOKIE_NAME = "ssocookie";
    /** Cookie值 */
    public static final String COOKIE_VALUE = "sso";
    
    /**
     * 登录用户名和密码校验
     * 
     * @param username
     *            用户名
     * @param password
     *            密码
     * @return true已登录;false未登录
     */
    public static boolean checkLogin(String username, String password) {
        if (USERNAME.equalsIgnoreCase(username) && PASSWORD.equalsIgnoreCase(password)) {
            return true;
        }
        return false;
    }

    /**
     * 校验Cookie
     * @param cookieName
     * @param cookieValue
     * @return
     */
    public static boolean checkCookie(String cookieName,String cookieValue) {
        if (cookieName == null || cookieName=="") {
            return false;
        }
        if (cookieValue == null || cookieValue=="") {
            return false;
        }
        if (COOKIE_NAME.equals(cookieName) && COOKIE_VALUE.equals(cookieValue)) {
            return true;
        }
        return false;
    }

}

编写http请求工具类

package com.myimooc.sso.util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import com.alibaba.fastjson.JSONObject;

/**
 * http工具类
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
public class HttpUtils {
    
    /**
     * 向指定url路径发起get请求,校验cookie
     * @param url 路径
     * @param cookieName
     * @param cookieValue
     * @return
     */
    public static RespMessage doGet(String url,String cookieName,String cookieValue){
        RespMessage respMessage = new RespMessage();
        HttpURLConnection httpURLConnection = null;
        URL targetUrl = null;
        try{
            targetUrl = new URL(url+"?cookieName="+cookieName+"&cookieValue="+cookieValue);
            httpURLConnection = (HttpURLConnection) targetUrl.openConnection();
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.connect();
            
            InputStream in = httpURLConnection.getInputStream();
            InputStreamReader isr = new InputStreamReader(in);
            BufferedReader br = new BufferedReader(isr);
            
            StringBuffer sb = new StringBuffer();
            String temp = null;
            while((temp=br.readLine())!=null){
                sb.append(temp);
            }
            
            br.close();
            isr.close();
            in.close();
            
            JSONObject resultJson = JSONObject.parseObject(sb.toString());
            respMessage.setRespCode(resultJson.getString("respCode"));
            respMessage.setRespMsg(resultJson.getString("respMsg"));
            return respMessage;
        }catch (Exception e) {
            respMessage.setRespCode("500");
            respMessage.setRespMsg("Cookie校验请求失败");
            return respMessage;
        }finally {
            if(httpURLConnection !=null){
                httpURLConnection.disconnect();
            }
        }
    }
    
}

check.x.com:编写认证中心控制器

package com.myimooc.sso.check.x.com;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.myimooc.sso.util.LoginCheck;
import com.myimooc.sso.util.RespMessage;

/**
 * SSO登录控制器
 * 
 * @author ZhangCheng
 * @date 2017-03-22
 * @version V1.0
 */
@Controller
@RequestMapping("/sso")
public class LoginController {

    /**
     * 处理用户登录请求
     * 
     * @param username
     *            用户名
     * @param password
     *            密码
     * @param gotoUrl
     *            登录成功后请求路径
     * @param response
     * @return
     */
    @PostMapping("/doLogin")
    public ModelAndView doLogin(String username, String password, String gotoUrl, HttpServletResponse response) {
        ModelAndView mv = new ModelAndView("login_fail");
        // 校验用户名和密码
        boolean ok = LoginCheck.checkLogin(username, password);
        // 判断是否登录成功
        if (ok) {
            Cookie cookie = new Cookie(LoginCheck.COOKIE_NAME, LoginCheck.COOKIE_VALUE);
            // 设置在父域下面
            cookie.setDomain("x.com");
            // 顶级域名下,所有应用都是可见的
            cookie.setPath("/");
            // 添加Cookie
            response.addCookie(cookie);
            mv.setViewName("redirect:" + gotoUrl);
        }
        return mv;
    }
    
    /**
     * 校验cookie
     * @param cookieName
     * @param cookieValue
     * @param response
     * @return
     */
    @GetMapping("/checkCookie")
    @ResponseBody
    public RespMessage checkCookie(String cookieName,String cookieValue,HttpServletResponse response){
        RespMessage result = new RespMessage();
        result.setRespCode("500");
        result.setRespMsg("CookieName或CookieValue无效");
        boolean isOk = LoginCheck.checkCookie(cookieName, cookieValue);
        if(isOk){
            result.setRespCode("200");
            result.setRespMsg("Cookie有效");
        }
        return result;
    }

    /**
     * 跳转到登录页面
     * 
     * @return
     */
    @GetMapping("/login")
    public ModelAndView login() {
        return new ModelAndView("login");
    }

}

3-3 编写demo1和demo2项目的控制层

demo1.x.com:编写demo1项目控制器

package com.myimooc.sso.demo1.x.com;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.myimooc.sso.util.HttpUtils;
import com.myimooc.sso.util.RespMessage;

/**
 * 
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
@Controller
public class DemoOneController {

    @RequestMapping("/demo1")
    public ModelAndView main(HttpServletRequest request) {
        ModelAndView mv = new ModelAndView();
        
        //校验cookie是否为空
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0){
            //校验cookie是否存在
            for(Cookie cookie : cookies){
                if("ssocookie".equals(cookie.getName())){
                    //向校验服务器发送校验请求
                    String url = "http://check.x.com:8080/sso/checkCookie";
                    RespMessage respMessage = HttpUtils.doGet(url, cookie.getName(), cookie.getValue());
                    if("200".equals(respMessage.getRespCode())){
                        mv.setViewName("demo1");
                        return mv;
                    }
                }
            }
        }
        mv.addObject("gotoUrl", "http://demo1.x.com:8080/demo1");
        mv.setViewName("login");
        return mv;
    }
}

demo2.x.com:编写demo2项目控制器

package com.myimooc.sso.demo2.x.com;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.myimooc.sso.util.HttpUtils;
import com.myimooc.sso.util.RespMessage;

/**
 * 
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
@Controller
public class DemoTwoController {

    @RequestMapping("/demo2")
    public ModelAndView main(HttpServletRequest request) {
        ModelAndView mv = new ModelAndView();
        
        //校验cookie是否为空
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0){
            //校验cookie是否存在
            for(Cookie cookie : cookies){
                if("ssocookie".equals(cookie.getName())){
                    //向校验服务器发送校验请求
                    String url = "http://check.x.com:8080/sso/checkCookie";
                    RespMessage respMessage = HttpUtils.doGet(url, cookie.getName(), cookie.getValue());
                    if("200".equals(respMessage.getRespCode())){
                        mv.setViewName("demo2");
                        return mv;
                    }
                }
            }
        }
        mv.addObject("gotoUrl", "http://demo2.x.com:8080/demo2");
        mv.setViewName("login");
        return mv;
    }
}

3-4 编写登录页

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<center>
    <h1>请登录</h1>
    <form action="http://check.x.com:8080/sso/doLogin" method="post">
        <input type="hidden" name="gotoUrl" value="${gotoUrl}"/>
        <span>用户名:</span><input type="text" name="username"/>
        <span>密    码:</span><input type="password" name="password"/>
        <input type="submit">
    </form>
</center>
</body>
</html>

3-5 同父域情况下的SSO效果演示

请注意观察浏览器URL地址

访问demo1.x.com:需要登录

clipboard.png

访问demo2.x.com:需要登录

clipboard.png

在demo1.x.com处登录成功后,跳转至demo1页面

clipboard.png

再访问demo2.x.com的demo2页面时,不再需要登录了

clipboard.png

第四章:跨域SSO的实现

4-1 准备工作

教学示例流程图

clipboard.png

个人学习流程图

clipboard.png

通过修改host文件,来模拟实现跨域应用。

文件路径:C:\Windows\System32\drivers\etc\hosts

修改如下

clipboard.png

说明:正常情况下,应该分别建立三个项目,对应www.a.com、www.b.com、www.x.com。但为了演示讲解方便,我们通过项目里面对应的包名来区分是哪个项目。

clipboard.png

项目搭建
项目命名为:myssocrossdomain

完成后的目录结构如下

clipboard.png

4-2 编写统一登录接口

编写消息响应类

package com.myimooc.sso.util;

import java.io.Serializable;
import java.util.Map;
/**
 * 消息响应对象
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
public class RespMessage implements Serializable{
    
    private static final long serialVersionUID = 1L;
    /** 响应编号 */
    private String respCode;
    /** 响应消息 */
    private String respMsg;
    /** 响应数据 */
    private Map<String,Object> respArgs;
    
    public String getRespCode() {
        return respCode;
    }

    public void setRespCode(String respCode) {
        this.respCode = respCode;
    }

    public String getRespMsg() {
        return respMsg;
    }

    public void setRespMsg(String respMsg) {
        this.respMsg = respMsg;
    }
    

    public Map<String, Object> getRespArgs() {
        return respArgs;
    }

    public void setRespArgs(Map<String, Object> respArgs) {
        this.respArgs = respArgs;
    }

    @Override
    public String toString() {
        return "RespMessage [respCode=" + respCode + ", respMsg=" + respMsg + ", respArgs=" + respArgs + "]";
    }
    
    
}

编写http请求工具类

package com.myimooc.sso.util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

import com.alibaba.fastjson.JSONObject;

/**
 * Http请求工具类
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
public class HttpUtils {
    
    /**
     * 向指定url路径发起get请求
     * @param url 请求路径
     * @param param 请求参数
     * @return
     */
    public static RespMessage doGet(String url,Map<String,String> param){
        RespMessage respMessage = new RespMessage();
        HttpURLConnection httpURLConnection = null;
        URL targetUrl = null;
        try{
            // 拼装请求参数
            StringBuffer targetUrlStr = new StringBuffer(url).append("?");
            for(Map.Entry<String, String> entry : param.entrySet()){
                targetUrlStr.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            url = targetUrlStr.substring(0,targetUrlStr.length()-1);
            
            targetUrl = new URL(url);
            httpURLConnection = (HttpURLConnection) targetUrl.openConnection();
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.connect();
            
            InputStream in = httpURLConnection.getInputStream();
            InputStreamReader isr = new InputStreamReader(in);
            BufferedReader br = new BufferedReader(isr);
            
            StringBuffer sb = new StringBuffer();
            String temp = null;
            while((temp=br.readLine())!=null){
                sb.append(temp);
            }
            
            br.close();
            isr.close();
            in.close();
            
            JSONObject resultJson = JSONObject.parseObject(sb.toString());
            respMessage.setRespCode(resultJson.getString("respCode"));
            respMessage.setRespMsg(resultJson.getString("respMsg"));
            
            JSONObject resultJsonMap = JSONObject.parseObject(resultJson.getString("respArgs"));
            respMessage.setRespArgs(resultJsonMap);
            return respMessage;
        }catch (Exception e) {
            respMessage.setRespCode("500");
            respMessage.setRespMsg("请求发起失败");
            return respMessage;
        }finally {
            if(httpURLConnection !=null){
                httpURLConnection.disconnect();
            }
        }
    }
}

www.x.com:编写认证中心校验工具类

package com.myimooc.sso.www.x.com;

/**
 * 登录校验工具类
 * 
 * @author ZhangCheng
 * @date 2017-03-22
 * @version V1.0
 */
public class LoginCheck {
    /** 测试用户名 */
    public static final String USERNAME = "user";
    /** 测试密码 */
    public static final String PASSWORD = "123";
    /** Cookie键 */
    public static final String COOKIE_NAME = "ssocookie";
    /** Cookie值 */
    public static final String COOKIE_VALUE = "sso";
    
    /**
     * 登录用户名和密码校验
     * 
     * @param username
     *            用户名
     * @param password
     *            密码
     * @return true已登录;false未登录
     */
    public static boolean checkLogin(String username, String password) {
        if (USERNAME.equalsIgnoreCase(username) && PASSWORD.equalsIgnoreCase(password)) {
            return true;
        }
        return false;
    }

    /**
     * 校验Cookie
     * @param cookieName
     * @param cookieValue
     * @return
     */
    public static boolean checkCookie(String cookieName,String cookieValue) {
        if (cookieName == null || cookieName=="") {
            return false;
        }
        if (cookieValue == null || cookieValue=="") {
            return false;
        }
        if (COOKIE_NAME.equals(cookieName) && COOKIE_VALUE.equals(cookieValue)) {
            return true;
        }
        return false;
    }

}

www.x.com:编写认证中心控制器

package com.myimooc.sso.www.x.com;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.myimooc.sso.util.RespMessage;

/**
 * SSO登录控制器
 * 
 * @author ZhangCheng
 * @date 2017-03-22
 * @version V1.0
 */
@Controller
@RequestMapping("/sso")
public class LoginController {

    /**
     * 校验用户信息
     * @param param
     * @return
     */
    @GetMapping("/doLogin")
    @ResponseBody
    public RespMessage doLogin(String username,String password) {
        RespMessage result = new RespMessage();
        result.setRespCode("500");
        result.setRespMsg("用户名或密码错误");
        
        // 校验用户名和密码
        boolean ok = LoginCheck.checkLogin(username,password);
        // 判断是否登录成功
        if (ok) {
            result.setRespCode("200");
            result.setRespMsg("用户名和密码正确");
            
            List<Map<String,String>> targetCookies = new ArrayList<Map<String,String>>();
            
            // 向www.a.com服务器发送增加cookie
            Map<String,String> targetCookiea = new HashMap<String,String>();
            String urla = "http://www.a.com/a/addCookie";
            targetCookiea.put("targetUrl", urla);
            targetCookiea.put("cookieName", LoginCheck.COOKIE_NAME);
            targetCookiea.put("cookieValue", LoginCheck.COOKIE_VALUE);
            
            // 向www.b.com服务器发送增加cookie
            Map<String,String> targetCookieb = new HashMap<String,String>();
            String urlb = "http://www.b.com/b/addCookie";
            targetCookieb.put("targetUrl", urlb);
            targetCookieb.put("cookieName", LoginCheck.COOKIE_NAME);
            targetCookieb.put("cookieValue", LoginCheck.COOKIE_VALUE);
            
            targetCookies.add(targetCookiea);
            targetCookies.add(targetCookieb);
            
            Map<String,Object> args = new HashMap<String,Object>();
            args.put("targetCookies", targetCookies);
            
            result.setRespArgs(args);
            
        }
        return result;
    }
    
    /**
     * 校验cookie
     * @param cookieName
     * @param cookieValue
     * @param response
     * @return
     */
    @GetMapping("/checkCookie")
    @ResponseBody
    public RespMessage checkCookie(String cookieName,String cookieValue){
        RespMessage result = new RespMessage();
        result.setRespCode("500");
        result.setRespMsg("CookieName或CookieValue无效");
        boolean isOk = LoginCheck.checkCookie(cookieName, cookieValue);
        if(isOk){
            result.setRespCode("200");
            result.setRespMsg("Cookie有效");
        }
        return result;
    }
}

www.x.com:编写登录页

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
<script type="text/javascript" src="[@common.ctx /]/static/plugin/jquery/jquery.min.js"></script>
<script type="text/javascript" src="[@common.ctx /]/static/plugin/jquery/jquery.cookie.js"></script>
<script type="text/javascript" src="[@common.ctx /]/static/script/login.js"></script>
</head>
<body>
<center>
    <h1>请登录</h1>
    <form>
        <input type="hidden" id="ctx" value="${contextPath}" />
        <input id="gotoUrl_input" type="hidden" name="gotoUrl" value="${gotoUrl}"/>
        <span>用户名:</span><input id="username_input" type="text" name="username"/>
        <span>密    码:</span><input id="password_input" type="password" name="password"/>
        <input id="login_button" type="button" value="登录">
    </form>
</center>
</body>
</html>

www.x.com:编写login.js

/**
 * 登录js
 */
$(function(){
    var ctx = $("#ctx").val();
    $("#login_button").click(function(){
        login();
    });
});

function login(){
    // 获取登录信息
    var username=$("#username_input").val();
    var password=$("#password_input").val();
    var path=$("#path_input").val();
    var gotoUrl=$("#gotoUrl_input").val();
    
    var requesturl="/a/doLogin";
    $.ajax({
        type:"POST",
        async:false,//发送同步请求
        url:requesturl,
        data:"username="+username+"&password="+password,
        success:function(result){
            // 登录失败
            if(result.respCode != 200 ){
                alert(result.respMsg);
                return;
            }
            // 登录成功
            var targetCookies = result.respArgs.targetCookies;
            
            // 向服务器发出添加cookie请求
            $.each(targetCookies,function(i,targetCookie){
                var targetUrl = targetCookie.targetUrl;
                var cookieName = targetCookie.cookieName;
                var cookieValue = targetCookie.cookieValue;
                creat(targetUrl,cookieName,cookieValue);
            });
        }
    });
    // 跳转到目标页
    window.location.href=gotoUrl;
}
/** js利用iframe实现跨域添加cookie */
function creat(targetUrl,cookieName,cookieValue){
    var iframe = document.createElement('iframe'); 
    var targetSrc = targetUrl+"?"+"cookieName="+cookieName+"&cookieValue="+cookieValue;
    iframe.src=targetSrc;
    document.body.appendChild(iframe);
}

4-3 编写登录校验接口

www.a.com:编写控制器

package com.myimooc.sso.www.a.com;

import java.util.HashMap;
import java.util.Map;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.myimooc.sso.util.RespMessage;
import com.myimooc.sso.util.HttpUtils;

/**
 * 
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
@Controller
@RequestMapping("/a")
public class DemoOneController {
    
    /**
     * 跳转到demo1的主页
     * @param request
     * @return
     */
    @RequestMapping("/demo1")
    public ModelAndView demo1(HttpServletRequest request) {
        ModelAndView mv = new ModelAndView();
        
        //校验cookie是否为空
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0){
            //校验cookie是否存在
            for(Cookie cookie : cookies){
                if("ssocookie".equals(cookie.getName())){
                    // 封装请求参数
                    Map<String,String> param = new HashMap<String,String>();
                    param.put("cookieName", cookie.getName());
                    param.put("cookieValue", cookie.getValue());
                    // 向校验服务器发送校验请求
                    String url = "http://www.x.com/sso/checkCookie";
                    RespMessage respMessage = HttpUtils.doGet(url, param);
                    // 校验通过
                    if("200".equals(respMessage.getRespCode())){
                        mv.setViewName("demo1");
                        return mv;
                    }
                }
            }
        }
        // 登录失败重新登录
        String path = request.getContextPath();
        mv.addObject("contextPath",path);
        mv.addObject("path","a");
        mv.addObject("gotoUrl", "http://www.a.com/a/demo1");
        mv.setViewName("login");
        return mv;
    }
    
    /**
     * 用户登录
     * @param param
     * @return
     */
    @PostMapping(value="/doLogin")
    @ResponseBody
    public RespMessage doLogin(@RequestParam Map<String,String> param){
        // 向校验服务器发送校验请求
        String url = "http://www.x.com/sso/doLogin";
        RespMessage respMessage = HttpUtils.doGet(url, param);
        System.out.println("SSO服务器响应消息:"+respMessage);
        return respMessage;
    }
    
    /**
     * 想当前域添加cookie
     * @param cookieName
     * @param cookieValue
     * @param response
     */
    @RequestMapping(value="/addCookie")
    public void addCookie(String cookieName,String cookieValue,HttpServletResponse response){
        Cookie cookie = new Cookie(cookieName,cookieValue);
        cookie.setPath("/");
        response.addCookie(cookie);
    }
}

www.b.com:编写控制器

package com.myimooc.sso.www.b.com;

import java.util.HashMap;
import java.util.Map;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.myimooc.sso.util.HttpUtils;
import com.myimooc.sso.util.RespMessage;

/**
 * 
 * @author ZhangCheng
 * @date 2017-04-02
 * @version V1.0
 */
@Controller
@RequestMapping("/b")
public class DemoTwoController {

    @RequestMapping("/demo2")
    public ModelAndView main(HttpServletRequest request) {
        ModelAndView mv = new ModelAndView();
        
        //校验cookie是否为空
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0){
            //校验cookie是否存在
            for(Cookie cookie : cookies){
                if("ssocookie".equals(cookie.getName())){
                 // 封装请求参数
                    Map<String,String> param = new HashMap<String,String>();
                    param.put("cookieName", cookie.getName());
                    param.put("cookieValue", cookie.getValue());
                    // 向校验服务器发送校验请求
                    String url = "http://www.x.com/sso/checkCookie";
                    RespMessage respMessage = HttpUtils.doGet(url, param);
                    // 校验通过
                    if("200".equals(respMessage.getRespCode())){
                        mv.setViewName("demo2");
                        return mv;
                    }
                }
            }
        }
        // 登录失败重新登录
        mv.addObject("contextPath",request.getContextPath());
        mv.addObject("path","b");
        mv.addObject("gotoUrl", "http://www.b.com/b/demo2");
        mv.setViewName("login");
        return mv;
    }
    
    /**
     * 用户登录
     * @param param
     * @return
     */
    @PostMapping(value="/doLogin")
    @ResponseBody
    public RespMessage doLogin(@RequestParam Map<String,String> param){
        // 向校验服务器发送校验请求
        String url = "http://www.x.com/sso/doLogin";
        RespMessage respMessage = HttpUtils.doGet(url, param);
        System.out.println("SSO服务器响应消息:"+respMessage);
        return respMessage;
    }
    
    /**
     * 向当前域添加cookie
     * @param cookieName
     * @param cookieValue
     * @param response
     */
    @RequestMapping(value="/addCookie")
    public void addCookie(String cookieName,String cookieValue,HttpServletResponse response){
        Cookie cookie = new Cookie(cookieName,cookieValue);
        cookie.setPath("/");
        response.addCookie(cookie);
    }
}

4-4 跨域SSO效果演示

注意观察浏览器URL地址

访问www.a.com的a项目需要登录

clipboard.png

访问www.b.com的b项目需要登录

clipboard.png

在www.a.com域登录

clipboard.png

登录成功

clipboard.png

www.b.com域即可直接访问,免登陆

clipboard.png

第五章:课程总结

5-1 课程总结

1.核心是COOKIE,需要注意设置的域、位置和安全性

注意COOKIE的加密

2.应用群的安全性问题:木桶效应

即应用群的安全性受限于某个安全性最低的应用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值