什么是單點(diǎn)登錄?

      網(wǎng)友投稿 590 2025-04-01

      單點(diǎn)登錄的英文名叫做:Single Sign On(簡(jiǎn)稱 SSO )。


      在 初學(xué)/以前 的時(shí)候,一般我們就 單系統(tǒng) ,所有的功能都在同一個(gè)系統(tǒng)上。

      后來(lái),我們?yōu)榱?合理利用資源和降低耦合性 ,于是把單系統(tǒng) 拆分 成多個(gè)子系統(tǒng)。

      比如阿里系 的 淘寶和 天貓 ,很明顯地我們可以知道這是兩個(gè)系統(tǒng),但是你在使用的時(shí)候,登錄了天貓, 淘寶也會(huì) 自動(dòng)登錄。

      簡(jiǎn)單來(lái)說(shuō),單點(diǎn)登錄就是 在多個(gè)系統(tǒng)中,用戶只需一次登錄,各個(gè)系統(tǒng)即可感知該用戶已經(jīng)登錄。

      在我初學(xué) JavaWeb 的時(shí)候,登錄和注冊(cè)是我做得最多的一個(gè)功能了(初學(xué)Servlet的時(shí)候做過(guò)、學(xué) SpringMVC 的時(shí)候做過(guò)、跟著 做項(xiàng)目 的時(shí)候做過(guò)…),反正我也數(shù)不清我做了多少次登錄和注冊(cè)的功能了...這里簡(jiǎn)單講述一下我們初學(xué)時(shí)是怎么做登錄功能的。

      眾所周知,HTTP是無(wú)狀態(tài)的協(xié)議,這意味著服務(wù)器無(wú)法確認(rèn)用戶的信息。于是乎,W3C就提出了:給每一個(gè)用戶都發(fā)一個(gè)通行證,無(wú)論 誰(shuí)訪問(wèn) 的時(shí)候都需要攜帶通行證,這樣服務(wù)器就可以從通行證上確認(rèn)用戶的信息。通行證就是Cookie。

      如果說(shuō)Cookie是檢查用戶身上的 ” 通行證“來(lái)確認(rèn)用戶的身份,那么Session就是通過(guò)檢查服務(wù)器上的”客戶明細(xì)表“來(lái)確認(rèn)用戶的身份的。Session相當(dāng)于在服務(wù)器中建立了一份“客戶明細(xì)表”。

      HTTP協(xié)議是無(wú)狀態(tài)的,Session不能依據(jù)HTTP連接來(lái)判斷是否為同一個(gè)用戶。于是乎:服務(wù)器向用戶瀏覽器發(fā)送了一個(gè)名為JESSIONID的Cookie,它的值是Session的id值。其實(shí)Session是依據(jù)Cookie來(lái)識(shí)別是否是同一個(gè)用戶。

      所以,一般我們單系統(tǒng)實(shí)現(xiàn)登錄會(huì)這樣做:

      (1) 登錄:將用戶信息保存在Session對(duì)象中

      如果在Session對(duì)象中能查到,說(shuō)明已經(jīng)登錄

      如果在Session對(duì)象中查不到,說(shuō)明沒(méi)登錄(或者已經(jīng)退出了登錄)

      (2)注銷(退出登錄):從Session中刪除用戶的信息

      (3)記住我(關(guān)閉掉瀏覽器后,重新打開瀏覽器還能保持登錄狀態(tài)):配合Cookie來(lái)用

      我之前Demo的代碼,可以參考一下:

      -

      Java 代碼

      01

      /**

      02

      * 用戶登陸

      03

      */

      04

      @ PostMapping (value =?"/user/session", produces = {"application/ json;charset =UTF-8"})

      05

      public ? Result login(String mobileNo , String password, String inputCaptcha , HttpSession session, HttpServletResponse response) {

      06

      07

      //判斷驗(yàn)證 碼是否 正確

      08

      if ? ( WebUtils.validateCaptcha ( inputCaptcha ,?" captcha ", session)) {

      09

      10

      //判斷有沒(méi)有該用戶

      11

      User user = userService.userLogin ( mobileNo , password);

      12

      if ? (user !=?null) {

      13

      /*設(shè)置自動(dòng)登陸,一個(gè)星期.? 將token保存在數(shù)據(jù)庫(kù)中*/

      14

      String loginToken = WebUtils.md5(new Date(). toString () + session.getId ());

      15

      user.setLoginToken ( loginToken );

      16

      User user1 = userService.userUpload (user);

      17

      18

      session.setAttribute ("user", user1);

      19

      20

      CookieUtil.addCookie(response,"loginToken",loginToken,604800);

      21

      22

      return ResultUtil.success (user1);

      23

      24

      } else {

      25

      return ResultUtil.error ( ResultEnum.LOGIN_ERROR );

      26

      }

      27

      } else {

      28

      return ResultUtil.error ( ResultEnum.CAPTCHA_ERROR );

      29

      }

      30

      31

      }

      32

      33

      /**

      34

      * 用戶退出

      35

      */

      36

      @ DeleteMapping (value = "/session", produces = {"application/ json;charset =UTF-8"})

      37

      public Result logout( HttpSession session,HttpServletRequest request,HttpServletResponse response ) {

      38

      39

      //刪除session和cookie

      40

      session.removeAttribute ("user");

      41

      42

      CookieUtil.clearCookie (request, response, " loginToken ");

      43

      44

      return ResultUtil.success ();

      45

      }

      46

      /**

      47

      * @author ozc

      48

      * @version 1.0

      49

      *

      50

      * -;實(shí)現(xiàn)自動(dòng)登陸功能

      51

      */

      52

      public ? class ? UserInterceptor ?implements ? HandlerInterceptor {

      53

      54

      55

      @ Autowired

      56

      private ? UserService userService ;

      57

      58

      public ? boolean ? preHandle ( HttpServletRequest request, HttpServletResponse response, Object o)?throws ? Exception {

      59

      User sessionUser = (User) request.getSession (). getAttribute ("user");

      60

      61

      // 已經(jīng)登陸了,放行

      62

      if ? ( sessionUser !=?null) {

      63

      return ? true;

      64

      }?else ? {

      65

      //得到帶過(guò)來(lái)cookie是否存在

      66

      String loginToken = CookieUtil.findCookieByName (request,?" loginToken ");

      67

      if ? ( StringUtils.isNotBlank ( loginToken )) {

      68

      //到數(shù)據(jù)庫(kù)查詢有沒(méi)有該Cookie

      69

      User user = userService.findUserByLoginToken ( loginToken );

      70

      if ? (user !=?null) {

      71

      request.getSession (). setAttribute ("user", user);

      72

      return ? true;

      73

      }?else ? {

      74

      //沒(méi)有該Cookie與之對(duì)應(yīng)的用戶(Cookie不匹配)

      75

      CookieUtil.clearCookie (request, response,?" loginToken ");

      76

      return ? false;

      77

      }

      78

      }?else ? {

      79

      80

      //沒(méi)有cookie、也沒(méi)有登陸。是index請(qǐng)求獲取用戶信息,可以放行

      81

      if ? ( request.getRequestURI ().contains("session")) {

      82

      return ? true;

      83

      }

      84

      85

      //沒(méi)有cookie憑證

      86

      response.sendRedirect ("/login.html");

      87

      return ? false;

      88

      }

      89

      }

      90

      }

      什么是單點(diǎn)登錄?

      91

      }

      總結(jié)一下上面代碼的思路:

      ? 用戶登錄時(shí),驗(yàn)證用戶的賬戶和密碼

      ? 生成一個(gè)Token保存在數(shù)據(jù)庫(kù)中,將Token寫到Cookie中

      ? 將用戶數(shù)據(jù)保存在Session中

      ? 請(qǐng)求時(shí)都會(huì)帶上Cookie,檢查有沒(méi)有登錄,如果已經(jīng)登錄則放行

      Session 不 共享問(wèn)題

      單系統(tǒng)登錄功能主要是用Session保存用戶信息來(lái)實(shí)現(xiàn)的,但我們清楚的是:多系統(tǒng)即可能有多個(gè)Tomcat,而Session是依賴當(dāng)前系統(tǒng)的Tomcat,所以系統(tǒng)A的Session和系統(tǒng)B的Session是 不 共享 的。

      解決系統(tǒng)之間Session 不 共享問(wèn)題有一下幾種方案:

      ? Tomcat集群Session全局復(fù)制(集群內(nèi)每個(gè)tomcat的session完全同步)【會(huì)影響集群的性能呢,不建議】

      ? 根據(jù)請(qǐng)求的IP進(jìn)行 Hash映射 到對(duì)應(yīng)的機(jī)器上(這就相當(dāng)于請(qǐng)求的IP一直會(huì)訪問(wèn)同一個(gè)服務(wù)器)【如果服務(wù)器宕機(jī)了,會(huì)丟失了一大部分Session的數(shù)據(jù),不建議】

      ? 把Session數(shù)據(jù)放在 Redis 中(使用 Redis 模擬Session)【 建議 】

      我們可以將登錄功能 單獨(dú)抽取 出來(lái),做成一個(gè)子系統(tǒng)。

      SSO(登錄系統(tǒng))的邏輯如下:

      -

      Java 代碼

      01

      // 登錄功能(SSO單獨(dú)的服務(wù))

      02

      @Override

      03

      public ? TaotaoResult login(String username, String password)?throws ? Exception {

      04

      05

      //根據(jù)用戶名查詢用戶信息

      06

      TbUserExample example =?new ? TbUserExample ();

      07

      Criteria criteria = example.createCriteria ();

      08

      criteria.andUsernameEqualTo (username);

      09

      List< TbUser > list = userMapper.selectByExample (example);

      10

      if ? (null ? == list || list.isEmpty ()) {

      11

      return ? TaotaoResult.build (400,?"用戶不存在");

      12

      }

      13

      //核對(duì)密碼

      14

      TbUser user = list.get (0);

      15

      if ? (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {

      16

      return ? TaotaoResult.build (400,?"密碼錯(cuò)誤");

      17

      }

      18

      //登錄成功,把用戶信息寫入 redis

      19

      //生成一個(gè)用戶token

      20

      String token = UUID.randomUUID (). toString ();

      21

      jedisCluster.set (USER_TOKEN_KEY +?":" ? + token, JsonUtils.objectToJson (user));

      22

      //設(shè)置session過(guò)期時(shí)間

      23

      jedisCluster.expire (USER_TOKEN_KEY +?":" ? + token, SESSION_EXPIRE_TIME);

      24

      return ? TaotaoResult.ok (token);

      25

      }

      其他子系統(tǒng)登錄時(shí), 請(qǐng)求SSO(登錄系統(tǒng))進(jìn)行登錄,將返回的token寫到Cookie中 ,下次訪問(wèn)時(shí)則把Cookie帶上:

      -

      Java 代碼

      01

      public ? TaotaoResult login(String username, String password,

      02

      HttpServletRequest request, HttpServletResponse response) {

      03

      //請(qǐng)求參數(shù)

      04

      Map param =?new ? HashMap <>();

      05

      param.put ("username", username);

      06

      param.put ("password", password);

      07

      //登錄處理

      08

      String stringResult = HttpClientUtil.doPost (REGISTER_USER_URL + USER_LOGIN_URL, param );

      09

      TaotaoResult result = TaotaoResult.format ( stringResult );

      10

      //登錄出錯(cuò)

      11

      if ? ( result.getStatus () !=?200) {

      12

      return ? result;

      13

      }

      14

      //登錄成功后把取token信息,并寫入cookie

      15

      String token = (String) result.getData ();

      16

      //寫入cookie

      17

      CookieUtils.setCookie (request, response,?"TT_TOKEN", token);

      18

      //返回成功

      19

      return ? result;

      20

      21

      }

      總結(jié):

      ? SSO系統(tǒng)生成一個(gè)token,并將用戶信息存到 Redis 中,并設(shè)置過(guò)期時(shí)間

      ? 其他系統(tǒng)請(qǐng)求SSO系統(tǒng)進(jìn)行登錄,得到SSO返回的token,寫到Cookie中

      ? 每次請(qǐng)求時(shí),Cookie都會(huì)帶上,-得到token,判斷是否已經(jīng)登錄

      到這里,其實(shí)我們會(huì)發(fā)現(xiàn)其實(shí)就兩個(gè)變化:

      ? 將登陸功能抽取為一個(gè)系統(tǒng)(SSO),其他系統(tǒng)請(qǐng)求SSO進(jìn)行登錄

      ? 本來(lái)將用戶信息存到Session,現(xiàn)在將用戶信息存到 Redis

      Cookie跨域的問(wèn)題

      上面我們解決了Session不能共享的問(wèn)題,但其實(shí)還有另一個(gè)問(wèn)題。 Cookie是不能跨域的

      比如說(shuō),我們請(qǐng)求 時(shí),瀏覽器會(huì)自動(dòng)把 google.com 的Cookie帶過(guò)去給 google 的服務(wù)器,而不會(huì)把 的Cookie帶過(guò)去給 google 的服務(wù)器。

      這就意味著, 由于域名不同 ,用戶向系統(tǒng)A登錄后,系統(tǒng)A返回給瀏覽器的Cookie,用戶再請(qǐng)求系統(tǒng)B的時(shí)候不會(huì)將系統(tǒng)A的Cookie帶過(guò)去。

      針對(duì)Cookie存在跨域問(wèn)題,有幾種解決方案

      1. 服務(wù)端將Cookie寫到客戶端后,客戶端對(duì)Cookie進(jìn)行解析,將Token解析出來(lái),此后請(qǐng)求都把這個(gè)Token帶上就行了

      2. 多個(gè)域名共享Cookie,在寫到客戶端的時(shí)候設(shè)置Cookie的domain。

      3. 將Token保存在 SessionStroage 中(不依賴Cookie就沒(méi)有跨域的問(wèn)題了)

      到這里,我們已經(jīng)可以實(shí)現(xiàn)單點(diǎn)登錄了。

      CAS原理

      說(shuō)到單點(diǎn)登錄,就肯定會(huì)見(jiàn)到這個(gè)名詞:CAS (Central Authentication Service),下面說(shuō)說(shuō)CAS是怎么搞的。

      如果已經(jīng)將登錄單獨(dú)抽取成系統(tǒng)出來(lái) ,我們還能這樣玩。現(xiàn)在我們有兩個(gè)系統(tǒng),

      分別是? www.java3y.com? 和? ? www.java4y.com ,一個(gè)SSO? ? www.sso.com

      首先,用戶想要訪問(wèn)系統(tǒng)A www.java3y.com 受限的資源(比如說(shuō)購(gòu)物車功能,購(gòu)物車功能需要登錄后才能訪問(wèn)),系統(tǒng)A www.java3y.com 發(fā)現(xiàn)用戶并沒(méi)有登錄,于是 重定向到 sso 認(rèn)證中心,并將自己的地址作為參數(shù) 。請(qǐng)求的地址如下:

      ? www.sso.com?service=www.java3y.com

      sso 認(rèn)證中心發(fā)現(xiàn)用戶未登錄,將用戶引導(dǎo)至登錄頁(yè)面,用戶進(jìn)行輸入用戶名和密碼進(jìn)行登錄,用戶與認(rèn)證中心建立 全局會(huì)話(生成一份Token,寫到Cookie中,保存在瀏覽器上)

      隨后,認(rèn)證中心 重定向 回系統(tǒng) A ,并把Token攜帶過(guò)去給系統(tǒng)A,重定向的地址如下:

      ? www.java3y.com?token=xxxxxxx

      接著,系統(tǒng)A去 sso 認(rèn)證中心驗(yàn)證這個(gè)Token是否正確,如果正確,則系統(tǒng)A和用戶建立局部會(huì)話( 創(chuàng)建Session )。到此,系統(tǒng)A和用戶已經(jīng)是登錄狀態(tài)了。

      此時(shí),用戶想要訪問(wèn)系統(tǒng)B www.java4y.com 受限的資源(比如說(shuō)訂單功能,訂單功能需要登錄后才能訪問(wèn)),系統(tǒng)B www.java4y.com 發(fā)現(xiàn)用戶并沒(méi)有登錄,于是 重定向到 sso 認(rèn)證中心,并將自己的地址作為參數(shù) 。請(qǐng)求的地址如下:

      ? www.sso.com?service=www.java4y.com

      注意,因?yàn)橹坝脩襞c認(rèn)證中心 www.sso.com 已經(jīng)建立了全局會(huì)話(當(dāng)時(shí)已經(jīng)把Cookie保存到瀏覽器上了),所以這次系統(tǒng)B 重定向 到認(rèn)證中心 www.sso.com 是可以帶上Cookie的。

      認(rèn)證中心 根據(jù)帶過(guò)來(lái)的Cookie 發(fā)現(xiàn)已經(jīng)與用戶建立了全局會(huì)話了,認(rèn)證中心 重定向 回系統(tǒng) B ,并把Token攜帶過(guò)去給系統(tǒng)B,重定向的地址如下:

      ? www.java4y.com?token=xxxxxxx

      接著,系統(tǒng)B去 sso 認(rèn)證中心驗(yàn)證這個(gè)Token是否正確,如果正確,則系統(tǒng)B和用戶建立局部會(huì)話( 創(chuàng)建Session )。到此,系統(tǒng)B和用戶已經(jīng)是登錄狀態(tài)了。

      看到這里,其實(shí)SSO認(rèn)證中心就類似一個(gè) 中轉(zhuǎn)站 。

      Java

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:Excel進(jìn)行表格在多個(gè)窗口打開的操作方法
      下一篇:費(fèi)用管理系統(tǒng)
      相關(guān)文章
      亚洲Av熟妇高潮30p| 亚洲AV无码一区二区一二区| 亚洲综合激情五月丁香六月| 亚洲色图.com| 久久综合亚洲色HEZYO国产| 一区国严二区亚洲三区| 国产成人 亚洲欧洲| 国产亚洲精品美女久久久久| 综合一区自拍亚洲综合图区| 亚洲AV噜噜一区二区三区| 亚洲成a人无码亚洲成www牛牛 | 亚洲综合无码一区二区痴汉| 国产成人精品日本亚洲18图 | 亚洲综合精品香蕉久久网| 中文国产成人精品久久亚洲精品AⅤ无码精品 | 亚洲国产av美女网站| 久久久久精品国产亚洲AV无码| 久久精品国产亚洲AV忘忧草18 | 国产AV无码专区亚洲AWWW| 亚洲一区AV无码少妇电影☆| 亚洲女久久久噜噜噜熟女| 亚洲AV无码乱码在线观看富二代 | 久久久久久亚洲精品成人| 亚洲电影免费在线观看| 亚洲色欲或者高潮影院| 亚洲av成人一区二区三区| 亚洲熟妇自偷自拍另欧美| 综合一区自拍亚洲综合图区| 亚洲免费在线观看| 亚洲人成无码网站| 亚洲avav天堂av在线不卡| 亚洲美女视频免费| 亚洲综合久久精品无码色欲| 亚洲GV天堂GV无码男同| 亚洲人成人无码网www国产| 亚洲精品乱码久久久久久蜜桃不卡 | 亚洲精华国产精华精华液| 婷婷亚洲综合一区二区 | 亚洲国产精品综合福利专区| 亚洲最大天堂无码精品区| 国产亚洲成在线播放va|