分布式單點登錄SSO
在分布式系統架構中, 如果用戶進行了登錄操作, 如果讓用戶在多個業務子系統中都能免密登錄呢?
當然想到讀寫速度快, 而且多個服務器能共同訪問, 優先想到的應該是redis緩存
SSO介紹
單點登錄(SingleSignOn,SSO),就是通過用戶的一次性鑒別登錄。當用戶在身份認證服務器上登錄一次以后,即可獲得訪問單點登錄系統中其他關聯系統和應用軟件的權限,同時這種實現是不需要管理員對用戶的登錄狀態或其他信息進行修改的,這意味着在多個應用系統中,用戶只需一次登錄就可以訪問所有相互信任的應用系統。這種方式減少了由登錄產生的時間消耗,輔助了用戶管理,是目前比較流行的
SSO實現流程
實現步驟:
- 當用戶輸入用戶名和密碼之后點擊提交按鈕.將數據傳給JT-WEB服務器.
- JT-WEB利用RPC方式訪問JT-SSO 校驗數據是否有效.
- 如果校驗的數據准備無誤,之后將用戶信息保存到Redis中. key 使用uuid, value使用userJSON. 並且設定7天超時.
- JT-SSO 將服務器數據返回給JT-WEB服務器.(如果用戶名和密碼錯誤的則返回為null即可).
- JT-WEB服務器將數據保存到Cookie中.(要求實現Cookie共享).
主要實現代碼
jt-web為前端服務器, jt-sso為單點登錄服務器, 使用dubbo進行通信
controller編寫(jt-web)
/**
* 1.url地址:http://www.jt.com/user/doLogin?r=0.04360522021726099
* 2.參數: {username:_username,password:_password},
* 3.返回值結果: SysResult
*
* 需求1: 將cookie名稱為 "JT_TICKET"數據輸出到瀏覽器中,要求7天超時.
* 並且實現"jt.com"數據共享
*
* Cookie特點:
* 1.瀏覽器中只能查看當前網址下的Cookie信息
* 2.doMain 表示cookie共享的策略
* doMain:www.jd.com 當前的Cookie數據只能在當前域名中使用
* doMain:.jd.com 當前Cookie是共享的可以在域名為jd.com結尾的域名中共享.
*
**/
@RequestMapping("/doLogin")
@ResponseBody
public SysResult doLogin(User user, HttpServletResponse response){
//完成用戶登錄操作 之后獲取ticket密鑰信息
String ticket = userService.doLogin(user);
if(StringUtils.isEmpty(ticket)){
//如果為null,則說明用戶名或密碼有問題
return SysResult.fail();
}
//1.創建Cookie對象, 將uuid發送代前端cookie保存
Cookie cookie = new Cookie("JT_TICKET",ticket);
//2.設定cookie存活的時間 value=-1 當用戶關閉會話時,cookie刪除
//2.設定cookie存活的時間 value= 0 立即刪除cookie
//2.設定cookie存活的時間 value= >0 設定cookie超時時間
cookie.setMaxAge(7*24*60*60);
//3.在jt.com的域名中實現數據共享.
cookie.setDomain("jt.com");
cookie.setPath("/"); //一般情況下都是/
//4.將數據保存到瀏覽器中
response.addCookie(cookie);
return SysResult.success();
}
service層編寫(jt-sso)
注意: 下面用的mybatisplus查詢的數據庫
/**
* 目的: 校驗用戶信息是否有效並且實現單點登錄操作.
* 步驟:
* 1.校驗用戶名和密碼是否正確(密碼明文轉化為密文)
* 2.查詢數據庫檢查是否有結果
* 3.如果有結果,則動態生成TICKET信息(uuid),將user對象轉化為JSON
* 4.將數據保存到redis中,並且設定超時時間.
* 5.返回當前用戶登錄的密鑰.
*
* @param user
* @return 返回uuid
*/
@Override
public String doLogin(User user) {
String passwork = user.getPassword();
String md5Pass = DigestUtils.md5DigestAsHex(passwork.getBytes()); // 將密碼MD5加密
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", user.getUsername())
.eq("password", md5Pass);
//獲取的是用戶的全部記錄 (包含涉密信息)
User userDB = userMapper.selectOne(queryWrapper); // 在數據庫中查詢用戶
//校驗數據是否有效
if( userDB == null){
//用戶名和密碼錯誤
return null;
}
//如果程序執行到這里,說明用戶名和密碼正確的. 開啟單點登錄流程
// uuid去掉橫杠-
String ticket = UUID.randomUUID() .toString().replace("-", "");
//脫敏處理 余額/密碼/手機號/家庭地址
userDB.setPassword("123456你信不???");
String userJSON = ObjectMapperUtil.toJSON(userDB);
//保存的redis, 時間為7天有效, 操作時保證原子性
jedisCluster.setex(ticket, 7*24*60*60, userJSON);
return ticket;
}