<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          大廠多個(gè)系統(tǒng)賬號(hào)是如何互通的?

          共 16017字,需瀏覽 33分鐘

           ·

          2024-04-11 06:30

          a85d4d4a5cadcce2bf6ecb7a21766f0e.webp來源:c1n.cn/lGL6r


          • 背景
          • 傳統(tǒng) Session 機(jī)制及身份認(rèn)證方案
            • Cookie 與服務(wù)器的交互
            • 服務(wù)器端的 session 的機(jī)制
            • 基于 session 的身份認(rèn)證流程
          • 集群環(huán)境下的 Session 困境及解決方案
            • Session 共享方案
          • 多服務(wù)下的登陸困境及 SSO 方案
            • SSO 的產(chǎn)生背景
            • SSO 的底層原理 CAS

          背景

          最近開發(fā)新產(chǎn)品,然后老板說我們現(xiàn)在系統(tǒng)太多了,每次切換系統(tǒng)登錄太麻煩了,能不能做個(gè)優(yōu)化,同一賬號(hào)互通掉。作為一個(gè)資深架構(gòu)獅,老板的要求肯定要滿足,安排!

          ceaec8d2b6a918d7c58fe5811e6b41ba.webp

          圖片

          一個(gè)公司產(chǎn)品矩陣比較豐富的時(shí)候,用戶在不同系統(tǒng)之間來回切換,固然對(duì)產(chǎn)品用戶體驗(yàn)上較差,并且增加用戶密碼管理成本。

          也沒有很好地利用內(nèi)部流量進(jìn)行用戶打通,并且每個(gè)產(chǎn)品的獨(dú)立體系會(huì)導(dǎo)致產(chǎn)品安全度下降。

          因此實(shí)現(xiàn)集團(tuán)產(chǎn)品的單點(diǎn)登錄對(duì)用戶使用體驗(yàn)以及效率提升有很大的幫助。那么如何實(shí)現(xiàn)統(tǒng)一認(rèn)證呢?我們先了解一下傳統(tǒng)的身份驗(yàn)證方式。

          傳統(tǒng) Session 機(jī)制及身份認(rèn)證方案

          Cookie 與服務(wù)器的交互

          07700480b9eae1382d7760192715baae.webp

          圖片

          眾所周知,http 是無狀態(tài)的協(xié)議,因此客戶每次通過瀏覽器訪問 web。

          頁面,請(qǐng)求到服務(wù)端時(shí),服務(wù)器都會(huì)新建線程,打開新的會(huì)話,而且服務(wù)器也不會(huì)自動(dòng)維護(hù)客戶的上下文信息。

          比如我們現(xiàn)在要實(shí)現(xiàn)一個(gè)電商內(nèi)的購物車功能,要怎么才能知道哪些購物車請(qǐng)求對(duì)應(yīng)的是來自同一個(gè)客戶的請(qǐng)求呢?

          d1d4b3dd55d3c41f3578fc558e9bbbad.webp

          圖片

          因此出現(xiàn)了 session 這個(gè)概念,session 就是一種保存上下文信息的機(jī)制,他是面向用戶的,每一個(gè) SessionID 對(duì)應(yīng)著一個(gè)用戶,并且保存在服務(wù)端中。

          session 主要以 cookie 或 URL 重寫為基礎(chǔ)的來實(shí)現(xiàn)的,默認(rèn)使用 cookie 來實(shí)現(xiàn),系統(tǒng)會(huì)創(chuàng)造一個(gè)名為 JSESSIONID 的變量輸出到 cookie 中。

          JSESSIONID 是存儲(chǔ)于瀏覽器內(nèi)存中的,并不是寫到硬盤上的,如果我們把瀏覽器的cookie 禁止,則 web 服務(wù)器會(huì)采用 URL 重寫的方式傳遞 Sessionid,我們就可以在地址欄看到 sessionid=KWJHUG6JJM65HS2K6 之類的字符串。

          通常 JSESSIONID 是不能跨窗口使用的,當(dāng)你新開了一個(gè)瀏覽器窗口進(jìn)入相同頁面時(shí),系統(tǒng)會(huì)賦予你一個(gè)新的 sessionid,這樣我們信息共享的目的就達(dá)不到了。

          服務(wù)器端的 session 的機(jī)制

          當(dāng)服務(wù)端收到客戶端的請(qǐng)求時(shí)候,首先判斷請(qǐng)求里是否包含了 JSESSIONID 的 sessionId,如果存在說明已經(jīng)創(chuàng)建過了,直接從內(nèi)存中拿出來使用,如果查詢不到,說明是無效的。

          如果客戶請(qǐng)求不包含 sessionid,則為此客戶創(chuàng)建一個(gè) session 并且生成一個(gè)與此 session 相關(guān)聯(lián)的 sessionid,這個(gè) sessionid 將在本次響應(yīng)中返回給客戶端保存。

          對(duì)每次 http 請(qǐng)求,都經(jīng)歷以下步驟處理:

          • 服務(wù)端首先查找對(duì)應(yīng)的 cookie 的值(sessionid)。
          • 根據(jù) sessionid,從服務(wù)器端 session 存儲(chǔ)中獲取對(duì)應(yīng) id 的 session 數(shù)據(jù),進(jìn)行返回。
          • 如果找不到 sessionid,服務(wù)器端就創(chuàng)建 session,生成 sessionid 對(duì)應(yīng)的 cookie,寫入到響應(yīng)頭中。

          session 是由服務(wù)端生成的,并且以散列表的形式保存在內(nèi)存中。

          基于 session 的身份認(rèn)證流程

          基于 seesion 的身份認(rèn)證主要流程如下:

          58a558f56e6ea7643a370e2c673d2aa5.webp

          圖片

          因?yàn)?http 請(qǐng)求是無狀態(tài)請(qǐng)求,所以在 Web 領(lǐng)域,大部分都是通過這種方式解決。但是這么做有什么問題呢?我們接著看。

          集群環(huán)境下的 Session 困境及解決方案

          隨著技術(shù)的發(fā)展,用戶流量增大,單個(gè)服務(wù)器已經(jīng)不能滿足系統(tǒng)的需要了,分布式架構(gòu)開始流行。

          d623e62592721c6b887436a44abef629.webp

          圖片

          通常都會(huì)把系統(tǒng)部署在多臺(tái)服務(wù)器上,通過負(fù)載均衡把請(qǐng)求分發(fā)到其中的一臺(tái)服務(wù)器上,這樣很可能同一個(gè)用戶的請(qǐng)求被分發(fā)到不同的服務(wù)器上。

          因?yàn)?session 是保存在服務(wù)器上的,那么很有可能第一次請(qǐng)求訪問的 A 服務(wù)器,創(chuàng)建了 session,但是第二次訪問到了 B 服務(wù)器,這時(shí)就會(huì)出現(xiàn)取不到 session 的情況。

          我們知道,Session 一般是用來存會(huì)話全局的用戶信息(不僅僅是登陸方面的問題),用來簡化/加速后續(xù)的業(yè)務(wù)請(qǐng)求。

          傳統(tǒng)的 session 由服務(wù)器端生成并存儲(chǔ),當(dāng)應(yīng)用進(jìn)行分布式集群部署的時(shí)候,如何保證不同服務(wù)器上 session 信息能夠共享呢?

          Session 共享方案

          Session 共享一般有兩種思路:

          • session 復(fù)制
          • session 集中存儲(chǔ)
          ①session 復(fù)制

          session 復(fù)制即將不同服務(wù)器上 session 數(shù)據(jù)進(jìn)行復(fù)制,用戶登錄,修改,注銷時(shí),將 session 信息同時(shí)也復(fù)制到其他機(jī)器上面去。

          8f4d128a0ccc631826d62437d5fc274d.webp

          圖片

          這種實(shí)現(xiàn)的問題就是實(shí)現(xiàn)成本高,維護(hù)難度大,并且會(huì)存在延遲登問題。

          ②session 集中存儲(chǔ)

          40260a56e36e44014da7e4665e68421e.webp

          圖片

          集中存儲(chǔ)就是將獲取 session 單獨(dú)放在一個(gè)服務(wù)中進(jìn)行存儲(chǔ),所有獲取 session 的統(tǒng)一來這個(gè)服務(wù)中去取。

          這樣就避免了同步和維護(hù)多套 session 的問題。一般我們都是使用 redis 進(jìn)行集中式存儲(chǔ) session。

          多服務(wù)下的登陸困境及 SSO 方案

          SSO 的產(chǎn)生背景

          77c42c94398d65f33f8589761af186ba.webp

          圖片

          如果企業(yè)做大了之后,一般都有很多的業(yè)務(wù)支持系統(tǒng)為其提供相應(yīng)的管理和 IT 服務(wù),按照傳統(tǒng)的驗(yàn)證方式訪問多系統(tǒng),每個(gè)單獨(dú)的系統(tǒng)都會(huì)有自己的安全體系和身份認(rèn)證系統(tǒng)。

          進(jìn)入每個(gè)系統(tǒng)都需要進(jìn)行登錄,獲取 session,再通過 session 訪問對(duì)應(yīng)系統(tǒng)資源。

          這樣的局面不僅給管理上帶來了很大的困難,對(duì)客戶來說也極不友好,那么如何讓客戶只需登陸一次,就可以進(jìn)入多個(gè)系統(tǒng),而不需要重新登錄呢?

          a277a64e38a6fc56f527138f4f2d6003.webp

          圖片

          “單點(diǎn)登錄”就是專為解決此類問題的。其大致思想流程如下:通過一個(gè) ticket 進(jìn)行串接各系統(tǒng)間的用戶信息。

          SSO 的底層原理 CAS

          ①CAS 實(shí)現(xiàn)單點(diǎn)登錄流程

          我們知道對(duì)于完全不同域名的系統(tǒng),cookie 是無法跨域名共享的,因此 sessionId 在頁面端也無法共享,因此需要實(shí)現(xiàn)單店登錄,就需要啟用一個(gè)專門用來登錄的域名如(ouath.com)來提供所有系統(tǒng)的 sessionId。

          當(dāng)業(yè)務(wù)系統(tǒng)被打開時(shí),借助中心授權(quán)系統(tǒng)進(jìn)行登錄,整體流程如下:

          • 當(dāng) b.com 打開時(shí),發(fā)現(xiàn)自己未登陸,于是跳轉(zhuǎn)到 ouath.com 去登陸
          • ouath.com 登陸頁面被打開,用戶輸入帳戶/密碼登陸成功
          • ouath.com 登陸成功,種 cookie 到 ouath.com 域名下
          • 把 sessionid 放入后臺(tái) redis,存放<ticket,sesssionid>數(shù)據(jù)結(jié)構(gòu),然后頁面重定向到 A 系統(tǒng)
          • 當(dāng) b.com 重新被打開,發(fā)現(xiàn)仍然是未登陸,但是有了一個(gè) ticket 值
          • 當(dāng) b.com 用 ticket 值,到 redis 里查到 sessionid,并做 session 同步,然后種 cookie 給自己,頁面原地重定向
          • 當(dāng) b.com 打開自己頁面,此時(shí)有了 cookie,后臺(tái)校驗(yàn)登陸狀態(tài),成功

          整個(gè)交互流程圖如下:

          b92f95b4df74c07c2ed8d12268e99abc.webp

          圖片

          ②單點(diǎn)登錄流程演示

          CAS 登錄服務(wù) demo 核心代碼如下:

          用戶實(shí)體類:

                
                public class UserForm implements Serializable{
          private static final long serialVersionUID = 1L;

          private String username;
          private String password;
          private String backurl;

          public String getUsername() {
              return username;
          }

          public void setUsername(String username) {
              this.username = username;
          }

          public String getPassword() {
              return password;
          }

          public void setPassword(String password) {
              this.password = password;
          }

          public String getBackurl() {
              return backurl;
          }

          public void setBackurl(String backurl) {
              this.backurl = backurl;
          }

          }

          登錄控制器:

                
                @Controller
          public class IndexController {
              @Autowired
              private RedisTemplate redisTemplate;

          @GetMapping("/toLogin")
          public String toLogin(Model model,HttpServletRequest request) {
              Object userInfo = request.getSession().getAttribute(LoginFilter.USER_INFO);
              //不為空,則是已登陸狀態(tài)
              if (null != userInfo){
                  String ticket = UUID.randomUUID().toString();
                  redisTemplate.opsForValue().set(ticket,userInfo,2, TimeUnit.SECONDS);
                  return "redirect:"+request.getParameter("url")+"?ticket="+ticket;
              }
              UserForm user = new UserForm();
              user.setUsername("laowang");
              user.setPassword("laowang");
              user.setBackurl(request.getParameter("url"));
              model.addAttribute("user", user);

              return "login";
          }

          @PostMapping("/login")
          public void login(@ModelAttribute UserForm user,HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException {
              System.out.println("backurl:"+user.getBackurl());
              request.getSession().setAttribute(LoginFilter.USER_INFO,user);

              //登陸成功,創(chuàng)建用戶信息票據(jù)
              String ticket = UUID.randomUUID().toString();
              redisTemplate.opsForValue().set(ticket,user,20, TimeUnit.SECONDS);
              //重定向,回原url  ---a.com
              if (null == user.getBackurl() || user.getBackurl().length()==0){
                  response.sendRedirect("/index");
              } else {
                  response.sendRedirect(user.getBackurl()+"?ticket="+ticket);
              }
          }

          @GetMapping("/index")
          public ModelAndView index(HttpServletRequest request) {
              ModelAndView modelAndView = new ModelAndView();
              Object user = request.getSession().getAttribute(LoginFilter.USER_INFO);
              UserForm userInfo = (UserForm) user;
              modelAndView.setViewName("index");
              modelAndView.addObject("user", userInfo);
              request.getSession().setAttribute("test","123");
              return modelAndView;
          }
          }

          登錄過濾器:

                
                public class LoginFilter implements Filter {
              public static final String USER_INFO = "user";
              @Override
              public void init(FilterConfig filterConfig) throws ServletException {

          }

          @Override
          public void doFilter(ServletRequest servletRequest,
                               ServletResponse servletResponse, FilterChain filterChain)
                  throws IOException, ServletException {

              HttpServletRequest request = (HttpServletRequest) servletRequest;
               HttpServletResponse response = (HttpServletResponse)servletResponse;

              Object userInfo = request.getSession().getAttribute(USER_INFO);;

              //如果未登陸,則拒絕請(qǐng)求,轉(zhuǎn)向登陸頁面
              String requestUrl = request.getServletPath();
              if (!"/toLogin".equals(requestUrl)//不是登陸頁面
                      &amp;&amp; !requestUrl.startsWith("/login")//不是去登陸
                      &amp;&amp; null == userInfo) {//不是登陸狀態(tài)

                  request.getRequestDispatcher("/toLogin").forward(request,response);
                  return ;
              }

              filterChain.doFilter(request,servletResponse);
          }

          @Override
          public void destroy() {

          }
          }

          配置過濾器:

                
                @Configuration
          public class LoginConfig {

          //配置filter生效
          @Bean
          public FilterRegistrationBean sessionFilterRegistration() {

              FilterRegistrationBean registration = new FilterRegistrationBean();
              registration.setFilter(new LoginFilter());
              registration.addUrlPatterns("/*");
              registration.addInitParameter("paramName""paramValue");
              registration.setName("sessionFilter");
              registration.setOrder(1);
              return registration;
          }
          }

          登錄頁面:

                
                <!DOCTYPE HTML>
          <html xmlns:th="http://www.thymeleaf.org">
          <head>
              <title>enjoy login</title>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
          </head>
          <body>
          <div text-align="center">
              <h1>請(qǐng)登陸</h1>
              <form action="#" th:action="@{/login}" th:object="${user}" method="post">
                  <p>用戶名: <input type="text" th:field="*{username}" /></p>
                  <p>密  碼: <input type="text" th:field="*{password}" /></p>
                  <p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
                  <input type="text" th:field="*{backurl}" hidden="hidden" />
              </form>
          </div>


          </body>
          </html>
          web 系統(tǒng) demo 核心代碼如下:

          過濾器:

                
                public class SSOFilter implements Filter {
              private RedisTemplate redisTemplate;

          public static final String USER_INFO = "user";

          public SSOFilter(RedisTemplate redisTemplate){
              this.redisTemplate = redisTemplate;
          }
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {

          }

          @Override
          public void doFilter(ServletRequest servletRequest,
                               ServletResponse servletResponse, FilterChain filterChain)
                  throws IOException, ServletException {

              HttpServletRequest request = (HttpServletRequest) servletRequest;
              HttpServletResponse response = (HttpServletResponse)servletResponse;

              Object userInfo = request.getSession().getAttribute(USER_INFO);;

              //如果未登陸,則拒絕請(qǐng)求,轉(zhuǎn)向登陸頁面
              String requestUrl = request.getServletPath();
              if (!"/toLogin".equals(requestUrl)//不是登陸頁面
                      &amp;&amp; !requestUrl.startsWith("/login")//不是去登陸
                      &amp;&amp; null == userInfo) {//不是登陸狀態(tài)

                  String ticket = request.getParameter("ticket");
                  //有票據(jù),則使用票據(jù)去嘗試拿取用戶信息
                  if (null != ticket){
                      userInfo = redisTemplate.opsForValue().get(ticket);
                  }
                  //無法得到用戶信息,則去登陸頁面
                  if (null == userInfo){
                      response.sendRedirect("http://127.0.0.1:8080/toLogin?url="+request.getRequestURL().toString());
                      return ;
                  }

                  /**
                   * 將用戶信息,加載進(jìn)session中
                   */
                  UserForm user = (UserForm) userInfo;
                  request.getSession().setAttribute(SSOFilter.USER_INFO,user);
                  redisTemplate.delete(ticket);
              }

              filterChain.doFilter(request,servletResponse);
          }

          @Override
          public void destroy() {

          }
          }

          控制器:

                
                @Controller
          public class IndexController {
              @Autowired
              private RedisTemplate redisTemplate;

          @GetMapping("/index")
          public ModelAndView index(HttpServletRequest request) {
              ModelAndView modelAndView = new ModelAndView();
              Object userInfo = request.getSession().getAttribute(SSOFilter.USER_INFO);
              UserForm user = (UserForm) userInfo;
              modelAndView.setViewName("index");
              modelAndView.addObject("user", user);

              request.getSession().setAttribute("test","123");
              return modelAndView;
          }
          }

          首頁:

                
                <!DOCTYPE HTML>
          <html xmlns:th="http://www.thymeleaf.org">
          <head>
              <title>enjoy index</title>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
          </head>
          <body>
          <div th:object="${user}">
              <h1>cas-website:歡迎你"></h1>
          </div>
          </body>
          </html>

          ③CAS 的單點(diǎn)登錄和 OAuth2 的區(qū)別

          OAuth2: 三方授權(quán)協(xié)議,允許用戶在不提供賬號(hào)密碼的情況下,通過信任的應(yīng)用進(jìn)行授權(quán),使其客戶端可以訪問權(quán)限范圍內(nèi)的資源。

          CAS: 中央認(rèn)證服務(wù)(Central Authentication Service),一個(gè)基于 Kerberos 票據(jù)方式實(shí)現(xiàn) SSO 單點(diǎn)登錄的框架,為 Web 應(yīng)用系統(tǒng)提供一種可靠的單點(diǎn)登錄解決方法(屬于 Web SSO )。

          CAS 的單點(diǎn)登錄時(shí)保障客戶端的用戶資源的安全 ;OAuth2 則是保障服務(wù)端的用戶資源的安全 。

          CAS 客戶端要獲取的最終信息是,這個(gè)用戶到底有沒有權(quán)限訪問我(CAS 客戶端)的資源;OAuth2 獲取的最終信息是,我(oauth2 服務(wù)提供方)的用戶的資源到底能不能讓你(oauth2 的客戶端)訪問。

          因此,需要統(tǒng)一的賬號(hào)密碼進(jìn)行身份認(rèn)證,用 CAS;需要授權(quán)第三方服務(wù)使用我方資源,使用 OAuth2。

          好了,不知道大家對(duì) SSO 是否有了更深刻的理解,歡迎留言。

                  
                    

          最近面試BAT,整理一份面試資料 Java面試BATJ通關(guān)手冊(cè) ,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“ 在看 ”,關(guān)注公眾號(hào)并回復(fù) 888  領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

                    

          PS:因公眾號(hào)平臺(tái)更改了推送規(guī)則,如果不想錯(cuò)過內(nèi)容,記得讀完點(diǎn)一下 在看 ,加個(gè) 星標(biāo) ,這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。

          瀏覽 22
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  做受 视频毛片下载 | 一区二区三区永久网 | 点.免费观看毛片网站 | 日韩免费黄色电影网站 | AV电影成人天堂 |