<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>

          redis系列——基于Redis實(shí)現(xiàn)的單點(diǎn)登錄(Demo)

          共 16219字,需瀏覽 33分鐘

           ·

          2020-10-24 05:31

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          66套java從入門(mén)到精通實(shí)戰(zhàn)課程分享

          一、SSO技術(shù)簡(jiǎn)介

          1、基本介紹

          ? ??? ??目前的企業(yè)應(yīng)用環(huán)境中,往往有很多的應(yīng)用系統(tǒng),如辦公自動(dòng)化(OA)系統(tǒng),財(cái)務(wù)管理系統(tǒng),檔案管理系統(tǒng),信息查詢(xún)系統(tǒng)等等。這些應(yīng)用系統(tǒng)服務(wù)于企業(yè)的信息化建設(shè),為企業(yè)帶來(lái)了很好的效益。但是,用戶(hù)在使用這些應(yīng)用系統(tǒng)時(shí),并不方便。用戶(hù)每次使用系統(tǒng),都必須輸入用戶(hù)名稱(chēng)和用戶(hù)密碼,進(jìn)行身份驗(yàn)證;而且,應(yīng)用系統(tǒng)不同,用戶(hù)賬號(hào)就不同,用戶(hù)必須同時(shí)牢記多套用戶(hù)名稱(chēng)和用戶(hù)密碼。特別是對(duì)于應(yīng)用系統(tǒng)數(shù)目較多,用戶(hù)數(shù)目也很多的企業(yè),這個(gè)問(wèn)題尤為突出。問(wèn)題的原因并不是系統(tǒng)開(kāi)發(fā)出現(xiàn)失誤,而是缺少整體規(guī)劃,缺乏統(tǒng)一的用戶(hù)登錄平臺(tái)。

          ? ??? ? SSO(Single Sign-On,單點(diǎn)登錄)是身份管理中的一部分。SSO的一種較為通俗的定義是:SSO是在多個(gè)應(yīng)用系統(tǒng)中,用戶(hù)只需要登錄一次就可以訪問(wèn)所有相互信任的應(yīng)用系統(tǒng)。它包括可以將這次主要的登錄映射到其他應(yīng)用中用于同一個(gè)用戶(hù)的登錄的機(jī)制。它是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一。

          2、SSO解決的問(wèn)題

          ? ? ? ? 我們?cè)谧鯯SO之前首先要明白為什么要有單點(diǎn)登錄,即SSO在解決什么問(wèn)題?那么我們先來(lái)看一下傳統(tǒng)的登錄實(shí)現(xiàn)方式:

          以上就是傳統(tǒng)的登錄實(shí)現(xiàn)方式,但是在并發(fā)量高的情況下呢?比如現(xiàn)在有 2000~3000 的并發(fā),這時(shí)一個(gè)tomcat 不能滿足業(yè)務(wù)需求,需要做集群。如下:

          那么現(xiàn)在就會(huì)出現(xiàn)Session共享的問(wèn)題(tomcat做集群配置session復(fù)制。如果集群中節(jié)點(diǎn)很多,會(huì)形成網(wǎng)絡(luò)風(fēng)暴。推薦節(jié)點(diǎn)數(shù)量不要超過(guò)5個(gè))。此外在分布式架構(gòu)中,我們會(huì)把系統(tǒng)拆分成多個(gè)子系統(tǒng),在這些子系統(tǒng)之間進(jìn)行跳轉(zhuǎn)時(shí)也會(huì)出現(xiàn)session不能共享的問(wèn)題(和上述類(lèi)似)。

          ? ? ? ? 上述這些情況正是SSO要解決的問(wèn)題!

          3、使用SSO的好處

          • 方便用戶(hù)

          ? ??? ??用戶(hù)使用應(yīng)用系統(tǒng)時(shí),能夠一次登錄,多次使用。用戶(hù)不再需要每次輸入用戶(hù)名稱(chēng)和用戶(hù)密碼,也不需要牢記多套用戶(hù)名稱(chēng)和用戶(hù)密碼。單點(diǎn)登錄平臺(tái)能夠改善用戶(hù)使用應(yīng)用系統(tǒng)的體驗(yàn)。

          • 方便管理員

          ? ??? ??系統(tǒng)管理員只需要維護(hù)一套統(tǒng)一的用戶(hù)賬號(hào),方便、簡(jiǎn)單。相比之下,系統(tǒng)管理員以前需要管理很多套的用戶(hù)賬號(hào)。每一個(gè)應(yīng)用系統(tǒng)就有一套用戶(hù)賬號(hào),不僅給管理上帶來(lái)不方便,而且,也容易出現(xiàn)管理漏洞。

          • 簡(jiǎn)化應(yīng)用系統(tǒng)開(kāi)發(fā)

          ? ??? ??開(kāi)發(fā)新的應(yīng)用系統(tǒng)時(shí),可以直接使用單點(diǎn)登錄平臺(tái)的用戶(hù)認(rèn)證服務(wù),簡(jiǎn)化開(kāi)發(fā)流程。單點(diǎn)登錄平臺(tái)通過(guò)提供統(tǒng)一的認(rèn)證平臺(tái),實(shí)現(xiàn)單點(diǎn)登錄。因此,應(yīng)用系統(tǒng)并不需要開(kāi)發(fā)用戶(hù)認(rèn)證程序。

          4、實(shí)現(xiàn)SSO的技術(shù)

          • 基于cookies實(shí)現(xiàn)

          ? ??? ??需要注意如下幾點(diǎn):如果是基于兩個(gè)域名之間傳遞sessionid的方法可能在windows中成立,在unix&linux中可能會(huì)出現(xiàn)問(wèn)題;可以基于數(shù)據(jù)庫(kù)實(shí)現(xiàn);在安全性方面可能會(huì)作更多的考慮。另外,關(guān)于跨域問(wèn)題,雖然cookies本身不跨域,但可以利用它實(shí)現(xiàn)跨域的SSO。

          • Broker-based(基于經(jīng)紀(jì)人)

          ? ??? ??例如Kerberos等,這種技術(shù)的特點(diǎn)就是,有一個(gè)集中的認(rèn)證和用戶(hù)帳號(hào)管理的服務(wù)器。經(jīng)紀(jì)人給被用于進(jìn)一步請(qǐng)求的電子的身份存取。中央數(shù)據(jù)庫(kù)的使用減少了管理的代價(jià),并為認(rèn)證提供一個(gè)公共和獨(dú)立的”第三方”。例如Kerberos、Sesame、IBM KryptoKnight(憑證庫(kù)思想)等。

          • Agent-based(基于代理人)

          ? ??? ??在這種解決方案中,有一個(gè)自動(dòng)地為不同的應(yīng)用程序認(rèn)證用戶(hù)身份的代理程序。這個(gè)代理程序需要設(shè)計(jì)有不同的功能。比如, 它可以使用口令表或加密密鑰來(lái)自動(dòng)地將認(rèn)證的負(fù)擔(dān)從用戶(hù)移開(kāi)。代理人被放在服務(wù)器上面,在服務(wù)器的認(rèn)證系統(tǒng)和客戶(hù)端認(rèn)證方法之間充當(dāng)一個(gè)”翻譯”。例如SSH等。

          • Token-based

          ? ??? ??例如SecurID、WebID;現(xiàn)在被廣泛使用的口令認(rèn)證,比如FTP,郵件服務(wù)器的登錄認(rèn)證,這是一種簡(jiǎn)單易用的方式,實(shí)現(xiàn)一個(gè)口令在多種應(yīng)用當(dāng)中使用。

          • 基于安全斷言標(biāo)記語(yǔ)言(SAML)實(shí)現(xiàn)

          ? ??? ? SAML(Security Assertion Markup Language,安全斷言標(biāo)記語(yǔ)言)的出現(xiàn)大大簡(jiǎn)化了SSO,并被OASIS批準(zhǔn)為SSO的執(zhí)行標(biāo)準(zhǔn)。開(kāi)源組織OpenSAML 實(shí)現(xiàn)了 SAML 規(guī)范,可參考http://www.opensaml.org/。

          二、SSO產(chǎn)品介紹

          1、SUN SSO技術(shù)

          1.SUM SSO介紹

          ? ??? ? SUN SSO技術(shù)是Sun Java System Access Manager產(chǎn)品中的一個(gè)組成部分。

          ? ??? ? Sun 的新身份管理產(chǎn)品包括Sun Java System Identity Manager、Sun Java System Directory Server Enterprise Edition 和 Sun Java System Access Manager,以上三者為Sun Java Identity Management Suite (身份識(shí)別管理套件)的組成部分,它們與Sun Java Application Platform Suite、Sun Java Availability Suite、Sun Java Communications Suite、Sun Java Web Infrastructure Suite組成Java ES。具有革新意義的這一系列產(chǎn)品提供端到端身份管理,同時(shí)可與 60 多種第三方資源和技術(shù)實(shí)現(xiàn)互操作,集成產(chǎn)品可以從SUN公司網(wǎng)站下載,一般以Agent軟件方式提供,是業(yè)內(nèi)集成程序最高、最為開(kāi)放的身份管理解決方案之一。

          ? ??? ??在Sun 的新身份管理產(chǎn)品中,Sun Java System Access Manager是基中的一個(gè)重要組成部分,Java Access Manager基于J2EE架構(gòu),采用標(biāo)準(zhǔn)的API,可擴(kuò)展性強(qiáng),具有高可靠性和高可用性,應(yīng)用是部署在Servlets容器中的,支持分布式,容易部署且有較低的TCO。通過(guò)使用集中驗(yàn)證點(diǎn)、其于角色的訪問(wèn)控制以及 SSO,Sun Java System Access Manager 為所有基于 Web 的應(yīng)用程序提供了一個(gè)可伸縮的安全模型。它簡(jiǎn)化了信息交換和交易,同時(shí)能保護(hù)隱私及重要身份信息的安全。

          2.SUN SSO 實(shí)現(xiàn)原理

          ? ??? ? SSO的核心在于統(tǒng)一用戶(hù)認(rèn)證,登錄、認(rèn)證請(qǐng)求通過(guò)IDENTITY SERVER服務(wù)器完成,然后分發(fā)到相應(yīng)應(yīng)用。SUN SSO是java Access Manager的一個(gè)組成部分,SSO基于Cookie實(shí)現(xiàn)解釋如下:

          (1)Policy Agent on Web or Application Server intercepts resource requests and enforces access control;

          (2)Client is issued SSO token containing information for session Validation with Session service.

          (3)SSO token has no content- just a long random string used as a handle.

          (4)Web-based applications use browser session cookies or URL rewriting to issue SSO token.

          (5)Non Web applications use the SSO API(Java/c) to obtain the SSO token to validate the users identity.

          3.SUN SSO 的應(yīng)用

          ? ??? ??這里說(shuō)的應(yīng)用是指Sun Java System Access Manager的應(yīng)用。成功應(yīng)用例子很多,包括德國(guó)電信等公司的應(yīng)用,國(guó)內(nèi)也有大量高校在使用,也有相當(dāng)多的其它行業(yè)的應(yīng)用。

          2、CAS技術(shù)

          1.CAS 背景介紹

          ? ??? ? CAS(Central Authentication Service),是耶魯大學(xué)開(kāi)發(fā)的單點(diǎn)登錄系統(tǒng)(SSO,single sign-on),應(yīng)用廣泛,具有獨(dú)立于平臺(tái)的,易于理解,支持代理功能。CAS系統(tǒng)在各個(gè)大學(xué)如耶魯大學(xué)、加州大學(xué)、劍橋大學(xué)、香港科技大學(xué)等得到應(yīng)用。

          ? ??? ? Spring Framework的Acegi安全系統(tǒng)支持CAS,并提供了易于使用的方案。Acegi安全系統(tǒng),是一個(gè)用于Spring Framework的安全框架,能夠和目前流行的Web容器無(wú)縫集成。它使用了Spring的方式提供了安全和認(rèn)證安全服務(wù),包括使用Bean Context,攔截器和面向接口的編程方式。因此,Acegi安全系統(tǒng)能夠輕松地適用于復(fù)雜的安全需求。Acegi安全系統(tǒng)在國(guó)內(nèi)外得到了廣泛的應(yīng)用,有著良好的社區(qū)環(huán)境。

          2.CAS 的設(shè)計(jì)目標(biāo)

          • 為多個(gè)Web應(yīng)用提供單點(diǎn)登錄基礎(chǔ)設(shè)施,同時(shí)可以為非Web應(yīng)用但擁有Web前端的功能服務(wù)提供單點(diǎn)登錄的功能;

          • 簡(jiǎn)化應(yīng)用認(rèn)證用戶(hù)身份的流程;

          • 將用戶(hù)身份認(rèn)證集中于單一的Web應(yīng)用,讓用戶(hù)簡(jiǎn)化他們的密碼管理,從而提高安全性;而且,當(dāng)應(yīng)用需要修改身份驗(yàn)證的業(yè)務(wù)邏輯時(shí),不需要到處修改代碼。

          3.CAS 的實(shí)現(xiàn)原理

          ? ??? ? CAS(Central Authentication Server)被設(shè)計(jì)成一個(gè)獨(dú)立的Web應(yīng)用。CAS創(chuàng)建一個(gè)位數(shù)很長(zhǎng)的隨機(jī)數(shù)(ticket)。CAS把這個(gè)ticket和成功登錄的用戶(hù)以及用戶(hù)要訪問(wèn)的service聯(lián)系起來(lái)。例如,如果用戶(hù)peon重定向自service S,CAS創(chuàng)建ticket T,這個(gè)ticket T允許peon訪問(wèn)service S。這個(gè)ticket是個(gè)一次性的憑證;它僅僅用于peon和僅僅用于service S,并且只能使用一次,使用之后馬上會(huì)過(guò)期,即ticket通過(guò)驗(yàn)證,CAS立即刪除該ticket,使它以后不能再使用。這樣可以保證其安全性。

          ? ??? ??關(guān)于ST,在取一個(gè)ST時(shí),即使用deleteTicket(ticketId)同時(shí)將一次性的ST刪除;而對(duì)于TGT或PT,則通過(guò)resetTimer(ticketId)以更新TGT或PT的時(shí)間。在CAS服務(wù)端返回的ST中只能得出用戶(hù)名。

          三、實(shí)現(xiàn)單點(diǎn)登錄系統(tǒng)

          1、SSO業(yè)務(wù)流程

          2、SSO系統(tǒng)創(chuàng)建

          ? ? ? ? 首先我們來(lái)創(chuàng)建SSO的Maven工程,添加依賴(lài)如下:


          ????
          ????
          ????????org.springframework
          ????????spring-context
          ????

          ????
          ????????org.springframework
          ????????spring-beans
          ????

          ????
          ????????org.springframework
          ????????spring-webmvc
          ????

          ????
          ????????org.springframework
          ????????spring-jdbc
          ????

          ????
          ????????org.springframework
          ????????spring-aspects
          ????

          ????
          ????????org.springframework
          ????????spring-context-support
          ????

          ????
          ????????javax.servlet
          ????????servlet-api
          ????????provided
          ????

          ????
          ????????javax.servlet
          ????????jsp-api
          ????????provided
          ????

          ????
          ????
          ????????redis.clients
          ????????jedis
          ????




          ????
          ????????
          ????????????org.apache.tomcat.maven
          ????????????tomcat7-maven-plugin
          ????????????
          ????????????????8084
          ????????????????/
          ????????????

          ????????

          ????

          ? ? ? ? 我們這里的SSO系統(tǒng)主要提供兩個(gè)功能模塊,一個(gè)是用戶(hù)的注冊(cè)功能,另一個(gè)就是用戶(hù)登錄相關(guān)的功能。下面我們就來(lái)分別實(shí)現(xiàn)這兩個(gè)模塊。因?yàn)槲覀冞@里采用了mybatis框架,并且持久化層都是簡(jiǎn)單的增刪改查操作,所以這里就直接使用Mybatis的逆向工程來(lái)生成Dao的代碼了。

          1.用戶(hù)注冊(cè)

          1)數(shù)據(jù)校驗(yàn)接口

          ? ??? ? Controller只是發(fā)布服務(wù)。接收三個(gè)參數(shù),一個(gè)是要校驗(yàn)的數(shù)據(jù),一個(gè)數(shù)據(jù)類(lèi)型,一個(gè)是callback。調(diào)用Service校驗(yàn)。返回json數(shù)據(jù)。需要支持jsonp,需要判斷callback。

          @RequestMapping("/check/{param}/{type}")
          @ResponseBody
          public?Object?checkData(
          ????????@PathVariable?String?param,
          ????????@PathVariable?Integer?type,
          ????????String?callback){
          ????try?{
          ????????ResultObject?result?=?registerService.checkData(param,?type);
          ????????if(StringUtils.isNotBlank(callback)){
          ????????????//請(qǐng)求為jsonp,需要支持
          ????????????MappingJacksonValue?mappingJacksonValue?=?new?MappingJacksonValue(result);
          ????????????mappingJacksonValue.setJsonpFunction(callback);
          ????????????return?mappingJacksonValue;
          ????????}
          ????????return?result;
          ????}?catch?(Exception?e)?{
          ????????e.printStackTrace();
          ????????return?ResultObject.build(500,?"數(shù)據(jù)校驗(yàn)失敗");
          ????}
          }

          ? ??? ? Service接收兩個(gè)參數(shù),一個(gè)是要校驗(yàn)的數(shù)據(jù),一個(gè)是數(shù)據(jù)類(lèi)型。根據(jù)不同的數(shù)據(jù)類(lèi)型生成不同的查詢(xún)條件,到user表中進(jìn)行查詢(xún)?nèi)绻樵?xún)到結(jié)果返回false,查詢(xún)結(jié)果為空返回true。

          public?ResultObject?checkData(String?param,?int?type)?{
          ????//根據(jù)數(shù)據(jù)類(lèi)型檢測(cè)數(shù)據(jù)
          ????TbUserExample?example?=?new?TbUserExample();
          ????Criteria?criteria?=?example.createCriteria();
          ????//1、2、3分別代表username,phone,email-->都不可重復(fù)
          ????if(1==type){
          ????????criteria.andUsernameEqualTo(param);
          ????}else?if?(2==type)?{
          ????????criteria.andPhoneEqualTo(param);
          ????}else?if?(3==type)?{
          ????????criteria.andEmailEqualTo(param);
          ????}
          ????//執(zhí)行查詢(xún)
          ????List?list?=?userMapper.selectByExample(example);
          ????//判斷查詢(xún)結(jié)果是否為空
          ????if(list==null||list.isEmpty()){
          ????????return?ResultObject.ok(true);
          ????}
          ????return?ResultObject.ok(false);
          }

          2)用戶(hù)注冊(cè)接口

          ? ? ? ? Controller接收一個(gè)表單,請(qǐng)求的方法為post。使用TbUser接收表單的內(nèi)容。調(diào)用Service插入數(shù)據(jù),返回。

          @RequestMapping(value="/register",method=RequestMethod.POST)
          @ResponseBody
          public?ResultObject?register(TbUser?user){
          ????try?{
          ????????ResultObject?result?=?registerService.register(user);
          ????????System.out.println(result.getStatus()+"===="+result.getMsg());
          ????????return?result;
          ????}?catch?(Exception?e)?{
          ????????e.printStackTrace();
          ????????return?ResultObject.build(500,?"用戶(hù)注冊(cè)失敗");
          ????}
          }

          ? ??? ? Service接收TbUser參數(shù),對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),校驗(yàn)成功,插入數(shù)據(jù),返回結(jié)果。

          public?ResultObject?register(TbUser?user)?{
          ????//校驗(yàn)數(shù)據(jù)
          ????//校驗(yàn)用戶(hù)名密碼不能為空
          ????if(StringUtils.isBlank(user.getUsername())||StringUtils.isBlank(user.getPassword())){
          ????????return?ResultObject.build(400,?"用戶(hù)名或密碼不能為空");
          ????}
          ????//校驗(yàn)數(shù)據(jù)是否重復(fù)
          ????//校驗(yàn)用戶(hù)名
          ????ResultObject?result?=?checkData(user.getUsername(),?1);
          ????if(!(boolean)?result.getData()){
          ????????return?ResultObject.build(400,?"用戶(hù)名重復(fù)");
          ????}
          ????//校驗(yàn)手機(jī)號(hào)
          ????if(user.getPhone()!=null){
          ????????result=checkData(user.getPhone(),?2);
          ????????if(!(boolean)?result.getData()){
          ????????????return?ResultObject.build(400,?"手機(jī)號(hào)重復(fù)");
          ????????}
          ????}
          ????//校驗(yàn)郵箱
          ????if(user.getEmail()!=null){
          ????????result=checkData(user.getEmail(),?3);
          ????????if(!(boolean)?result.getData()){
          ????????????return?ResultObject.build(400,?"郵箱重復(fù)");
          ????????}
          ????}
          ????//插入數(shù)據(jù)
          ????user.setCreated(new?Date());
          ????user.setUpdated(new?Date());
          ????//密碼MD5加密
          ????user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));
          ????userMapper.insert(user);
          ????return?ResultObject.ok();
          }

          2.用戶(hù)登錄

          1)用戶(hù)登錄接口

          ? ? ? ? Controller接收兩個(gè)參數(shù),一個(gè)是用戶(hù)名,一個(gè)是密碼,請(qǐng)求的方法為post。調(diào)用Service方法返回登錄處理結(jié)果,響應(yīng)json數(shù)據(jù)。

          @RequestMapping(value="/user/login",method=RequestMethod.POST)
          @ResponseBody
          public?ResultObject?login(String?username,String?password,HttpServletRequest?request,HttpServletResponse?response){
          ????try?{
          ????????ResultObject?result?=?loginService.login(username,?password,?request,?response);
          ????????return?result;
          ????}?catch?(Exception?e)?{
          ????????e.printStackTrace();
          ????????return?ResultObject.build(500,?"登錄失敗");
          ????}
          }

          ? ??? ? Service接收用戶(hù)名、密碼。校驗(yàn)密碼是否正確,生成token,向redis中寫(xiě)入用戶(hù)信息,把token寫(xiě)入cookie,并在返回結(jié)果中包含token。

          public?ResultObject?login(String?username,?String?password,
          ????????HttpServletRequest?request,?HttpServletResponse?response)?{
          ????//校驗(yàn)用戶(hù)名密碼是否正確
          ????TbUserExample?example?=?new?TbUserExample();
          ????Criteria?criteria?=?example.createCriteria();
          ????criteria.andUsernameEqualTo(username);
          ????List?list=userMapper.selectByExample(example);
          ????//取用戶(hù)信息
          ????if(list==null||list.isEmpty()){
          ????????return?ResultObject.build(400,?"用戶(hù)名或密碼錯(cuò)誤");
          ????}
          ????TbUser?user=list.get(0);
          ????//校驗(yàn)密碼
          ????if(!user.getPassword().equals(DigestUtils.md5DigestAsHex(password.getBytes()))){
          ????????return?ResultObject.build(400,?"用戶(hù)名或密碼錯(cuò)誤");
          ????}
          ????//登錄成功,生成token
          ????String?token?=?UUID.randomUUID().toString();
          ????//把用戶(hù)信息寫(xiě)入redis
          ????//key:REDIS_SESSION:{TOKEN}
          ????//value:user轉(zhuǎn)成json
          ????user.setPassword(null);
          ????jedisClient.set(REDIS_SESSION_KEY+":"+token,?JsonUtils.objectToJson(user));
          ????//設(shè)置session過(guò)期時(shí)間
          ????jedisClient.expire(REDIS_SESSION_KEY+":"+token,?SESSION_EXPIRE);
          ????//寫(xiě)cookie
          ????CookieUtils.setCookie(request,?response,?"PSP_TOKEN",?token);
          ????return?ResultObject.ok(token);
          }

          2)通過(guò)token查詢(xún)用戶(hù)信息

          ? ? ? ? Controller從url中取token的內(nèi)容,調(diào)用Service取用戶(hù)信息,響應(yīng)json數(shù)據(jù)。

          @RequestMapping("/user/token/{token}")
          @ResponseBody
          public?Object?getUserByToken(@PathVariable?String?token,String?callback){
          ????try?{
          ????????ResultObject?result?=?loginService.getUserByToken(token);
          ????????if(StringUtils.isNotBlank(callback)){
          ????????????System.out.println("callback!!");
          ????????????MappingJacksonValue?mappingJacksonValue=new?MappingJacksonValue(result);
          ????????????System.out.println(mappingJacksonValue.toString());
          ????????????return?mappingJacksonValue;
          ????????}
          ????????return?result;
          ????}?catch?(Exception?e)?{
          ????????e.printStackTrace();
          ????????return?ResultObject.build(500,?"獲取用戶(hù)信息失敗");
          ????}
          }

          ? ??? ? Service接收token,根據(jù)token查詢(xún)r(jià)edis,查詢(xún)到結(jié)果返回用戶(hù)對(duì)象,更新過(guò)期時(shí)間。如果查詢(xún)不到結(jié)果,返回Session已經(jīng)過(guò)期,狀態(tài)碼400。

          public?ResultObject?getUserByToken(String?token)?{
          ????//根據(jù)token取用戶(hù)信息
          ????String?json?=?jedisClient.get(REDIS_SESSION_KEY+":"+token);
          ????//判斷是否查詢(xún)到結(jié)果
          ????if(StringUtils.isBlank(json)){
          ????????return?ResultObject.build(400,?"用戶(hù)session已過(guò)期");
          ????}
          ????//把json轉(zhuǎn)換成java對(duì)象
          ????TbUser?user?=?JsonUtils.jsonToPojo(json,?TbUser.class);
          ????//更新session過(guò)期時(shí)間
          ????jedisClient.expire(REDIS_SESSION_KEY+":"+token,?SESSION_EXPIRE);
          ????return?ResultObject.ok(user);
          }

          2.展示注冊(cè)和登錄頁(yè)面

          ? ? ? ? 在SSO系統(tǒng)中只有登錄注冊(cè)功能,所以只需要兩個(gè)頁(yè)面就可以了,下面是其跳轉(zhuǎn)Controller

          @Controller
          public?class?PageController?{
          ????/**
          ?????*?展示登錄頁(yè)面
          ?????*/
          ????@RequestMapping("/page/login")
          ????public?String?showLogin(String?redirectURL,Model?model){
          ????????//需要把參數(shù)傳遞到j(luò)sp,頁(yè)面回調(diào)
          ????????model.addAttribute("redirect",?redirectURL);
          ????????return?"login";
          ????}
          ?
          ????/**
          ?????*?展示注冊(cè)頁(yè)面
          ?????*/
          ????@RequestMapping("/page/register")
          ????public?String?showRegister(){
          ????????return?"register";
          ????}
          }

          ? ? ? ? 我們這里為了解決登錄回調(diào),在登錄頁(yè)面的js實(shí)現(xiàn)如下:

          var?redirectUrl?=?"${redirect}";
          var?LOGIN?=?{
          ????????checkInput:function()?{
          ????????????if?($("#loginname").val()?==?"")?{
          ????????????????alert("用戶(hù)名不能為空");
          ????????????????$("#loginname").focus();
          ????????????????return?false;
          ????????????}
          ????????????if?($("#nloginpwd").val()?==?"")?{
          ????????????????alert("密碼不能為空");
          ????????????????$("#nloginpwd").focus();
          ????????????????return?false;
          ????????????}
          ????????????return?true;
          ????????},
          ????????doLogin:function()?{
          ????????????$.post("/user/login",?$("#formlogin").serialize(),function(data){
          ????????????????if?(data.status?==?200)?{
          ????????????????????alert("登錄成功!");
          ????????????????????if?(redirectUrl?==?"")?{
          ????????????????????????location.href?=?"http://www.psp.com";
          ????????????????????}?else?{
          ????????????????????????location.href?=?redirectUrl;
          ????????????????????}
          ????????????????}?else?{
          ????????????????????alert("登錄失敗,原因是:"?+?data.msg);
          ????????????????????$("#loginname").select();
          ????????????????}
          ????????????});
          ????????},
          ????????login:function()?{
          ????????????if?(this.checkInput())?{
          ????????????????this.doLogin();
          ????????????}
          ????????}???
          };
          $(function(){
          ????$("#loginsubmit").click(function(){
          ????????LOGIN.login();
          ????});
          });

          3、其他系統(tǒng)整合SSO

          ? ? ? ? 現(xiàn)在我們就來(lái)演示其他系統(tǒng)對(duì)SSO進(jìn)行整合,這里就以門(mén)戶(hù)系統(tǒng)整合SSO為例。

          1.門(mén)戶(hù)登錄

          ? ??? ??當(dāng)用戶(hù)在首頁(yè)點(diǎn)擊登錄或者注冊(cè)的時(shí)候需要跳轉(zhuǎn)到sso系統(tǒng)。進(jìn)行相應(yīng)的操作。登錄成功跳轉(zhuǎn)到首頁(yè)。首頁(yè)應(yīng)該顯示當(dāng)前登錄的用戶(hù)。首先門(mén)戶(hù)系統(tǒng)的登錄按鈕代碼如下,

          "http://sso.psp.com/user/page/login">登錄

          只是一個(gè)簡(jiǎn)單的超鏈接,跳轉(zhuǎn)到SSO登錄頁(yè)面,并進(jìn)行相關(guān)的登錄操作,當(dāng)?shù)卿浲瓿珊螅谑醉?yè)展示用戶(hù)。其前端實(shí)現(xiàn)如下,

          checkLogin?:?function(){
          ????var?_ticket?=?$.cookie("PSP_TOKEN");
          ????if(!_ticket){
          ????????return?;
          ????}
          ????$.ajax({
          ????????url?:?"http://sso.psp.com/user/token/"?+?_ticket,
          ????????dataType?:?"json",
          ????????type?:?"GET",
          ????????success?:?function(data){
          ????????????if(data.status?==?200){
          ????????????????var?username?=?data.data.username;
          ????????????????var?html?=?username?+?",您好!";
          ????????????????$("#loginbar").html(html);
          ????????????}
          ????????}
          ????});
          }

          2.登錄攔截器

          ? ??? ? 在Poratl系統(tǒng)中,對(duì)于有些頁(yè)面是需要登錄之后才能訪問(wèn)的,比如訂單頁(yè)面,當(dāng)用戶(hù)查看訂單頁(yè)面時(shí)此時(shí)必須要求用戶(hù)登錄,可以使用攔截器來(lái)實(shí)現(xiàn)。攔截器的處理流程為:

          • 攔截請(qǐng)求url

          • 從cookie中取token

          • 如果沒(méi)有toke跳轉(zhuǎn)到登錄頁(yè)面。

          • 取到token,需要調(diào)用sso系統(tǒng)的服務(wù)查詢(xún)用戶(hù)信息。

          • 如果用戶(hù)session已經(jīng)過(guò)期,跳轉(zhuǎn)到登錄頁(yè)面

          • 如果沒(méi)有過(guò)期,放行。

          ?其中攔截器配置如下,攔截order下的所有操作

          ?
          ????
          ????????"/order/**"/>
          ????????"com.psp.portal.interceptor.LoginInterceptor"/>
          ????

          ?

          然后在springmvc中需要實(shí)現(xiàn)HandlerInterceptor接口。

          public?class?LoginInterceptor?implements?HandlerInterceptor?{

          ????@Autowired
          ????private?UserService?userService;
          ????@Value("${SSO_LOGIN_URL}")
          ????private?String?SSO_LOGIN_URL;
          ?
          ????@Override
          ????public?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,
          ????????????Object?object)?throws?Exception?{
          ????????//?1、攔截請(qǐng)求url
          ????????//?2、從cookie中取token
          ????????//?3、如果沒(méi)有toke跳轉(zhuǎn)到登錄頁(yè)面。
          ????????//?4、取到token,需要調(diào)用sso系統(tǒng)的服務(wù)查詢(xún)用戶(hù)信息。
          ????????TbUser?user?=?userService.getUserByToken(request,?response);
          ????????//?5、如果用戶(hù)session已經(jīng)過(guò)期,跳轉(zhuǎn)到登錄頁(yè)面
          ????????if?(user?==?null)?{
          ????????????response.sendRedirect(SSO_LOGIN_URL+"?redirectURL="+request.getRequestURI());
          ????????????return?false;
          ????????}
          ????????//把用戶(hù)對(duì)象放入request中
          ????????request.setAttribute("user",?user);
          ????????//?6、如果沒(méi)有過(guò)期,放行。
          ????????return?true;
          ????}
          ????@Override
          ????public?void?afterCompletion(HttpServletRequest?arg0,
          ????????????HttpServletResponse?response,?Object?object,?Exception?exception)
          ????????????throws?Exception?{
          ????}
          ????@Override
          ????public?void?postHandle(HttpServletRequest?request,?HttpServletResponse?response,
          ????????????Object?object,?ModelAndView?modelAndView)?throws?Exception?{
          ????}
          }

          其對(duì)應(yīng)的Service作用為,根據(jù)token取用戶(hù)信息,如果取到返回TbUser對(duì)象,如果取不到,返回null。

          @Service
          public?class?UserServiceImpl?implements?UserService?{

          ????@Value("${SSO_BASE_URL}")
          ????private?String?SSO_BASE_URL;
          ????@Value("${SSO_USER_TOKEN_SERVICE}")
          ????private?String?SSO_USER_TOKEN_SERVICE;
          ?
          ?
          ????@Override
          ????public?TbUser?getUserByToken(HttpServletRequest?request,
          ????????????HttpServletResponse?response)?{
          ????????try?{
          ????????????//從cookie中獲取token
          ????????????String?token?=?CookieUtils.getCookieValue(request,?"PSP_TOKEN");
          ????????????//判斷token是否有值
          ????????????if(StringUtils.isBlank(token)){
          ????????????????return?null;
          ????????????}
          ????????????//調(diào)用sso的查詢(xún)服務(wù)
          ????????????String?json?=?HttpClientUtil.doGet(SSO_BASE_URL+SSO_USER_TOKEN_SERVICE+token);
          ????????????//把json轉(zhuǎn)換成java對(duì)象
          ????????????ResultObject?result?=?ResultObject.format(json);
          ????????????if(result.getStatus()!=200){
          ????????????????return?null;
          ????????????}
          ????????????//取用戶(hù)對(duì)象
          ????????????result?=?ResultObject.formatToPojo(json,?TbUser.class);
          ????????????TbUser?user=(TbUser)?result.getData();
          ????????????return?user;
          ????????}?catch?(Exception?e)?{
          ????????????return?null;
          ????????}
          ????}
          }






          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循?CC 4.0 BY-SA?版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。

          本文鏈接:

          https://blog.csdn.net/qq_22172133/article/details/82291112






          粉絲福利:108本java從入門(mén)到大神精選電子書(shū)領(lǐng)取

          ???

          ?長(zhǎng)按上方鋒哥微信二維碼?2 秒
          備注「1234」即可獲取資料



          感謝點(diǎn)贊支持下哈?

          瀏覽 36
          點(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>
                  国产精品小电影 | 久久精品视频免费观看 | 人人撸人人模 | 国产无码中文字幕在线 | 狼友新网址 |