分析為什么采用集群策略 集群Session共享問(wèn)題 實(shí)現(xiàn)SSO

原生HttpSession解決集群Session共享問(wèn)題 實(shí)現(xiàn)SSO單點(diǎn)登錄

在介紹本節(jié)內(nèi)容之前灵妨,在這里談?wù)勎医佑|到的一些后端架構(gòu)出現(xiàn)的問(wèn)題

就在前兩天輔導(dǎo)員早上9點(diǎn)突然發(fā)布一條選課通知,到中午12點(diǎn)之前完成大三下學(xué)期的選課落竹,好的泌霍,我打開(kāi)了鏈接想著4個(gè)小時(shí)的選課時(shí)間怎么選不上?然而還真沒(méi)選上

問(wèn)題出現(xiàn)

  • 請(qǐng)求超時(shí)

    仔細(xì)看了一下之后大概得出了結(jié)論述召,這個(gè)web選課應(yīng)用后端使用php編寫(xiě)烹吵,部署到了Apache服務(wù)器上碉熄,查閱了一下php部署在Apache的集群方式更多人叫它拓展用用服務(wù)器組,個(gè)人感覺(jué)沒(méi)有配置應(yīng)用服務(wù)器組肋拔,不然全院四個(gè)年級(jí)加起來(lái)也不夠5000的流量怎么會(huì)做不到

    我查閱了一下锈津,因?yàn)樽约簺](méi)有使用過(guò)Apache服務(wù)器,大概談一下我對(duì)這個(gè)問(wèn)題的認(rèn)識(shí)凉蜂,Apache服務(wù)器有自己的幾種工作模式琼梆,并且給我感覺(jué)有一套自己的進(jìn)程管理體系,類(lèi)似于線(xiàn)程池窿吩,為了減少建立進(jìn)程去處理請(qǐng)求的額外開(kāi)銷(xiāo)茎杂,啟動(dòng)Apache服務(wù)器的時(shí)候,就會(huì)建立默認(rèn)配置的空閑進(jìn)程等待請(qǐng)求的到來(lái)去處理纫雁,(Apache是以進(jìn)程為基礎(chǔ)的結(jié)構(gòu)煌往,進(jìn)程要比線(xiàn)程消耗更多的系統(tǒng)開(kāi)支,不太適合于多處理器環(huán)境轧邪,因此刽脖,在一個(gè)Apache Web站點(diǎn)擴(kuò)容時(shí),通常是增加服務(wù)器或擴(kuò)充群集節(jié)點(diǎn)而不是增加處理器)忌愚,而在啟動(dòng)Tomcat的時(shí)候能夠發(fā)現(xiàn)進(jìn)程其實(shí)只有Tomcat進(jìn)程曲管,但是它其中的線(xiàn)程卻存在許多。這是兩者不太一樣的地方

  • Apache服務(wù)器與Tomcat服務(wù)器的區(qū)別

    • Apache多被稱(chēng)為web服務(wù)器硕糊,并且對(duì)Linux支持的相當(dāng)完美院水,Apache是以進(jìn)程為基礎(chǔ)的結(jié)構(gòu),進(jìn)程要比線(xiàn)程消耗更多的系統(tǒng)開(kāi)支简十,不太適合于多處理器環(huán)境檬某,因此,在一個(gè)Apache Web站點(diǎn)擴(kuò)容時(shí)螟蝙,通常是增加服務(wù)器或擴(kuò)充群集節(jié)點(diǎn)而不是增加處理器
    • Apache給我的感覺(jué)和Nginx效果和功能一樣橙喘,都是web服務(wù)器,但是Apache支持php拓展模塊胶逢,也就使得php的后端應(yīng)用程序能夠使用它作為載體厅瞎,也就和Tomcat這種基于J2EE規(guī)范的應(yīng)用服務(wù)器可以和Nginx配置集群使Nginx作為負(fù)載均衡服務(wù)器來(lái)使用
  • 舉個(gè)帖子中的例子

    • Apache是一輛卡車(chē),上面可以裝一些東西如html等初坠。但是不能裝水和簸,要裝水必須要有容器(桶),Tomcat就是一個(gè)桶(裝像Java這樣的水)碟刺,而這個(gè)桶也可以不放在卡車(chē)上锁保。
    • Apache只支持靜態(tài)網(wǎng)頁(yè),但像jsp等動(dòng)態(tài)網(wǎng)頁(yè)就需要Tomcat這種應(yīng)用服務(wù)器來(lái)處理。
    • Apache和Tomcat整合使用:如果客戶(hù)端請(qǐng)求的是靜態(tài)頁(yè)面爽柒,則只需要Apache服務(wù)器響應(yīng)請(qǐng)求吴菠;如果客戶(hù)端請(qǐng)求動(dòng)態(tài)頁(yè)面,則是Tomcat這種應(yīng)用服務(wù)器服務(wù)器響應(yīng)請(qǐng)求浩村;
    • 因?yàn)閖sp是服務(wù)器端解釋代碼的做葵,這樣整合就可以減少Tomcat的服務(wù)開(kāi)銷(xiāo) 。

終于請(qǐng)求到了登錄頁(yè)卻執(zhí)行不了登錄操作

  • 驗(yàn)證碼錯(cuò)誤

    經(jīng)過(guò)無(wú)數(shù)次的刷新嘗試之后總算有一條剛剛忙碌完的進(jìn)程顧及到了我心墅,這個(gè)時(shí)候酿矢,我開(kāi)始執(zhí)行了登錄操作,卻提示我驗(yàn)證碼失敗怎燥,我校驗(yàn)了很多次卻不能夠成功登錄瘫筐,這個(gè)時(shí)候我又分析了一下,因?yàn)樽约阂矊?shí)現(xiàn)過(guò)驗(yàn)證碼登錄的邏輯铐姚,所以說(shuō)這個(gè)流程還是掌握的比較清楚的

    請(qǐng)求登錄頁(yè)的時(shí)候策肝,請(qǐng)求后端獲取驗(yàn)證碼的接口,這個(gè)時(shí)候后端如果不使用Redis緩存的技術(shù)去解決驗(yàn)證碼的校驗(yàn)隐绵,最簡(jiǎn)單的方式就是放置在session中之众,key可為一個(gè)常亮,我們就叫LOGIN_CODE_SESSION_KEY那么值的話(huà)很好理解就是驗(yàn)證碼的值了氢橙,再次請(qǐng)求登錄接口的時(shí)候,可以實(shí)現(xiàn)一個(gè)過(guò)濾器去過(guò)濾登錄借口恬偷,校驗(yàn)請(qǐng)求中的驗(yàn)證碼是否與session中的驗(yàn)證碼值匹配

    那么為什么會(huì)提示驗(yàn)證碼錯(cuò)誤導(dǎo)致驗(yàn)證碼錯(cuò)誤進(jìn)一步致使登錄失敗呢

    • 可以想想這樣一種情況悍手,Apache服務(wù)器的進(jìn)程數(shù)已經(jīng)到達(dá)了接近極限的地步,這種情況下?lián)Q做是什么服務(wù)器我想效率的話(huà)肯定低得不能再低甚至可能發(fā)生宕機(jī)問(wèn)題袍患,我在登錄的時(shí)候有點(diǎn)擊過(guò)驗(yàn)證碼的動(dòng)作坦康,但是卻得不到任何響應(yīng),可以再這樣想一下诡延,因?yàn)楹蠖朔?wù)器的負(fù)擔(dān)太重滞欠,生成驗(yàn)證碼的邏輯已經(jīng)執(zhí)行,但是在頁(yè)面上因?yàn)樾侍亮迹憫?yīng)沒(méi)有及時(shí)到達(dá)筛璧,web頁(yè)面沒(méi)有刷新最新的驗(yàn)證碼,導(dǎo)致我們驗(yàn)證時(shí)攜帶過(guò)期驗(yàn)證碼進(jìn)行登錄惹恃,提示登錄失敗驗(yàn)證碼錯(cuò)誤
  • 登錄壓根沒(méi)響應(yīng)

    下面會(huì)介紹怎么成了一個(gè)沒(méi)有響應(yīng)的web應(yīng)用

服務(wù)器未響應(yīng)

  • 服務(wù)器宕機(jī)

    服務(wù)器沒(méi)有響應(yīng)這個(gè)東西我曾經(jīng)折騰實(shí)驗(yàn)室服務(wù)器的時(shí)候就出現(xiàn)過(guò)這種尷尬的情況夭谤,那會(huì)兒造成的錯(cuò)誤還不是一臺(tái)軟件級(jí)別的服務(wù)器宕機(jī),而是整個(gè)一臺(tái)物理級(jí)別的服務(wù)器宕機(jī)...難怪怎么用ssh想要上去都沒(méi)用巫糙,很快很多線(xiàn)上應(yīng)用就開(kāi)始找我了朗儒,然而我還很懵逼,和畢業(yè)的學(xué)長(zhǎng)分析了一下,沒(méi)錯(cuò)是關(guān)機(jī)了..

  • 服務(wù)器為什么會(huì)宕機(jī)

    簡(jiǎn)單說(shuō)一下服務(wù)器這個(gè)概念醉锄,在物理級(jí)別的服務(wù)器這個(gè)概念乏悄,簡(jiǎn)單一點(diǎn)來(lái)說(shuō),它是一臺(tái)機(jī)器恳不,機(jī)房里面很多個(gè)大機(jī)箱基本就是這個(gè)了檩小,軟件級(jí)別的服務(wù)器是什么,類(lèi)似Nginx Apache Tomcat 這類(lèi)的web服務(wù)器和應(yīng)用服務(wù)器

    至于web服務(wù)器和應(yīng)用服務(wù)器我就不在這里贅述妆够,下面來(lái)分析一下服務(wù)器為什么會(huì)宕機(jī)

    先說(shuō)說(shuō)我搞崩的實(shí)驗(yàn)室云服務(wù)器识啦,上面部署了很多應(yīng)用服務(wù)器node的tomcat好像還有php的之類(lèi)應(yīng)用服務(wù)器,上面的應(yīng)用也就更不用說(shuō)神妹,實(shí)驗(yàn)室官網(wǎng)可以去參觀一下 www.xiyoumobile.com 學(xué)長(zhǎng)學(xué)姐們的心血颓哮,真的很贊,尤其是在我搞崩之后覺(jué)得有點(diǎn)對(duì)不起他們鸵荠,但是學(xué)長(zhǎng)還是給我鼓勵(lì)冕茅,說(shuō)正題,我造成的線(xiàn)上事故是因?yàn)槭罴賹?xiě)的SpringBoot項(xiàng)目需要部署蛹找,并且因?yàn)橐恍┙涌谥荒芡ㄟ^(guò)學(xué)校的內(nèi)網(wǎng)才能夠訪問(wèn)爬到數(shù)據(jù)姨伤,這個(gè)時(shí)候果斷想到了折騰一下實(shí)驗(yàn)室服務(wù)器,但是沒(méi)有經(jīng)驗(yàn)的我按照原始方式簡(jiǎn)單的打了.war包移除內(nèi)置Tomcat之后放在上面庸疾,當(dāng)時(shí)還沒(méi)事乍楚,直到第二天早上我知道的時(shí)候應(yīng)該已經(jīng)關(guān)機(jī)了幾個(gè)小時(shí)了

    原因

    SpringBoot在我看來(lái)是Spring官方為了簡(jiǎn)化基于Spring框架組件的一套為了簡(jiǎn)化自身開(kāi)發(fā)的框架,說(shuō)句實(shí)話(huà)用起來(lái)很方便届慈,但是也正是因?yàn)樗姆奖阃较渲泻芏嘁蕾?lài)關(guān)系以及Bean的依賴(lài),組裝變得規(guī)模很龐大金顿,使用一些提供的支持的時(shí)候也只是去操作高度封裝的Api接口臊泌,看過(guò)一些源碼,確實(shí)覺(jué)得寫(xiě)的很好揍拆,這個(gè)時(shí)候會(huì)造成什么問(wèn)題呢渠概,Jvm方法區(qū)正是因?yàn)橛辛诉@么多的Bean以及一些動(dòng)態(tài)代理類(lèi)的信息,硬生生地讓整個(gè)SpringBoot后端服務(wù)占到了可能高于2G的內(nèi)存嫂拴,實(shí)驗(yàn)室服務(wù)器因?yàn)樯暾?qǐng)的早播揪,后來(lái)才知道是動(dòng)態(tài)4G內(nèi)存,再加上之前上面那么多東西筒狠,后來(lái)想想自己真的是有點(diǎn)弱智...也是因?yàn)楫?dāng)時(shí)對(duì)Jvm沒(méi)有什么了解剪芍,以至于沒(méi)有意識(shí)到Jvm的簡(jiǎn)單調(diào)優(yōu),導(dǎo)致實(shí)驗(yàn)室服務(wù)器內(nèi)存耗盡宕機(jī)最終關(guān)機(jī)

    服務(wù)器宕機(jī)的原因

    就像我上文一樣窟蓝,物理級(jí)別的服務(wù)器宕機(jī)的原因罪裹,要么是創(chuàng)建的進(jìn)程過(guò)多组贺,占用內(nèi)存過(guò)多文黎,導(dǎo)致操作系統(tǒng)調(diào)度變慢廷痘,以至于到最后不能合理地去管理進(jìn)程回收一些空閑進(jìn)程尘应,導(dǎo)致內(nèi)存一直持續(xù)過(guò)高占用,這個(gè)時(shí)候如果有新的進(jìn)程需要執(zhí)行任務(wù)峡继,可能就會(huì)出現(xiàn)死機(jī)的情況冯袍,進(jìn)而就關(guān)機(jī)了,像這種情況碾牌,可以分析Jvm的GC情況康愤,可能是自己的編碼導(dǎo)致一直存在某些引用持有一些本該被GC的引用,導(dǎo)致GC的時(shí)候并沒(méi)有將其回收導(dǎo)致的問(wèn)題舶吗,可能最后還會(huì)出現(xiàn)OOM的問(wèn)題征冷,分析起來(lái)還是挺麻煩的,因?yàn)槲覍?duì)這里還不是特別清楚誓琼,所以也就先不說(shuō)了检激,最終這個(gè)選課系統(tǒng)的后臺(tái)服務(wù)器還是被重啟了,這個(gè)時(shí)候再次嘗試的時(shí)候...一個(gè)字爽腹侣,暢快的感覺(jué)叔收,總的來(lái)說(shuō)我覺(jué)得一個(gè)后端項(xiàng)目如果不能保證并發(fā)量的出現(xiàn)能夠正常運(yùn)行,給我感覺(jué)是個(gè)失敗的項(xiàng)目

    應(yīng)用服務(wù)器宕機(jī)

    應(yīng)用服務(wù)器為什么會(huì)宕機(jī)傲隶?例如Tomcat來(lái)說(shuō)饺律,其中的Connector組件維護(hù)一個(gè)線(xiàn)程池,一條新的請(qǐng)求到達(dá)服務(wù)器的時(shí)候跺株,簡(jiǎn)單地來(lái)說(shuō)就是一條線(xiàn)程去處理一條請(qǐng)求复濒,這個(gè)在SpringBoot項(xiàng)目或者SSM項(xiàng)目中基于J2EE規(guī)范的后端服務(wù)中l(wèi)og打的全的話(huà)可以觀察到,處理請(qǐng)求和響應(yīng)其實(shí)是同一個(gè)線(xiàn)程帖鸦,如果服務(wù)器采用同步方式去處理請(qǐng)求芝薇,這個(gè)時(shí)候大家都知道I/O的效率是很低的胚嘲,如果說(shuō)一條請(qǐng)求需要處理一條很費(fèi)時(shí)的I/O操作作儿,也就是說(shuō)這次請(qǐng)求需要占用這個(gè)這個(gè)線(xiàn)程直到它執(zhí)行完I/O操作,使用過(guò)Tomcat應(yīng)用服務(wù)器的人應(yīng)該都知道馋劈,線(xiàn)程池也是有默認(rèn)最高上限的攻锰,調(diào)得過(guò)高可能會(huì)影響線(xiàn)程池的工作,低了可能并發(fā)量比較低妓雾,我一直用的默認(rèn)的沒(méi)有去管過(guò)

    <Connector port="8080"    
                   maxThreads="150" minSpareThreads="25" maxSpareThreads="75"    
                   enableLookups="false" redirectPort="8443" acceptCount="100"    
                   debug="0" connectionTimeout="20000"     
                   disableUploadTimeout="true" />    
    

    這個(gè)是Tomcat conf下server.conf文件的配置娶吞,需要了解的可以去試試,這個(gè)時(shí)候同步策略處理請(qǐng)求械姻,一旦占用時(shí)間過(guò)長(zhǎng)妒蛇,例如部署了一個(gè)并發(fā)量較高的服務(wù),請(qǐng)求峰值一旦來(lái)臨,線(xiàn)程池將會(huì)被耗盡绣夺,并且可能造成整個(gè)應(yīng)用服務(wù)器的宕機(jī)吏奸,當(dāng)然處理這種邏輯,我們可以在代碼中使用異步處理請(qǐng)求來(lái)實(shí)現(xiàn)

    同步服務(wù)為每個(gè)請(qǐng)求創(chuàng)建單一線(xiàn)程陶耍,由此線(xiàn)程完成整個(gè)請(qǐng)求的處理:接收消息奋蔚,處理消息,返回?cái)?shù)據(jù)烈钞;這種情況下服務(wù)器資源對(duì)所有請(qǐng)求開(kāi)放泊碑,服務(wù)器資源被所有入棧請(qǐng)求競(jìng)爭(zhēng)使用,如果請(qǐng)求過(guò)多就會(huì)導(dǎo)致服務(wù)器資源耗盡宕機(jī)毯欣,或者導(dǎo)致競(jìng)爭(zhēng)加劇馒过,資源調(diào)度頻繁,服務(wù)器資源利用效率降低仪媒。

    來(lái)降低web服務(wù)器的負(fù)擔(dān)沉桌,并且還能夠響應(yīng)并發(fā)量較大的情況,綜上所述算吩,為了能夠配置一個(gè)高并發(fā)量的后端架構(gòu)留凭,最好是項(xiàng)目后端架構(gòu)轉(zhuǎn)向集群

  • 要是讓我做這個(gè)選課系統(tǒng)我會(huì)如何架構(gòu)

    首先考慮到并發(fā)量,因?yàn)槠鋵?shí)實(shí)現(xiàn)一個(gè)服務(wù)來(lái)說(shuō)很簡(jiǎn)單偎巢,主要就是并發(fā)量較大的情況下蔼夜,服務(wù)器能不能承受住這種壓力正常地運(yùn)轉(zhuǎn),限時(shí)選課系統(tǒng)如果不作處理很難保證在后端運(yùn)行的時(shí)候不會(huì)出現(xiàn)響應(yīng)過(guò)慢甚至宕機(jī)的情況压昼,我還是選擇Nginx作為負(fù)載均衡服務(wù)器求冷,因?yàn)楣俜浇o定的Nginx訪問(wèn)的并發(fā)量最高能到5W,可是我看過(guò)實(shí)際測(cè)試也就只能到3W窍霞,但是對(duì)于我們這個(gè)系統(tǒng)..完全夠了匠题,其次就是Tomcat的集群,項(xiàng)目使用SpringBoot搭建但金,驗(yàn)證碼以及SSO處理邏輯會(huì)使用到Redis這種NoSql數(shù)據(jù)庫(kù)韭山,如果一旦使用到數(shù)據(jù)庫(kù),最好還是做數(shù)據(jù)庫(kù)的集群冷溃,主從庫(kù)的建立钱磅,Redis的集群以及主從庫(kù)設(shè)置可以看我上一篇博客,MySql的集群搭建似枕,主從庫(kù)的建立盖淡,MySql這里我沒(méi)有嘗試過(guò)搭建集群,所以也不再贅述凿歼,如果使用Nginx負(fù)載均衡去配合應(yīng)用服務(wù)器的集群的話(huà)褪迟,即使是應(yīng)用服務(wù)器集群中的某一臺(tái)宕機(jī)冗恨,也不會(huì)影響到別的服務(wù)器運(yùn)行也不會(huì)影響業(yè)務(wù)

項(xiàng)目架構(gòu)演進(jìn)示意圖

后端項(xiàng)目架構(gòu)演進(jìn)

集群產(chǎn)生的問(wèn)題

Cookie Session策略實(shí)現(xiàn)登錄邏輯

試想一下這個(gè)場(chǎng)景,后端采用Tomcat集群味赃,有5臺(tái)Tomcat派近,配置Nginx作為負(fù)載均衡服務(wù)器,采用權(quán)重策略進(jìn)行反向代理洁桌,假如Nginx將一個(gè)用戶(hù)的請(qǐng)求首先轉(zhuǎn)發(fā)到了Tomcat1上渴丸,用戶(hù)進(jìn)行了登錄,響應(yīng)中可以拿到cookie或者set-cookie字段另凌,并且value若是基于Tomcat應(yīng)用服務(wù)器的話(huà)谱轨,value的值基本都是JSESSION=xxxxxxxx類(lèi)似的情況,Tomcat底層維護(hù)著一個(gè)Map吠谢,通過(guò)這個(gè)JSESSIONID尋找屬于用戶(hù)與服務(wù)器之間的會(huì)話(huà)土童,并get到session對(duì)象,就可以實(shí)現(xiàn)訪問(wèn)放置在session中的一些用戶(hù)信息或一些其余別的放置在session中的敏感信息

問(wèn)題出現(xiàn)

cookie session策略用于解決Http無(wú)狀態(tài)的問(wèn)題工坊,但是如果集群Tomcat之后献汗,用戶(hù)如果登錄請(qǐng)求被Nginx轉(zhuǎn)發(fā)到了Tomcat1上,并且做了登錄王污,那么這個(gè)cookie默認(rèn)情況下會(huì)被保存至瀏覽器的緩存中罢吃,直至一次瀏覽器的生命周期結(jié)束cookie將被銷(xiāo)毀,但是這個(gè)cookie所對(duì)應(yīng)的session會(huì)話(huà)也只是針對(duì)于對(duì)客戶(hù)端/Web與Tomcat1之間昭齐,用戶(hù)登錄了尿招,那么之后呢?

如果用戶(hù)接下來(lái)訪問(wèn)個(gè)人信息頁(yè)阱驾,這個(gè)時(shí)候假如配置Nginx的負(fù)載均衡策略為權(quán)重策略就谜,并且5臺(tái)Tomcat的權(quán)重相同(轉(zhuǎn)發(fā)到每一臺(tái)的幾率都相同,還有ip hash等等一些策略去實(shí)現(xiàn)負(fù)載均衡里覆,這里也不贅述)丧荐,如果訪問(wèn)個(gè)人信息這個(gè)請(qǐng)求被Nginx轉(zhuǎn)發(fā)到了除Tomcat1之外的任意一臺(tái)服務(wù)器,都會(huì)出現(xiàn)一個(gè)問(wèn)題喧枷,這個(gè)問(wèn)題是什么大家都可以想一想

繼續(xù)要求登錄

因?yàn)檎?qǐng)求個(gè)人信息這個(gè)請(qǐng)求攜帶的cookie并不能標(biāo)示Tomcat2上的一次會(huì)話(huà)虹统,想來(lái)也很清楚,這個(gè)用戶(hù)根本沒(méi)在Tomcat2上做過(guò)登錄割去,那這樣的話(huà)集群帶來(lái)的代價(jià)有點(diǎn)高窟却,這樣的話(huà)如果集群的規(guī)模比較大昼丑,也就是說(shuō)有可能后來(lái)訪問(wèn)任何需要驗(yàn)證登錄的接口都會(huì)判斷為未登錄呻逆,這種情況只要不解決session共享問(wèn)題,那么都會(huì)出現(xiàn)問(wèn)題

如何解決session共享 實(shí)現(xiàn)SSO

github: https://github.com/challengerzsz/Mall 項(xiàng)目可以參考一下

  • 貼上一個(gè)簡(jiǎn)單的用戶(hù)登錄Controller菩帝,在登錄邏輯中咖城,若用戶(hù)登錄成功茬腿,則使用封裝的Cookie工具操作,實(shí)例化一個(gè)Cookie對(duì)象宜雀,并且設(shè)置時(shí)長(zhǎng)以及domain參數(shù)(為了讓這個(gè)cookie在請(qǐng)求二級(jí)域名的時(shí)候可以獲取到)切平,還有一些設(shè)置都可以自行百度,在代碼中設(shè)置的超時(shí)時(shí)間為1年辐董,可以根據(jù)自己的邏輯來(lái)使用悴品,最后向響應(yīng)中加入這個(gè)Cookie,Cookie中的key為一個(gè)常量简烘,value為登錄這次請(qǐng)求的會(huì)話(huà)sessionId

    /**
     * 用戶(hù)登錄
     *
     * @param username
     * @param password
     * @return
     */
    @PostMapping("/login")
    public ServerResponse<User> login(String username, String password, HttpSession session, HttpServletResponse response) {
    
        ServerResponse<User> serverResponse = userService.login(username, password);
        if (serverResponse.isSuccess()) {
            CookieUtil.writeLoginToken(response, session.getId());
            redisUtil.setRedisValueEx(session.getId(),    JsonUtil.objToString(serverResponse.getData()),
                        Const.RedisCacheExTime.REDIS_SESSION_EXTIME);
            }
            return serverResponse;
    }
    
    public static void writeLoginToken(HttpServletResponse response, String token) {
    
            Cookie cookie = new Cookie(COOKIE_NAME, token);
            cookie.setDomain(COOKIE_DOMAIN);
            //設(shè)置cookie的path為/ 這樣二級(jí)域名可以共享到最大域名下的cookie實(shí)現(xiàn)共享
            cookie.setPath("/");
          //通過(guò)腳本將無(wú)法讀取到Cookie信息苔严,避免腳本攻擊
            cookie.setHttpOnly(true);
            //若不設(shè)置cookie的有效期 生命周期為瀏覽器的生命周期 在內(nèi)存不會(huì)持久化到硬盤(pán)
            cookie.setMaxAge(60 * 60 * 24 * 365);
            logger.info("write cookieName :{}, cookieValue :{}", cookie.getName(), cookie.getValue());
            response.addCookie(cookie);
    }
    
  • 其實(shí)大家能夠看出來(lái),這種解決session共享的問(wèn)題是通過(guò)我們強(qiáng)行向?yàn)g覽器寫(xiě)入一個(gè)新Cookie孤澎,規(guī)定這個(gè)Cookie中的key為一個(gè)聲明的常量標(biāo)示這個(gè)Cookie届氢,value為首次登錄請(qǐng)求的那一次會(huì)話(huà)中,應(yīng)用服務(wù)器返回給瀏覽器的sessionId覆旭,當(dāng)然這個(gè)Cookie只會(huì)在登錄成功的邏輯下才會(huì)被回寫(xiě)回響應(yīng)

  • 調(diào)用Cookie工具校驗(yàn)是否登錄

    大家應(yīng)該已經(jīng)猜到封裝的Cookie工具要實(shí)現(xiàn)什么了退子,所有訪問(wèn)需要身份驗(yàn)證的接口都應(yīng)該調(diào)用這個(gè)工具類(lèi),首先從請(qǐng)求中取出Cookie型将,這里要強(qiáng)調(diào)一下寂祥,取出的Cookie如果做過(guò)登錄操作,那么應(yīng)該有兩個(gè)Cookie七兜,一個(gè)是Tomcat1自己返回給瀏覽器的Cookie壤靶,另一個(gè)是我們手動(dòng)寫(xiě)入的一個(gè)Cookie,通過(guò)校驗(yàn)是否存在有我們手寫(xiě)的這個(gè)Cookie惊搏,進(jìn)而判斷用戶(hù)是否已經(jīng)完成過(guò)登錄 贮乳,這樣就完了嗎?大家可以想一想恬惯,這個(gè)時(shí)候如果知道了服務(wù)端手寫(xiě)的Cookie的key就可以偽造一個(gè)Cookie去進(jìn)行請(qǐng)求向拆,那么如果校驗(yàn)邏輯真的就這樣的話(huà),我們?nèi)绾未_保這個(gè)用戶(hù)是我們的用戶(hù)酪耳,并且是登錄后訪問(wèn)的我們的服務(wù)浓恳?

  • HttpOnly

    大家應(yīng)該可以看到上面代碼段有設(shè)置cookie屬性的語(yǔ)句

    cookie.setHttpOnly(true);
    

    這句話(huà)是什么意思呢?

    如果cookie中設(shè)置了HttpOnly屬性碗暗,那么通過(guò)js腳本將無(wú)法讀取到cookie信息颈将,這樣能有效的防止XSS攻擊,竊取cookie內(nèi)容言疗,這樣就增加了cookie的安全性晴圾,即便是這樣,也不要將重要信息存入cookie噪奄。

    XSS全稱(chēng)Cross SiteScript死姚,跨站腳本攻擊人乓,是Web程序中常見(jiàn)的漏洞,XSS屬于被動(dòng)式且用于客戶(hù)端的攻擊方式都毒,所以容易被忽略其危害性色罚。其原理是攻擊者向有XSS漏洞的網(wǎng)站中輸入(傳入)惡意的HTML代碼,當(dāng)其它用戶(hù)瀏覽該網(wǎng)站時(shí)账劲,這段HTML代碼會(huì)自動(dòng)執(zhí)行戳护,從而達(dá)到攻擊的目的。如瀑焦,盜取用戶(hù)Cookie姑尺、破壞頁(yè)面結(jié)構(gòu)、重定向到其它網(wǎng)站等蝠猬。

    也就是說(shuō)cookie通過(guò)設(shè)置這一參數(shù)為true則可以實(shí)現(xiàn)防止腳本偽造cookie進(jìn)行攻擊切蟋,但是這樣后端就不需要校驗(yàn)了嗎?我在有的網(wǎng)站也看到了HttpOnly這種安全措施有的時(shí)候也不安全的說(shuō)法榆芦,那么我們?nèi)绾稳プ瞿?/p>

    Redis的參與

    細(xì)心的人應(yīng)該已經(jīng)看到上面UserController登錄中有一句代碼

  redisUtil.setRedisValueEx(session.getId(),    JsonUtil.objToString(serverResponse.getData()),
                      Const.RedisCacheExTime.REDIS_SESSION_EXTIME);

這句話(huà)是什么意思呢柄粹,我封裝了一個(gè)對(duì)RedisTemplate操作的工具類(lèi),通過(guò)使用RedisTemplate操作Redis匆绣,并且設(shè)置鍵值攜帶過(guò)期屬性驻右,Redis中的key為登錄時(shí)會(huì)話(huà)session的Id,值為將此用戶(hù)的實(shí)例通過(guò)封裝好的JsonUtil進(jìn)行序列化后的Json字符串崎淳,最終以字符串的形式作為key保存在Redis中

工具類(lèi)讀取Cookie校驗(yàn)的時(shí)候堪夭,如果有我們手寫(xiě)的Cookie并且有value的情況下,通過(guò)調(diào)用redis中的get方法去校驗(yàn)這個(gè)sessionId是否是登錄是我們set進(jìn)Redis中的值拣凹,如果能夠從Redis中通過(guò)這個(gè)sessionId能夠get到用戶(hù)的Json數(shù)據(jù)森爽,也就說(shuō)明確實(shí)登錄過(guò)也就防止了偽造,如需使用用戶(hù)信息的時(shí)候嚣镜,將這個(gè)Json字符串反序列化成為實(shí)例對(duì)象即可

封裝CookieUtil讀取Cookie的方法

  
  /**
   * 獲取屬于mall服務(wù)器下的cookie 并且返回cookie的值即登錄時(shí)的sessionId
   * @param request
   * @return
   */
  public static String readLoginToken(HttpServletRequest request
          Cookie[] cookies = request.getCookies();
          if (cookies != null) {
              for (Cookie cookie : cookies) {
                  logger.info("read cookieName :{} cookieValue :{}", cookie.getName(), cookie.getValue());
                  if (StringUtils.equals(cookie.getName(), COOKIE_NAME)) {
                      logger.info("return cookieName :{} cookieValue :{}", cookie.getName(), cookie.getValue());
                      return cookie.getValue();
                  }
              }
          }
          return null;
  }

調(diào)用需要校驗(yàn)身份信息的借口時(shí)可以這樣來(lái)操作

@GetMapping("/getInfo")
public ServerResponse<User> getInfo(HttpServletRequest request) {

    String loginToken = CookieUtil.readLoginToken(request);
    logger.error("error {}", loginToken);
    if (StringUtils.isEmpty(loginToken)) {
        return ServerResponse.createByErrorMsg("用戶(hù)未登錄");
    }
    String userJson = redisUtil.getRedisValue(loginToken);
    User currentUser = JsonUtil.stringToObj(userJson, User.class);
    if (currentUser == null) {
        return ServerResponse.createByErrorCodeMsg(ResponseCode.NEED_LOGIN.getCode(), "未登錄爬迟,需要強(qiáng)制登錄");
    return userService.getInfo(currentUser.getId());
}

如果博客中有問(wèn)題,請(qǐng)私信我一同解決

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末菊匿,一起剝皮案震驚了整個(gè)濱河市付呕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跌捆,老刑警劉巖徽职,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異佩厚,居然都是意外死亡姆钉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)育韩,“玉大人,你說(shuō)我怎么就攤上這事闺鲸〗钐郑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵摸恍,是天一觀的道長(zhǎng)悉罕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)立镶,這世上最難降的妖魔是什么壁袄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮媚媒,結(jié)果婚禮上嗜逻,老公的妹妹穿的比我還像新娘。我一直安慰自己缭召,他們只是感情好栈顷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著嵌巷,像睡著了一般萄凤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搪哪,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天靡努,我揣著相機(jī)與錄音,去河邊找鬼晓折。 笑死惑朦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的漓概。 我是一名探鬼主播行嗤,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼垛耳!你這毒婦竟也來(lái)了栅屏?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤堂鲜,失蹤者是張志新(化名)和其女友劉穎栈雳,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體缔莲,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哥纫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了痴奏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛀骇。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡厌秒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出擅憔,到底是詐尸還是另有隱情鸵闪,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布暑诸,位于F島的核電站蚌讼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏个榕。R本人自食惡果不足惜篡石,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望西采。 院中可真熱鬧凰萨,春花似錦、人聲如沸械馆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)狱杰。三九已至瘦材,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仿畸,已是汗流浹背食棕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留错沽,地道東北人簿晓。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像千埃,于是被迫代替她去往敵國(guó)和親憔儿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容