慕課網_《Java實現SSO單點登陸》學習總結

時間:2017年3月22日星期三
說明:本文部份內容均來自慕課網。@慕課網:http://www.imooc.com
教學示例源碼:無
我的學習源碼:https://github.com/zccodere/s...javascript

第一章:概述

1-1 課程介紹及SSO介紹

課程目標html

認識並理解SSO及其應用,並能根據其實現原理自行實現SSO。

學習內容java

1.SSO的介紹和應用體驗(以新浪爲例)
    SSO:一次登陸,到處穿梭
2.SSO的分類介紹的實現探討
    同域SSO:不一樣的應用位於同一個域名下面
    跨域SSO:不一樣的應用位於不一樣的域名下面
3.各類SSO的具體實現介紹的代碼示例

同域SSO圖示jquery

clipboard.png

1-2 SSO核心技術分析

SSO的實現步驟和原理git

以旅遊是購買的通票爲例:github

clipboard.png

SSO特色:web

1.必需要登錄一次
2.票據與驗票機制

實現SSO的步驟拆解ajax

關鍵:存儲票據(購票與存儲),查驗票據(是否有票與是否有效)

核心技術點實現原理:spring

比照旅遊進行

第二章:同域SSO

2-1 同域SSO準備工做

教學示例流程圖json

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.應用羣的安全性問題:木桶效應

即應用羣的安全性受限於某個安全性最低的應用
相關文章
相關標籤/搜索