最強(qiáng)SSO單點(diǎn)登錄教程(四)自己動手寫SSO單點(diǎn)登錄服務(wù)端和客戶端

作者:藍(lán)雄威,叩丁狼教育高級講師宛琅。原創(chuàng)文章联逻,轉(zhuǎn)載請注明出處。

一苹丸、前言

我們自己動手寫單點(diǎn)登錄的服務(wù)端目的是為了加深對單點(diǎn)登錄的理解.如果你們公司想實(shí)現(xiàn)單點(diǎn)登錄/單點(diǎn)注銷功能,推薦使用開源的單點(diǎn)登錄框架CAS.我們后面的章節(jié)也會帶同學(xué)們快速搭建CAS Server和CAS Client的環(huán)境.

二、條件

如果沒看前面章節(jié)的同學(xué),請返回去觀看這幾章內(nèi)容,不然這代碼是不太好理解的.

  • SSO單點(diǎn)登錄教程(一)多系統(tǒng)的復(fù)雜性
  • SSO單點(diǎn)登錄教程(二)單點(diǎn)登錄流程分析
  • SSO單點(diǎn)登錄教程(三)單點(diǎn)注銷流程分析

三苇经、環(huán)境要求

  • JDK1.7+
  • Maven3.3
  • Eclipse/IDEA

四赘理、準(zhǔn)備工作

因?yàn)槲覀冎饕v的是跨域的單點(diǎn)登錄,所以我們需要把不同項(xiàng)目部署到不同域名下.不可能為了完成這個代碼,讓同學(xué)們?nèi)グ⒗镌瀑I三臺主機(jī),映射三個IP.所以我們的實(shí)驗(yàn)就在本機(jī)來實(shí)現(xiàn).我們需要修改host文件,讓三個域名映射到本機(jī).
host文件存放的位置:C:\Windows\System32\drivers\etc
打開host文件之后,在最后追加如下配置:

127.0.0.1 www.sso.com
127.0.0.1 www.crm.com
127.0.0.1 www.wms.com

這段配置的意思是,我們在瀏覽器中輸入:
http://www.sso.com
http://www.crm.com
http://www.wms.com
其實(shí)訪問的都是本機(jī):127.0.0.1

PS:有些同學(xué)打開這個文件之后,保存的時候可能被拒絕.原因可能是權(quán)限不夠.解決方法:把host文件拷貝到桌面(有權(quán)限的地方即可),修改好之后再把:C:\Windows\System32\drivers\etc的host文件覆蓋.

五滥朱、下載基礎(chǔ)項(xiàng)目

基礎(chǔ)項(xiàng)目代碼下載鏈接在頁面底部.

我在github上傳的是maven結(jié)構(gòu)的項(xiàng)目.如果需要導(dǎo)入到Eclipse/IDEA中需要生成對應(yīng)的Eclipse/IDEA的配置文件.
cmd命令進(jìn)入到項(xiàng)目的根目錄 $項(xiàng)目存放位置/sso-server-base-project

  • 如果是Eclipse,運(yùn)行mvn eclipse:eclipse
  • 如果是IDEA,運(yùn)行mvn idea:idea

處理好之后,把項(xiàng)目導(dǎo)入到工具中,我們就可以開始開發(fā)了.

六鞠呈、項(xiàng)目結(jié)構(gòu)說明

服務(wù)端
sso-server-base-project目錄
  src
      main
        java
        resources
           -applicationContext.xml
        webapp
          static
          WEB-INF
              views
                -login.jsp
                -logOut.jsp
              -web.xml
  -pom.xml  

服務(wù)端項(xiàng)目就只配置了SpringMVC的環(huán)境.
pom.xml:項(xiàng)目的pom文件,已經(jīng)配置的Tomcat插件端口為:8443
applicationContext.xml:spring配置文件
static:靜態(tài)資源目錄,存放css,js
login.jsp:登陸頁面
logOut.jsp:登出頁面
web.xml:web的配置文件,配置前端請求DispatherServlet

客戶端
sso-client-base-project目錄
  src
      main
        java
          -cn.wolfcode.sso.controller.MainServlet.java
          -cn.wolfcode.sso.controller.LogOutServlet.java
        webapp
          WEB-INF
              views
                -main.jsp
              -web.xml

客戶端沒有使用Spring框架.使用Servlet3.0

@WebServlet(name = "mainServlet", urlPatterns = "/main")

在Servlet類上貼這個注解就可以進(jìn)行映射.
MainServlet.java:處理主頁請求/main的servlet.
LogOutServlet.java:處理登出的請求/logOut的servlet
main.jsp:首頁

客戶端項(xiàng)目導(dǎo)入之后,運(yùn)行tomcat7:run命令,在瀏覽器中輸入
http://www.crm.com:8088/main
會看到如下界面:

CRM項(xiàng)目首頁

七、執(zhí)行流程圖

我們代碼的開發(fā)就參考著單點(diǎn)登錄流程圖來實(shí)現(xiàn),所以我在這也把這張圖放過來.

單點(diǎn)登錄原理

八厦幅、代碼實(shí)現(xiàn)

準(zhǔn)備階段:

一:在resources目錄創(chuàng)建sso.properties,內(nèi)容如下:

#統(tǒng)一認(rèn)證中心的地址
server-url-prefix=http://www.sso.com:8443
#本項(xiàng)目的地址
client-host-url=http://www.crm.com:8088

二:添加工具類.
我們在后續(xù)的開發(fā)中需要使用這個工具類,寫得比較簡單,可以先看看,我們用到再給同學(xué)們解釋啥意思.

SSOClientUtil.java

package cn.wolfcode.sso.util;

import java.io.IOException;
import java.util.Properties;

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

public class SSOClientUtil {
    private static Properties ssoProperties = new Properties();
    public static String SERVER_URL_PREFIX;//統(tǒng)一認(rèn)證中心地址:http://www.sso.com:8443,在sso.properties配置
    public static String CLIENT_HOST_URL;//當(dāng)前客戶端地址:http://www.crm.com:8088,在sso.properties配置
    static{
        try {
            ssoProperties.load(SSOClientUtil.class.getClassLoader().getResourceAsStream("sso.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        SERVER_URL_PREFIX = ssoProperties.getProperty("server-url-prefix");
        CLIENT_HOST_URL = ssoProperties.getProperty("client-host-url");
    }
    /**
     * 當(dāng)客戶端請求被攔截,跳往統(tǒng)一認(rèn)證中心,需要帶redirectUrl的參數(shù),統(tǒng)一認(rèn)證中心登錄后回調(diào)的地址
     * 通過Request獲取這次請求的地址 http://www.crm.com:8088/main
     * 
     * @param request
     * @return
     */
    public static String getRedirectUrl(HttpServletRequest request){
        //獲取請求URL
        return CLIENT_HOST_URL+request.getServletPath();
    }
    /**
     * 根據(jù)request獲取跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的地址 http://www.sso.com:8443//checkLogin?redirectUrl=http://www.crm.com:8088/main
     * 通過Response跳轉(zhuǎn)到指定的地址
     * @param request
     * @param response
     * @throws IOException
     */
    public static void redirectToSSOURL(HttpServletRequest request,HttpServletResponse response) throws IOException {
        String redirectUrl = getRedirectUrl(request);
        StringBuilder url = new StringBuilder(50)
                .append(SERVER_URL_PREFIX)
                .append("/checkLogin?redirectUrl=")
                .append(redirectUrl);
        response.sendRedirect(url.toString());
    }
    
    
    /**
     * 獲取客戶端的完整登出地址 http://www.crm.com:8088/logOut
     * @return
     */
    public static String getClientLogOutUrl(){
        return CLIENT_HOST_URL+"/logOut";
    }
    /**
     * 獲取認(rèn)證中心的登出地址 http://www.sso.com:8443/logOut
     * @return
     */
    public static String getServerLogOutUrl(){
        return SERVER_URL_PREFIX+"/logOut";
    }
}

HttpUtil.java

package cn.wolfcode.sso.util;

import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.util.StreamUtils;

public class HttpUtil {
    /**
     * 模擬瀏覽器的請求
     * @param httpURL 發(fā)送請求的地址
     * @param params  請求參數(shù)
     * @return
     * @throws Exception
     */
    public static String sendHttpRequest(String httpURL,Map<String,String> params) throws Exception{
        //建立URL連接對象
        URL url = new URL(httpURL);
        //創(chuàng)建連接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //設(shè)置請求的方式(需要是大寫的)
        conn.setRequestMethod("POST");
        //設(shè)置需要輸出
        conn.setDoOutput(true);
        //判斷是否有參數(shù).
        if(params!=null&&params.size()>0){
            StringBuilder sb = new StringBuilder();
            for(Entry<String,String> entry:params.entrySet()){
                sb.append("&").append(entry.getKey()).append("=").append(entry.getValue());
            }
            //sb.substring(1)去除最前面的&
            conn.getOutputStream().write(sb.substring(1).toString().getBytes("utf-8"));
        }
        //發(fā)送請求到服務(wù)器
        conn.connect();
        //獲取遠(yuǎn)程響應(yīng)的內(nèi)容.
        String responseContent = StreamUtils.copyToString(conn.getInputStream(),Charset.forName("utf-8"));
        conn.disconnect();
        return responseContent;
    }
    /**
     * 模擬瀏覽器的請求
     * @param httpURL 發(fā)送請求的地址
     * @param jesssionId 會話Id
     * @return
     * @throws Exception
     */
    public static void sendHttpRequest(String httpURL,String jesssionId) throws Exception{
        //建立URL連接對象
        URL url = new URL(httpURL);
        //創(chuàng)建連接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //設(shè)置請求的方式(需要是大寫的)
        conn.setRequestMethod("POST");
        //設(shè)置需要輸出
        conn.setDoOutput(true);
        conn.addRequestProperty("Cookie","JSESSIONID="+jesssionId);
        //發(fā)送請求到服務(wù)器
        conn.connect();
        conn.getInputStream();
        conn.disconnect();
    }
}

階段一:

階段一代碼下載鏈接在頁面底部.
第一階段我們先完成,攔截客戶端的請求,判斷是否有局部會話,沒有局部會話就重定向到統(tǒng)一認(rèn)證中心的登陸界面.
需求分析:
我們要在客戶端攔截請求,應(yīng)該使用啥技術(shù)呢?如果使用的是Spring框架,我們可以使用攔截器.但我們的客戶端啥框架都沒用.要攔截請求,可以使用過濾器Filter.

客戶端

創(chuàng)建:SSOClientFilter.java,實(shí)現(xiàn)javax.servlet.Filter接口,并貼上Servlet3.0的注解

@WebFilter(filterName="SSOClientFilter",urlPatterns="/*")
public class SSOClientFilter implements Filter {
  ....
}

步驟:
1.判斷是否有局部會話
2.如果有局部會話,直接放行
3.如果沒有,重定向到統(tǒng)一認(rèn)證中心的checkLogin方法,檢查是否有全局會話.

package cn.wolfcode.sso.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import cn.wolfcode.sso.util.SSOClientUtil;
@WebFilter(filterName="SSOClientFilter",urlPatterns="/*")
public class SSOClientFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        HttpSession session = req.getSession();
        //1.判斷是否有局部的會話
        Boolean isLogin = (Boolean) session.getAttribute("isLogin");
        if(isLogin!=null && isLogin){
            //有局部會話,直接放行.
            chain.doFilter(request, response);
            return;
        }
        //沒有局部會話,重定向到統(tǒng)一認(rèn)證中心,檢查是否有其他的系統(tǒng)已經(jīng)登錄過.
        // http://www.sso.com:8443/checkLogin?redirectUrl=http://www.crm.com:8088
        //這是我們自己寫工具類的方法,同學(xué)們可以自己看一下,很簡單能看懂的.
        SSOClientUtil.redirectToSSOURL(req, resp);
    }

    @Override
    public void destroy() {}
}
服務(wù)端

步驟:
1.接受重定向過來的checkLogin請求.判斷是否有全局的會話
2.如果沒有全局會話,獲取地址欄的redirectUrl參數(shù),放入到request域中.并轉(zhuǎn)發(fā)到登陸頁面.
3.如果有全局會話,目前還沒到這個階段,這個邏輯我們先不寫.我們先按執(zhí)行流程來寫代碼.

在java目錄創(chuàng)建SSOServerController.java,并貼上@Controller注解

@Controller
public class SSOServerController {
}

編寫checkLogin方法.

package cn.wolfcode.sso.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

/**
 * Created by wolfcode-lanxw
 */
@Controller
public class SSOServerController {
    /**
     * 檢查是否有全局會話.
     * @param redirectUrl 客戶端被攔截的請求地址
     * @param session      統(tǒng)一認(rèn)證中心的會話對象
     * @param model        數(shù)據(jù)模型
     * @return              視圖地址
     */
    @RequestMapping("/checkLogin")
    public String checkLogin(String redirectUrl, HttpSession session, Model model){
        //1.判斷是否有全局的會話
        //從會話中獲取令牌信息,如果取不到說明沒有全局會話,如果能取到說明有全局會話
        String token = (String) session.getAttribute("token");
        if(StringUtils.isEmpty(token)){
            //表示沒有全局會話
            model.addAttribute("redirectUrl",redirectUrl);
            //跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的登陸頁面.已經(jīng)配置視圖解析器,
            // 會找/WEB-INF/views/login.jsp視圖
            return "login";
        }else{
            //有全局會話
            //目前這段邏輯我們先不寫,按著圖解流程編寫代碼
            return "";
        }
    }
}
測試:

服務(wù)端和客戶端代碼寫好之后,兩個項(xiàng)目都運(yùn)行tomcat7:run的命令.
在瀏覽器地址欄輸入:
www.crm.com:8088/main
發(fā)現(xiàn)我們的這個請求被攔截了,跳轉(zhuǎn)到了統(tǒng)一認(rèn)證中心的登陸界面.如下圖所示:

統(tǒng)一認(rèn)證中心登錄頁

階段二:

基礎(chǔ)項(xiàng)目代碼下載鏈接在頁面底部.

服務(wù)端:

步驟:
1.編寫登陸方法,實(shí)現(xiàn)認(rèn)證功能.
2.認(rèn)證通過,創(chuàng)建令牌.
3.創(chuàng)建全局會話存儲令牌信息
4.把令牌存入到數(shù)據(jù)庫t_token表中.

為了減低學(xué)習(xí)的難度,我們這個案例里面就不去連接數(shù)據(jù)庫(當(dāng)然要連接數(shù)據(jù)庫也不難),我們的認(rèn)證就使用靜態(tài)的認(rèn)證(賬戶名:zhangsan,密碼:666).
我們使用java中的Set集合來模擬t_token表.
創(chuàng)建MockDatabaseUtil.java來模擬數(shù)據(jù)庫

package cn.wolfcode.sso.util;
import java.util.*;
/**
 * Created by wolfcode-lanxw
 */
public class MockDatabaseUtil {
    //模擬數(shù)據(jù)庫中的t_token表
    public static Set<String> T_TOKEN = new HashSet<String>();
}

編寫統(tǒng)一認(rèn)證中心的登陸方法,在SSOServerController.java中添加login方法.

    /**
     * 登陸方法
     * @param username      前臺登陸的用戶名
     * @param password      前臺登陸的密碼
     * @param redirectUrl   客戶端被攔截的地址
     * @param session       服務(wù)端會話對象
     * @param model         模型數(shù)據(jù)
     * @return               響應(yīng)的視圖地址
     */
    @RequestMapping("/login")
    public String login(String username,String password,String redirectUrl,HttpSession session,Model model){
        if("zhangsan".equals(username)&&"666".equals(password)){
            //賬號密碼匹配
            //1.創(chuàng)建令牌信息,只要保證唯一即可,我們就使用UUID.
            String token = UUID.randomUUID().toString();
            //2.創(chuàng)建全局的會話,把令牌信息放入會話中.
            session.setAttribute("token",token);
            //3.需要把令牌信息放到數(shù)據(jù)庫中.
            MockDatabaseUtil.T_TOKEN.add(token);
            //4.重定向到redirectUrl,把令牌信息帶上.  http://www.crm.com:8088/main?token=
            model.addAttribute("token",token);
            return "redirect:"+redirectUrl;
        }
        //如果賬號密碼有誤,重新回到登錄頁面,還需要把redirectUrl放入request域中.
        model.addAttribute("redirectUrl",redirectUrl);
        return "login";
    }
客戶端:

1.統(tǒng)一認(rèn)證中心登錄成功之后,會重定向到之前客戶端被攔截的地址,并會把令牌信息在地址欄中作為參數(shù)http://www.crm.com:8088/main?token=VcnVMguCDWJX5zHa
此時訪問的是客戶端的地址,這個地址會被SSOClientFilter攔截到.
我們在Filter里面需要判斷用戶地址欄中是否有攜帶token信息,如果有,說明擁有令牌信息.但是我們得校驗(yàn)令牌token的有效性,使用HttpUrlConnection發(fā)送請求到統(tǒng)一認(rèn)證中心進(jìn)行校驗(yàn).
2.如果統(tǒng)一認(rèn)證中心給我們返回true,表示令牌有效.
3.我們創(chuàng)建局部會話,并放行請求.

SSOClientFilter.java中添加如下代碼

public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        HttpSession session = req.getSession();
        //1.判斷是否有局部的會話
        Boolean isLogin = (Boolean) session.getAttribute("isLogin");
        if(isLogin!=null && isLogin){
            //有局部會話,直接放行.
            chain.doFilter(request, response);
            return;
        }
        /**-------------------------階段二添加的代碼start---------------------------------**/
        //判斷地址欄中是否有攜帶token參數(shù).
        String token = req.getParameter("token");
        if(StringUtils.isNoneBlank(token)){
            //token信息不為null,說明地址中包含了token,擁有令牌.
            //判斷token信息是否由認(rèn)證中心產(chǎn)生的.
            //驗(yàn)證地址為:http://www.sso.com:8443/verify
            String httpURL = SSOClientUtil.SERVER_URL_PREFIX+"/verify";
            Map<String,String> params = new HashMap<String,String>();
            //把客戶端地址欄添加到的token信息傳遞給統(tǒng)一認(rèn)證中心進(jìn)行校驗(yàn)
            params.put("token", token);
            try {
                String isVerify = HttpUtil.sendHttpRequest(httpURL, params);
                if("true".equals(isVerify)){
                    //如果返回的字符串是true,說明這個token是由統(tǒng)一認(rèn)證中心產(chǎn)生的.
                    //創(chuàng)建局部的會話.
                    session.setAttribute("isLogin", true);
                    //放行該次的請求
                    chain.doFilter(request, response);
                    return;
                }
            } catch (Exception e) {
                //這里可以完善,比如出現(xiàn)異常在前臺顯示具體頁面
                //我們這個案例就不做這個哈.
                e.printStackTrace();
            }
        }
        /**-------------------------階段二添加的代碼end---------------------------------**/
        //沒有局部會話,重定向到統(tǒng)一認(rèn)證中心,檢查是否有其他的系統(tǒng)已經(jīng)登錄過.
        // http://www.sso.com:8443/checkLogin?redirectUrl=http://www.crm.com:8088
        SSOClientUtil.redirectToSSOURL(req, resp);
    }
服務(wù)端:

1.需要在統(tǒng)一認(rèn)證中心添加一個認(rèn)證令牌信息的方法.
SSOServerController.java中添加verifyToken方法,具體代碼如下:

    /**
     * 校驗(yàn)客戶端傳過來的令牌信息是否有效
     * @param token 客戶端傳過來的令牌信息
     * @return
     */
    @RequestMapping("/verify")
    @ResponseBody
    public String verifyToken(String token){
        //在模擬的數(shù)據(jù)庫表t_token中查找是否有這條記錄
        if(MockDatabaseUtil.T_TOKEN.contains(token)){
            //說明令牌有效,返回true
            return "true";
        }
        return "false";
    }
測試:

到這里為止,階段二代碼就搞定了.單點(diǎn)登錄功能的95%代碼完成.
客戶端和服務(wù)端都運(yùn)行tomcat7:run命令
在瀏覽器中按下Ctrl+Shift+Delete按鍵清楚cookie和緩存,避免干擾.
在瀏覽器中輸入:http://www.crm.com:8088/main,瀏覽器跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的登陸頁面.輸入zhangsan:666,點(diǎn)擊登陸.此時就訪問到了CRM系統(tǒng)的首頁.界面如下.

CRM系統(tǒng)首頁

階段三:

階段三代碼下載鏈接在頁面底部.
在前面的代碼我們完成了單系統(tǒng)的登陸,現(xiàn)在我們先看看如果在多系統(tǒng)的環(huán)境下,我們是否能實(shí)現(xiàn)多系統(tǒng)的下一次登陸,處處運(yùn)行的功能.

客戶端:

1.拷貝sso-client-base-project項(xiàng)目,命名為sso-client-base-project2
2.修改新項(xiàng)目的pom.xml文件第41行,Tomcat插件的啟動端口,修改為:8089
3.修改sso.properties文件,修改如下:

server-url-prefix=http://www.sso.com:8443
client-host-url=http://www.wms.com:8089

4.修改/WEB-INF/views/main.jsp的標(biāo)題,和內(nèi)容,主要方便測試的時候看到不同的效果.(可改可不改)

服務(wù)端:

需要完善checkLogin方法,添加如果有全局會話的邏輯.

    @RequestMapping("/checkLogin")
    public String checkLogin(String redirectUrl, HttpSession session, Model model){
        //1.判斷是否有全局的會話
        //從會話中獲取令牌信息,如果取不到說明沒有全局會話,如果能取到說明有全局會話
        String token = (String) session.getAttribute("token");
        if(StringUtils.isEmpty(token)){
            //表示沒有全局會話
            model.addAttribute("redirectUrl",redirectUrl);
            //跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的登陸頁面.已經(jīng)配置視圖解析器,
            // 會找/WEB-INF/views/login.jsp視圖
            return "login";
        }else{
            /**---------------------------階段三添加的代碼start--------------------**/
            //有全局會話
            //取出令牌信息,重定向到redirectUrl,把令牌帶上  
            // http://www.wms.com:8089/main?token=
            model.addAttribute("token",token);
            /**---------------------------階段三添加的代碼end-----------------------**/
            return "redirect:"+redirectUrl;
        }
    }
測試:

在服務(wù)端和兩個客戶端運(yùn)行tomcat7:run命令.
在瀏覽器中按下Ctrl+Shift+Delete按鍵清楚cookie和緩存,避免干擾.
在瀏覽器中輸入:http://www.crm.com:8088/main,瀏覽器跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的登陸頁面.輸入zhangsan:666,點(diǎn)擊登陸.此時就訪問到了CRM系統(tǒng)的首頁.說明已經(jīng)登錄成功.
接著瀏覽器中輸入:http://www.wms.com:8089/main,發(fā)現(xiàn)這次請求就不需要登陸,可以直接訪問了.到此為止,我們就完成單點(diǎn)登錄所有的代碼.可以實(shí)現(xiàn)一次登陸,處處穿梭.

九蜘澜、單點(diǎn)登錄步驟梳理:

客戶端
1.攔截客戶端的請求判斷是否有局部的session
    
    2.1如果有局部的session,放行請求.
    
    2.2如果沒有局部session
        
          2.2.1請求中有攜帶token參數(shù)

                    2.2.1.1如果有,使用HttpURLConnection發(fā)送請求校驗(yàn)token是否有效.
       
                                  2.2.1.1.1如果token有效,建立局部的session.

                                  2.2.1.1.2如果token無效,重定向到統(tǒng)一認(rèn)證中心頁面進(jìn)行登陸.

                    2.2.1.2如果沒有,重定向到統(tǒng)一認(rèn)證中心頁面進(jìn)行登陸.

         2.2.2請求中沒有攜帶token參數(shù),重定向到統(tǒng)一認(rèn)證中心頁面進(jìn)行登陸.

服務(wù)端
1.檢測客戶端在服務(wù)端是否已經(jīng)登錄了.(checkLogin方法)
    1.1獲取session中的token.
    1.2如果token不為空,說明服務(wù)端已經(jīng)登錄過了,此時重定向到客戶端的地址,并把token帶上
    1.3如果token為空,跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的的登錄頁面,并把redirectUrl放入到request域中.

2.統(tǒng)一認(rèn)證中心的登錄方法(login方法)
    2.1判斷用戶提交的賬號密碼是否正確.
    2.2如果正確
        2.2.1創(chuàng)建token(可以使用UUID,保證唯一就可以)
        2.2.2把token放入到session中,還需要把token放入到數(shù)據(jù)庫表t_token中
        2.2.3這個token要知道有哪些客戶端登陸了,存入數(shù)據(jù)庫t_client_info表中.);
        2.2.4轉(zhuǎn)發(fā)到redirectUrl地址,把token帶上.
    2.3如果錯誤
        轉(zhuǎn)發(fā)到login.jsp,還需要把redirectUrl參數(shù)放入到request域中.

3.統(tǒng)一認(rèn)證中心認(rèn)證token方法(verifyToken方法),返回值為String,貼@ResponseBody
    3.1如果MockDatabaseUtil.T_TOKEN.contains(token)結(jié)果為true,說明token是有效的.
        3.1.1返回true字符串.
    3.1如果MockDatabaseUtil.T_TOKEN.contains(token)結(jié)果為false,說明token是無效的,返回false字符串.

十施流、代碼下載

0.初始項(xiàng)目Demo

熟悉git命令的同學(xué):

客戶端的基礎(chǔ)項(xiàng)目:

git clone git@github.com:javalanxiongwei/sso-client-base-project.git
cd sso-client-base-project/
git reset --hard 8401333ea845eb32e5f6091e7326ada1983d1ea3

服務(wù)頓的基礎(chǔ)項(xiàng)目:

git clone git@github.com:javalanxiongwei/sso-server-base-project.git
cd sso-server-base-project/
git reset --hard 6334d9afa08b3d5fc886ad212b3ec62376f5ff32
不熟悉git命令的同學(xué)

客戶端的基礎(chǔ)項(xiàng)目
服務(wù)端的基礎(chǔ)項(xiàng)目


1.階段一Demo

熟悉git命令的同學(xué):

客戶端階段一:

git reset --hard b53e0234895b2044ed3042f8f856676c69160281

服務(wù)頓階段一:

git reset --hard 0ee718f408ef82d230fbc61c63b07b29b1277e45
不熟悉git命令的同學(xué)

客戶端階段一
服務(wù)頓階段一


2.階段二Demo

熟悉git命令的同學(xué):

客戶端階段二:

git reset --hard b53e0234895b2044ed3042f8f856676c69160281

服務(wù)頓階段二:

git reset --hard 0ee718f408ef82d230fbc61c63b07b29b1277e45
不熟悉git命令的同學(xué)

客戶端階段二
服務(wù)端階段二


3.階段三Demo

熟悉git命令的同學(xué):

客戶端2階段三下載:

git clone git@github.com:javalanxiongwei/sso-client-base-project2.git
cd sso-client-base-project2/
git reset --hard 01db6af390ff9f765121d3f9e9b1895b0e671bd5

服務(wù)頓階段三:

git reset --hard 80e7ad5a1d67b5d63d00e3532fed9ef58fe74fd9
不熟悉git命令的同學(xué)

客戶端2階段三
服務(wù)端階段三


WechatIMG7.jpeg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鄙信,隨后出現(xiàn)的幾起案子瞪醋,更是在濱河造成了極大的恐慌,老刑警劉巖装诡,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件银受,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸦采,警方通過查閱死者的電腦和手機(jī)宾巍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渔伯,“玉大人顶霞,你說我怎么就攤上這事÷嗪穑” “怎么了选浑?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吐限。 經(jīng)常有香客問我鲜侥,道長,這世上最難降的妖魔是什么诸典? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任描函,我火速辦了婚禮崎苗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舀寓。我一直安慰自己胆数,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布互墓。 她就那樣靜靜地躺著必尼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篡撵。 梳的紋絲不亂的頭發(fā)上判莉,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音育谬,去河邊找鬼券盅。 笑死,一個胖子當(dāng)著我的面吹牛膛檀,可吹牛的內(nèi)容都是我干的锰镀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼咖刃,長吁一口氣:“原來是場噩夢啊……” “哼泳炉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嚎杨,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤花鹅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后枫浙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翠胰,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年自脯,在試婚紗的時候發(fā)現(xiàn)自己被綠了之景。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡膏潮,死狀恐怖锻狗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情焕参,我是刑警寧澤轻纪,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站叠纷,受9級特大地震影響刻帚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涩嚣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一崇众、第九天 我趴在偏房一處隱蔽的房頂上張望掂僵。 院中可真熱鬧,春花似錦顷歌、人聲如沸锰蓬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芹扭。三九已至,卻和暖如春赦抖,著一層夾襖步出監(jiān)牢的瞬間舱卡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工队萤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灼狰,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓浮禾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親份汗。 傳聞我的和親對象是個殘疾皇子盈电,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容