SSO英文全稱Single Sign On,單點登陸。SSO是在多個應用系統中,用戶只須要登陸一次就能夠訪問全部相互信任的應用系統。它包括能夠將此次主要的登陸映射到其餘應用中用於同一個用戶的登陸的機制。它是目前比較流行的企業業務整合的解決方案之一。javascript
爲何須要單點登陸?html
咱們通常經過session設計登陸,這種對於只有一個工程來講是沒有問題的。可是對於分佈式系統來講,因爲牽涉到多個子系統,若是每個系統都要輸入一遍用戶名,密碼;這會很是麻煩。所以,單點登陸應運而生。java
實現原理git
將登陸系統單獨摘出來,作成一個登陸子系統。請求登錄時候訪問這個子系統,當登錄驗證經過的時候,生成一個token存入網站頂級域名下的cookie當中。將與這個token對應的用戶狀態信息存入緩存中去,並設置生存時間,此處緩存使用的是redis。當其餘的子系訪問必須登陸纔可訪問的資源時候,必須先到登陸系統進行驗證token的存在性以及是否過時,驗證經過纔可進行訪問。ajax
token:這至關於一個令牌,無論你在哪一個子系統下面,想要獲取登錄狀態就須要得到單點登陸系統生成的token,這個token是在登錄成功時候生成,生成後保存到redis(緩存)中做爲key,key生成規則使用的是UUID,目的是爲了防止多個用戶登陸時候產生衝突,value爲用戶的信息,並設置有效時間。同時將這個key保存到cookie當中。這樣當你登陸其餘子系統時須要驗證登陸狀態的時候,能夠經過獲取瀏覽器cookie從而取出這個token並去緩存中查詢是否存在相應數據,若是存在,就說明是在登錄狀態。若是token存在而緩存中不存在說明用戶登陸狀態過時了。若是沒有token,說明用戶根本沒有登陸。redis
具體實現(util以及Result是我本身封裝的類,根據我的編碼習慣而已)數據庫
/** * 單點登陸,登陸成功後會生成一個token保存在cookie當中 * @param user 登陸用戶信息 * @param request 獲取登陸相關數據 * @param response 回覆 * @return 返回登陸結果 */ @RequestMapping(value = "/login",method = RequestMethod.POST) @ResponseBody public Result login(User user,HttpServletRequest request, HttpServletResponse response){ String url=request.getParameter("url"); //從service層獲取信息,若是登錄成功裏面會保存有token Result result= this.userService.login(user); //將cookie寫入本地根域名下面 if (result.getStatus().equals(200)){ CookiteUtils.setCookie(request,response,TOKEN_ID, (String) result.getData()); //若是url不爲空,直接跳轉回原來訪問的頁面 if (!StringUtils.isBlank(url)){ try { response.sendRedirect(url); } catch (IOException e) { e.printStackTrace(); } } } return result; }
/** * * @param user 用戶登陸相關信息 * @des: Result是我本身封裝的返回類,登陸時將token和用戶信息存入redis * @return */ public Result login(User user){ //先檢驗用戶名與密碼是否爲空 if (StringUtils.isBlank(user.getUsername())|| StringUtils.isBlank(user.getPassword())){ return Result.build(400,"用戶名或密碼不能爲空"); } //轉爲UUID與數據庫中的用戶名密碼進行比較 user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes())); //根據用戶的用戶名密碼查詢信息 User temp=userMapper.findByUsernameAndPassword(user); //若是用戶名密碼正確會查詢出信息,不對的話沒法查詢到 if(temp==null){ return Result.build(400,"用戶名或密碼不正確"); } //查詢到的話生成token做爲鍵,用戶的信息做爲值保存到redis中 String token_ID=UUID.randomUUID().toString().replaceAll("-",""); temp.setPassword(null); jedis.set(TOKEN_ID+":"+token_ID, JsonUtils.objectToJson(temp)); jedis.expire(TOKEN_ID+":"+token_ID,300); //返回給controller層,並從controller層中將token保存到根cookie中 return Result.ok(token_ID); }
最後經過jsonp進行跨域請求來進行測試,是否可以實現單點登陸的功能。json
//跨域請求,使用jsonp var _ticket=$.cookie("TOKEN_ID"); if (!_ticket){ return; } $.ajax({ type:"GET", dataType:"jsonp", url:"http://localhost:8082/user/token/"+_ticket,//這個就是驗證是否登陸或者登陸是否超時,由於咱們咱們放入redis緩存的時候設置了有效時長 success: function (data) { //登陸成功顯示用戶名 if (data.status==200){ var username=data.data.username; var html=username; $("#checkLogin").html(html); } } });
demo地址:https://gitee.com/CloseHeart/SOS跨域