SpringBoot模擬單點登入
SSO: Single Sign On,官方的概念:web系統由單系統發展成多系統組成的應用群,複雜性應該由系統內部承擔,而不是使用者。無論web系統內部多麼複雜,對使用者而言,都是一個統一的整體,也就是說,使用者訪問web系統的整個應用群與訪問單個系統一樣,登入/登出只要一次就夠了。
簡言之,系統內部通過某種技術實現使用者統一登入和登出,所以單點登入技術一定要包括兩部分:登入、登出。
1為什麼要用單點登入?因為Cookie不能跨域。
2如何實現單點登入?
建立許可權認證中心來處理登入和登出的問題,真正提供服務的應用服務端通過Filter將鑑權任務重定向給認證中心。
原理圖:
登入:
登出:
首先看得出分為2個角色,一個是應用服務端,也就是認證客戶端;另一個是認證服務端。根據上圖來分析2個角色應該具備的功能。
認證客戶端應該具備的能力:
1必須以Filter或者外掛等形式提供,方便系統接入SSO。
2未登陸的使用者重定向到SSO認證中心
3接收SSO發來的令牌並將該令牌發回給SSO做令牌認證
4處理令牌認證結果並建立區域性會話
5攔截使用者登出請求並重定向到SSO
6處理SSO發來的登出會話請求
認證服務端應該具備的能力:
0獨立的web服務
1提供登陸頁面,和對使用者的校驗
2建立全域性會話、提供token
3校驗token有效性並維護client端地址
4處理登出請求、銷燬全域性會話、並通知維護的
5判斷使用者有沒有登入
程式碼模擬:
總共4個專案,核心的是2個SSO-server和SSO-client,其中client不是獨立的web應用而是程式碼外掛。SSO-mock-app1和SSO-mock-app2是安裝了外掛的2個獨立應用。
Sso-server:https://github.com/yejingtao/forblog/tree/master/sso-server
Sso-client:https://github.com/yejingtao/forblog/tree/master/sso-client
Sso-mock-app1:https://github.com/yejingtao/forblog/tree/master/sso-mock-app1
Sso-moke-app2:https://github.com/yejingtao/forblog/tree/master/sso-mock-app2
Sso-client包括3部分:filter負責判斷使用者是否登陸和重定向到sso-server;controller負責與sso-server傳遞令牌等通訊;service維護區域性會話狀態。
Sso-server包括3部分:controller負責與sso-client傳遞令盤、校驗使用者登陸情況等通訊;service維護全域性使用者會話狀態;templates為使用者登陸提供頁面入口
下面我們按照圖中的順序來對照程式碼看一下:
1 客戶端filter判斷如果使用者沒有登入,重定向到sso-server端:
登陸過放行
if(userName!=null
&& String.valueOf(userName).trim().length()>0
&& userAccessService.checkUserStatus(userName.toString())) {
chain.doFilter(req, response);
}
沒登陸過重定向:
String originalUrl = req.getRequestURL().toString();
httpResponse.sendRedirect(ssoServerPath+"/index?originalUrl="+originalUrl+"&ssoUser="+userName);
2 sso-server端收到請求也需要判斷使用者是否登陸,使用者不存在就給到自己的登陸頁面,同時返回請求過來的連線在成功登陸後做3秒自動跳轉。
@RequestMapping("/index") public String firstCheck(HttpServletRequest request) { String originalUrl = request.getParameter("originalUrl"); String ssoUser = request.getParameter("ssoUser"); String token = null; boolean loginFlag = false; if(ssoUser!=null && ssoUser.trim().length()>0) { //對使用者先判斷是否已經登陸過 token = authSessionService.getUserToken(ssoUser); if(token!=null) { loginFlag = true; } } if(loginFlag) { //判斷如果使用者已經在SSO-Server認證過,直接傳送token if(tokenTrans(request,originalUrl,ssoUser,token)) { if(originalUrl!=null) { if(originalUrl.contains("?")) { originalUrl = originalUrl + "&ssoUser="+ssoUser; }else { originalUrl = originalUrl + "?ssoUser="+ssoUser; } } } return "redirect:"+originalUrl; }else { //需要替換成專業點的路徑,自己登陸下了 return "redirect:/loginPage?originalUrl="+request.getParameter("originalUrl"); } }
3 使用者登陸頁面登入後,驗證使用者名稱、建立token
//登陸邏輯,返回的是令牌
@RequestMapping(value="/doLogin",method=RequestMethod.POST)
public String login(HttpServletRequest request, HttpServletResponse response,
String userName, String password, String originalUrl) {
if(authSessionService.verify(userName,password)) {
String token = authSessionService.cacheSession(userName);
if(tokenTrans(request,originalUrl,userName,token)) {
//跳轉到提示成功的頁面
request.setAttribute("helloName", userName);
if(originalUrl!=null) {
if(originalUrl.contains("?")) {
originalUrl = originalUrl + "&ssoUser="+userName;
}else {
originalUrl = originalUrl + "?ssoUser="+userName;
}
request.setAttribute("originalUrl", originalUrl);
}
}
return "hello";//TO-DO 三秒跳轉
}
//驗證不通過,重新來吧
if(originalUrl!=null) {
request.setAttribute("originalUrl", originalUrl);
}
return "loginIndex";
}
重點是token如何傳給sso-client端並從clinet端註冊上來地址的:
4 server端將token傳給client端:
private boolean tokenTrans(HttpServletRequest request, String originalUrl,String userName, String token) {
String[] paths = originalUrl.split("/");
String shortAppServerUrl = paths[2];
String returnUrl = "http://"+shortAppServerUrl+"/receiveToken?ssoToken="+token+"&userName="+userName;
//http://peer1:8088/receiveToken?ssoToken=80414bcb-a71d-48c8-bfee-098a303324d4&userName=xixi
return "success".equals(restTemplate.getForObject(returnUrl, String.class));
}
5 client端將收到的token和自己的服務地址傳遞給server端:
@RequestMapping("/receiveToken")
@ResponseBody
public String receiveToken(HttpServletRequest request, String ssoToken,String userName) {
if(ssoToken!=null && ssoToken.toString().trim().length()>0) {
String realUrl = request.getRequestURL().toString();
String[] paths = realUrl.split("/");
String realUrlUrls = paths[2];
String returnUrl = ssoServerPath+"/varifyToken?address="+realUrlUrls+"&token="+ssoToken;
//http://peer2:8089/varifyToken?address=peer1:8088&token=c2ce29be-5adb-4aaf-82cc-2ba24330176e
String resultStr = restTemplate.getForObject(returnUrl, String.class);
if("true".equals(resultStr)) {
//建立區域性會話,儲存使用者狀態為已登陸
userAccessService.putUserStatus(userName, resultStr);
return "success";
}
}
return "error";
}
6 server端檢查token是否是自己發放的令牌並維護客戶端的地址
//校驗token並註冊地址
@RequestMapping(value="/varifyToken",method=RequestMethod.GET)
@ResponseBody
public String varifyToken(String token, String address) {
return String.valueOf(authSessionService.checkAndAddAddress(token, address));
}
7 令牌互動完畢後,提示使用者登陸成功並自動跳轉回使用者的app服務頁面
onload = function(){
setInterval(go,1000);
};
var x = 3;
function go(){
x--;
if(x>0){
document.getElementById('sp').innerHTML = x;
}else{
var returnUrl = [[${originalUrl}]];
location.href = returnUrl;
}
}
8 sso-client要有根據使用者名稱發起登出的能力
@RequestMapping("/ssoLogout")
@ResponseBody
public String ssoLogout(String userName) {
String userToken = userAccessService.getUserToken(userName);
if(userToken!=null) {
String returnUrl = ssoServerPath+"/logoutByToken?ssoToken="+userToken;
return restTemplate.getForObject(returnUrl, String.class);
}
return "None Token";
}
9 登出在sso-server端提供2種,可以用使用者登出,也可以用token登出,程式碼中都有提供這裡主要講根據token登出,我們假定登出請求都是從客戶端發起的
@RequestMapping(value="/logoutByToken",method=RequestMethod.GET)
@ResponseBody
public String logoutByToken(String ssoToken) {
List<String> addressList = authSessionService.logoutByToken(ssoToken);
if(addressList!=null) {
addressList.stream().forEach(s -> sendLogout2Client(s,ssoToken));
}
return "logout";
}
private void sendLogout2Client(String address,String ssoToken) {
String returnUrl = "http://"+address+"/ssoDeleteToken?ssoToken="+ssoToken;
try {
restTemplate.getForObject(returnUrl, String.class);
}catch(Exception e) {
//Log and do nothing
}
}
10 對應的客戶端要有個刪除token的接收動作
@RequestMapping("/ssoDeleteToken")
@ResponseBody
public String ssoDeleteToken(String ssoToken) {
userAccessService.deleteToken(ssoToken);
return "success";
}
11 細節需要注意下:
a 客戶端filter過濾範圍要控制好,不要將sso-server發來的請求進行過濾,否則會死迴圈
b client端和server端的Redis要維護好user和token
啟動測試:
Sso-mock-app1,植入sso-client外掛,域名使用peer1,服務地址peer1:9001
Sso-mock-app2,植入sso-client外掛,域名使用peer2,服務地址peer2:9002
Sso-Server,獨立部署,域名使用peer3,服務地址peer3:9003
都啟動之後,開始測試:
步驟二:在peer3,也就是SSO-Server的登陸頁面上用yejingtao賬戶登陸,登陸成功後3秒鐘後頁面自動跳轉到peer1的步驟一中的請求頁面。
步驟三:在app2服務嘗試yejingtao賬號直接訪問,成功。
步驟四:在SSO-Server端登出該賬號
步驟五:登出後app1和app2又要重新登入了。
整個過程中可以配合redisclient觀察redis資料幫助自己理解。
由於只是驗證和示例程式碼,好多可以優化的地方,例如
1 token已經預留了失效時間,可以在邏輯中增加對token時效的判斷,並定時清理
2 sso-server與sso-client的通訊可以改成post請求,雖然不會經過瀏覽器,從使用效果和安全級別上沒有差別,但是從http協議設計來考慮還是post更好
3 Redis序列化部分可以換成Jackson2JsonRedisSerializer節省記憶體空間
4 sso-client攔截的許可權範圍可以放在application.yml中讓使用者自己設定
如果自己產品中用SSO的話當然不用自己寫,成型有CAS等,原理當然差別不大。 還有一種解決系統間統一認證的思路,OAuth2.0,雖然也是解決多系統登入的,但是跟這裡的單點登入思路完全不一樣,等後面有機會再博文介紹。相關推薦
SpringBoot模擬單點登入
SSO: Single Sign On,官方的概念:web系統由單系統發展成多系統組成的應用群,複雜性應該由系統內部承擔,而不是使用者。無論web系統內部多麼複雜,對使用者而言,都是一個統一的整體,也就是說,使用者訪問web系統的整個應用群與訪問單個系統一樣,登入/登出
Springboot+shiro單點登入實現
import com.ymatou.envmanagement.model.User; import com.ymatou.envmanagement.util.SessionUtil; import com.ymatou.envmanagement.util.WapperUtil; import org.a
SSO單點登入,簡單模擬
SSO單點登入(以下全是個人理解,如果有誤,共同批評進步) 1.什麼是單點登入: 在不同的應用中,受保護的同一使用者,登入一次就可以訪問相關的其他系統應用。比如搜狐登入後,可以直接訪問部落格、郵箱等等,而不用再重新登入部落格系統、郵箱系統等等。方便了使用者的操作。 2.同域下單點登入
SpringBoot+MyBatis+Redis實現SSO單點登入系統(二)
SpringBoot+MyBatis+Redis實現SSO單點登入系統(二) 三、程式碼 配置檔案配置資料庫,redis等相關的資訊。 # See http://docs.spring.io/spring-boot/docs/current/reference/html
SpringBoot+MyBatis+Redis實現SSO單點登入系統(一)
SpringBoot+MyBatis+Redis實現SSO單點登入系統(一) 一、SSO系統概述 SSO英文全稱Single Sign On,單點登入。SSO是在多個應用系統中,使用者只需要
基於CAS的單點登入SSO[5]: 基於Springboot實現CAS客戶端的前後端分離
基於CAS的單點登入SSO[5]: 基於Springboot實現CAS客戶端的前後端分離 作者:家輝,日期:2017-08-24 CSDN部落格: http://blog.csdn.net/gobitan 摘要:現在大部分系統的開發都已經
據說是springboot下實現cas的單點登入(但是我總感覺是MVC)----基於前後臺分離的
一、前言 前後端分離開發是目前軟體開發的主流,大大提高了開發效率 但也帶來了很多不方便之處。 1、優點: ① 傳統全棧開發的 MVC 模式將不適合,後臺採取 MVP 面向介面程式設計,耦合度大大降低 2、缺點: ① 跨域問題不勝其擾 3、原則:&n
Spring Boot WebSocket 單節點模擬實現單點登入擠退
1、建立WebSocketServer @ServerEndpoint("/websocket/{sid}") @Component // 成分、元件 public class WebSocketServer { //靜態變數,用來記錄當前線上
Springboot 使用Redis+Session實現Session共享 , 實現單點登入
話說在前: 在你開啟我的這篇東西的時候,你應該學會了基於springboot專案使用Redis了,因為我不會在這篇過多去介紹·從redis基本配置。 然後, 你如果登入這模組,使用了shiro 或者說是 security ,沒關係的。 我也是結合shiro一起使用的。
CAS5.3單點登入服務端搭建與整合springboot
什麼是單點登入 單點登入(Single Sign On),簡稱為 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的應用系統。 SSO是概念,實現SSO需要用到CAS框架 使用cas框架實現單點登陸 有多個不同伺服器的
基於springboot和redis實現的單點登入
1、具體的加密和解密方法 package com.example.demo.util; import com.google.common.base.Strings; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder
SpringBoot+SpringSession+Redis實現session共享及單點登入
最近在學習springboot,session這個點一直困擾了我好久,今天把這些天踩的坑分享出來吧,希望能幫助更多的人。 一、pom.xml配置 <dependency> <groupId>org.springframework.boot
cas單點登入 (二) 客戶端與springboot整合
在springboot專案中實現cas單點登入統一認證,只需要在專案中配置 cas過濾器即可使用. 1. springboot專案pom.xml中 新增web支援依賴 、cas客戶端依賴包 <dependency> <groupId>org
(十二)Java B2B2C多使用者商城 springboot架構-SSO單點登入之OAuth2.0 登出流程(3)
上一篇我根據框架中OAuth2.0的使用總結,畫了一個根據使用者名稱+密碼實現OAuth2.0的登入認證的流程圖,今天我們看一下logout的流程: /** * 使用者登出 * @param accessToken * @return */ @R
(十三)java springboot b2b2c多使用者商城系統分析 - SSO單點登入之OAuth2.0 根據token獲取使用者資訊(4)
上一篇我根據框架中OAuth2.0的使用總結,畫了SSO單點登入之OAuth2.0 登出流程,今天我們看一下根據使用者token獲取yoghurt資訊的流程: /** * 根據token獲取使用者資訊 * @param accessToken * @return * @
(十二)java springboot b2b2c shop 多使用者商城系統原始碼- SSO單點登入之OAuth2.0 登出流程(3)
上一篇我根據框架中OAuth2.0的使用總結,畫了一個根據使用者名稱+密碼實現OAuth2.0的登入認證的流程圖,今天我們看一下logout的流程: /** * 使用者登出 * @param accessToken * @return */
(十一)JAVA springboot ssm b2b2c多使用者商城系統 - SSO單點登入之OAuth2.0登入流程(2)
上一篇是站在巨人的肩膀上去研究OAuth2.0,也是為了快速幫助大家認識OAuth2.0,閒話少說,我根據框架中OAuth2.0的使用總結,畫了一個簡單的流程圖(根據使用者名稱+密碼實現OAuth2.0的登入認證): 上面的圖很清
1 springboot的 springboot security OAuth的sso單點登入:
整個工程包括三個獨立的應用,一個認證服務和兩個客戶端應用,結構非常簡單。 Maven依賴 <dependency> <groupId>org.springframework.boot</groupId> &l
springboot+security+JWT實現單點登入
本次整合實現的目標:1、SSO單點登入2、基於角色和spring security註解的許可權控制。 整合過程如下: 1、使用maven構建專案,加入先關依賴,pom.xml如下: <?xml version="1.0" encoding="UTF-8"?> <project
CAS單點登入-客戶端整合(shiro、springboot、jwt、pac4j)(十)
CAS單點登入-客戶端整合(shiro springboot jwt pac4j)(十) 由於我們通常在業務上會有以下的使用場景: 移動端通過業務系統鑑權 移動端免登入(登入一次以後) 解決方案: JWT(token認證方案) OAuth(第三方認