微服務(wù) & SSO & Session & Token(Cookie)

三種架構(gòu)

前后端半分離架構(gòu)
前后端半分離架構(gòu).png
前后端分離架構(gòu)
前后端分離架構(gòu).png
將 Postman 升級成前端服務(wù)器
將 Postman 升級成前端服務(wù)器.png

前后端分離架構(gòu) | 實(shí)現(xiàn)概述

  • admin 項(xiàng)目的角色是:前端服務(wù)器利虫;
  • admin 項(xiàng)目原本應(yīng)該是個 Node.js + Angular 的項(xiàng)目,這里使用 Springboot + Angular 來代替糠惫;
  • Angular Build 完的結(jié)果,直接放在 Springboot 的 java/main/resource/static 下硼讽;
  • admin 項(xiàng)目向 zuul 發(fā)送請求;
  • 在認(rèn)證服務(wù)器的客戶端應(yīng)用列表中壤躲,要加上 admin 項(xiàng)目备燃;

真實(shí)壞境下碉克,Web 應(yīng)用部署在 NodeJS 中并齐,瀏覽器向 NodeJS 發(fā)請求,NodeJS 把請求發(fā)到 Zuul撕贞,Zuul 中完成限流测垛、認(rèn)證麻掸、審計(jì)赐纱、授權(quán)熬北,完了調(diào)用業(yè)務(wù)系統(tǒng)诚隙;

SSO & Authorizatin code grant & 前端服務(wù)器

  • OAuth2 的 Authorizatin code grant 認(rèn)證模式的實(shí)現(xiàn),需要引入前端服務(wù)器久又;
  • 引入了前端服務(wù)器,就實(shí)現(xiàn)了 SSO地消;
  • 當(dāng)前端服務(wù)器重啟后,瀏覽器訪問前端服務(wù)器疼阔,直接就是登錄狀態(tài);因?yàn)樵?Authorizatin code grant 認(rèn)證模式下婆廊,登錄的位置是認(rèn)證服務(wù)器巫橄,只要登錄服務(wù)器上的 Session 沒過期,認(rèn)證服務(wù)器就知道湘换,從瀏覽器來的請求是誰,不用再輸用戶名和密碼了彩倚,然后直接跳到客戶端應(yīng)用,如果之前頒發(fā)給這個用戶的 Token 沒有過期崎溃,就把之前的 Token 返回給前端服務(wù)器盯质;如果無效,就生成一個新 Token 發(fā)給前端服務(wù)器呼巷;這帶來的一個問題是,如果用戶的登出王悍,只是清空前端服務(wù)器的 Session,會導(dǎo)致用戶無法登出鲜漩;
  • 前端服務(wù)器可以有多個,任何一個登錄成功孕似,Session 信息都會存在認(rèn)證服務(wù)器中,當(dāng)瀏覽器再發(fā)登錄請求到第二個前端服務(wù)器中喉祭,前端服務(wù)器去認(rèn)證服務(wù)器中拿的 Token 都是一樣的,這就是 SSO理卑;

基于 Session 的 SSO

2 個 Session
  • 認(rèn)證服務(wù)器的 Session蔽氨,存的是用戶信息和 Session ID,Session ID 返回給瀏覽器作為 Cookie孵滞,這個 Cookie(SESSION) 和前端服務(wù)器返回給瀏覽器的 Cookie(JSESSIONID) 不是同一個鸯匹;
  • 前端服務(wù)器的 Session,存的是用戶的 Token殴蓬;
3 個有效期
  • 認(rèn)證服務(wù)器 Session 的有效期,控制多長時(shí)間需要用戶輸一次用戶名和密碼痘绎;
  • 前端服務(wù)器 Session 的有效期肖粮,控制的是多長時(shí)間跳轉(zhuǎn)一次認(rèn)證服務(wù)器;
  • Token 的有效期涩馆,控制登錄一次能訪問多長時(shí)間的微服務(wù);
優(yōu)點(diǎn)
  • Session 信息都是存儲在服務(wù)器里魂那,不論是前端服務(wù)器還是認(rèn)證服務(wù)器;
  • Token 信息和 Session 信息都存儲在數(shù)據(jù)庫中鲜结,可控性高,可以看到系統(tǒng)中所有的登錄狀態(tài)精刷,想讓誰下線就讓誰下線;
  • 沒有跨域問題挤土,客戶端應(yīng)用不管部署在什么域名下都可以和認(rèn)證服務(wù)器交互误算,基于 Token 的 SSO 會有同域的問題仰美;
缺點(diǎn)
  • 復(fù)雜度高:2 套機(jī)制,Session + Token咖杂;
  • 性能比較低蚊夫,占用應(yīng)用服務(wù)器的內(nèi)存存儲 Session;
適用
  • 適用與百萬用戶以下或有一定規(guī)模的公司的內(nèi)部管理系統(tǒng)知纷;

Logout 時(shí)同步前端服務(wù)器 & 認(rèn)證服務(wù)器的 Session

  • 點(diǎn)擊 Logout 按鈕,在前端服務(wù)器將 session 失效后伍绳,要向認(rèn)證服務(wù)器發(fā)一個請求 http://auth.imooc.com:9090/logout?redirect_uri=http://admin.imooc.com:8080;
  • 重寫 Spring Security 的 DefaultLogoutPageGeneratingFilter 過濾器乍桂,這個過濾器原本會生成一個是否確定登出的 Form 表單,重寫后將其邏輯變成睹酌,F(xiàn)orm 表單渲染好了之后,直接 Submit憋沿,并且通過一個 hidden 的 input 把參數(shù) redirect_uri=http://admin.imooc.com:8080 提交給認(rèn)證服務(wù)器的 Logout 邏輯;
  • 寫一個 Logout 成功后要做什么的 Handler:OAuth2LogoutSuccessHandler甥绿,在 Logout 成功后则披,重定向到前端服務(wù)器的首頁;
  • 把自己寫的 OAuth2LogoutSuccessHandler 配置到認(rèn)證服務(wù)器中:
@Configuration
@EnableWebSecurity // 讓安全配置生效
public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {   

    // ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .formLogin().and() // 這里可以配置自己的登錄頁
        .httpBasic().and()
        // 自己寫的士复,logout 成功以后的 handler
        .logout().logoutSuccessHandler(logoutSuccessHandler)
        ;
    }

}

重要的事情說三遍
  • 跨域翩活!跨域便贵!跨域!
  • Logout 的 window.location.href = 'http://localhost:9090/logout?redirect_uri=http://localhost:8080' 和 Login 的 window.location.href = 'http://localhost:9090/oauth/authorize?' 的域名必須一樣利耍,否則盔粹,Logout 和 Login 帶的 SESSION 會不一樣隘梨,用戶在數(shù)據(jù)庫中的 session 信息刪不掉舷嗡,用戶登出失敗捻脖;

Token 的有效期

Token 是短活的令牌中鼠,Token 過期的時(shí)候可婶,可能 Session 還沒過期。

refresh token
refresh token.png
  • access_token:短生命周期矛渴;
  • refresh_token:長生命周期熊杨;
  • 客戶端應(yīng)用拿 refresh_token 不斷的刷 access_token盗舰;
access_token vs access_token
  • 拿到 access_token 可以任意訪問微服務(wù),而且是合法的钻趋;
  • refresh_token 在使用的時(shí)候,必須和 client_id较沪,client_secret 一起使用失仁;
refresh_token 的啟用
  • 字段 oauth_client_details.refresh_token_validity 必須要填,單位是 s萄焦,只要這個字段有值冤竹,在發(fā) access_token 的同時(shí)茬射,會發(fā) refresh_token;
  • 去認(rèn)證服務(wù)器刷新 Token 的客戶端應(yīng)用在抛,在認(rèn)證服務(wù)器中配置的信息的 authorized_grant_types 字段中,要加上 refresh_token肠阱;
  • @EnableAuthorizationServer 的配置類中望浩,要給 refresh_token 請求專門配置一個 userDetailsService:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints
            // 這個 userDetailsService 是專門給 refresh_token 用的
            .userDetailsService(userDetailsService)
            .tokenStore(tokenStore())
            // 這個是支持前 4 種認(rèn)證模式的:password, code, ... , ...
            .authenticationManager(authenticationManager);
}
  • refresh_token 請求的 grant_typerefresh_token

refresh_token 過期了怎么辦

方案一 | 強(qiáng)制 Logout
  • 當(dāng) refresh_token 失敗后磨德,返回前端 500,完了帶一個專門標(biāo)記 refresh_token 失敗的標(biāo)記酥宴;
  • 前端的 HttpClient 加一個攔截器您觉,在請求之后攔截,如果收到 refresh_token 失敗的標(biāo)記琳水,調(diào)用 Logout 接口,前端服務(wù)器中的 session 失效诚啃,認(rèn)證服務(wù)器中的 session 失效,讓用戶重新登錄始赎;
方案二 | 讓認(rèn)證服務(wù)器決定
  • 直接請求認(rèn)證服務(wù)器的 /oauth/authorize 接口仔燕,讓認(rèn)證服務(wù)器決定是否要重新登錄;
  • 如果認(rèn)證服務(wù)器中的 session 沒過期晰搀,回調(diào)到前端服務(wù)器,前端服務(wù)器獲取 Token 后存儲在自己的 session 中杆逗;
  • 如果認(rèn)證服務(wù)器中的 session 過期,讓瀏覽器重定向到登錄頁面髓迎;

基于 Token 的 SSO

步驟
  • 瀏覽器訪問首頁,請求會先經(jīng)過前端服務(wù)器的 CookieTokenFilter排龄,發(fā)現(xiàn)請求既沒有 access_token,也沒有 refresh_token 后尺铣,會調(diào)用認(rèn)證服務(wù)器的登錄接口争舞,認(rèn)證服務(wù)器返回登錄頁,輸入用戶名密碼后發(fā)請求到認(rèn)證服務(wù)器登錄竞川;
  • 在認(rèn)證服務(wù)器上登錄成功后,重定向到前端服務(wù)器后委乌,前端服務(wù)器獲取到 Token 后,不存儲在前端服務(wù)器本地的 session 中戈咳,而是將 Token(access_token, refresh_token) 作為 Cookie 返回給瀏覽器壕吹;
  • 瀏覽器重定向到首頁,請求還是會經(jīng)過 CookieTokenFilter耳贬,此時(shí)有 access_token,CookieTokenFilter 會把 access_token 放入 Header 中暂吉,然后放行到網(wǎng)關(guān)缎患,請求進(jìn)入網(wǎng)關(guān)限流攔截器,認(rèn)證攔截器挤渔,審計(jì)攔截器风题,授權(quán)攔截器后進(jìn)入 /user/me 攔截器嫉父,/user/me 攔截器判斷請求的 Header 中有 username 信息(授權(quán)攔截器放進(jìn)去的)眼刃,返回 username 給前端,前端看到有返回?cái)?shù)據(jù)擂红,就進(jìn)入登錄后的頁面;
  • 瀏覽器帶著 Cookie(token) 訪問 order 服務(wù)树碱,經(jīng)過 CookieTokenFilter 后变秦,CookieTokenFilter 會把 token 放進(jìn) Header 中成榜,經(jīng)過網(wǎng)關(guān)的限流蹦玫、認(rèn)證、審計(jì)惑淳、授權(quán)后饺窿,會進(jìn)入 order 服務(wù),完成對 order 服務(wù)的訪問肚医;
  • 登出的時(shí)候,瀏覽器先把 Cookie 清空肠套,然后訪問認(rèn)證服務(wù)器的登出接口,登出后瓷耙,重定向到首頁刁赖,首頁會先訪問 /user/me 接口,但是會被 CookieTokenFilter 攔截下來宇弛,CookieTokenFilter 發(fā)現(xiàn)沒有 access_token 和 refresh_token,會訪問認(rèn)證服務(wù)器的登錄接口彻况,認(rèn)證服務(wù)器返回登錄頁谁尸;
基于 Token 的 SSO vs 基于 Session 的 SSO
  • 基于 Session 的 SSO纽甘,每當(dāng)前端服務(wù)器的 session 失效后,就要訪問認(rèn)證服務(wù)器去判斷 session 是否過期背镇,由認(rèn)證服務(wù)器決定放回前端服務(wù)器舊的 Token泽裳,還是返回瀏覽器登錄頁;
  • 基于 Token 的 SSO涮总,當(dāng)瀏覽器中的 Cookie 失效后,才會去認(rèn)證服務(wù)器一次認(rèn)證烹笔;
  • 不管那種方案抛丽,在認(rèn)證服務(wù)器上,都會有用戶的 session亿鲜, 基于 Session 的 SSO 需要認(rèn)證服務(wù)器上的 session 有效期較長,這樣才不會導(dǎo)致前端服務(wù)器頻繁的訪問認(rèn)證服務(wù)器蒿柳;基于 Token 的 SSO 不需要認(rèn)證服務(wù)器上的 session 有效期很長,只要瀏覽器中 Cookie 有效妓蛮,就能訪問服務(wù)圾叼,哪怕認(rèn)證服務(wù)器中的 session 已經(jīng)失效,只要 Cookie 中的 access_token 沒失效夷蚊,任然可以繼續(xù)訪問微服務(wù);
優(yōu)點(diǎn)
  • 復(fù)雜度較低儿倒,只需要考慮兩個 Token 過期要怎么處理就可以了呜笑;
  • 更適合海量用戶的場景,比如有上千萬的用戶叫胁,不可能把這些用戶的信息都存在 前端服務(wù)器的 session 中;
缺點(diǎn)
  • 安全性較低微谓,access_token 存儲在 Cookie 中了输钩,可以使用 HTTPS 保證 Cookie 的傳遞豺型,還有就是放在 Cookie 中的 Token 不會有很長的有效期买乃;如果是 JWT 的話,瀏覽器中還會存用戶信息肴焊,那樣的安全風(fēng)險(xiǎn)更高 功戚;
  • 可控性較低,因?yàn)?access_token 是存儲在瀏覽器的 Cookie 中啸臀,沒法由管理人員手動失效掉 access_token;
  • 沒法跨域席揽,因?yàn)榻o瀏覽器的 Cookie 是有域名限制的谓厘,不在同一個一級域名下的前端應(yīng)用,是無法做到 SSO 的竟稳;可以通過往返回給瀏覽器的 Cookie 中加多個域名;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末聂宾,一起剝皮案震驚了整個濱河市诊笤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖鄙煤,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茶袒,死亡現(xiàn)場離奇詭異,居然都是意外死亡亡资,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門锥腻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來母谎,“玉大人,你說我怎么就攤上這事供璧《臣牵” “怎么了睡毒?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵演顾,是天一觀的道長隅居。 經(jīng)常有香客問我,道長胎源,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任宪卿,我火速辦了婚禮万栅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烦粒。我一直安慰自己,他們只是感情好兽掰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著禾进,像睡著了一般廉涕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宠纯,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天层释,我揣著相機(jī)與錄音,去河邊找鬼廉白。 笑死,一個胖子當(dāng)著我的面吹牛猴蹂,可吹牛的內(nèi)容都是我干的楣嘁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼逐虚,長吁一口氣:“原來是場噩夢啊……” “哼聋溜!你這毒婦竟也來了叭爱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤馒胆,失蹤者是張志新(化名)和其女友劉穎凝果,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體器净,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年沿量,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朴则。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡钓简,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撤蚊,到底是詐尸還是另有隱情,我是刑警寧澤侦啸,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布丧枪,位于F島的核電站,受9級特大地震影響拧烦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜服赎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一交播、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秦士,春花似錦、人聲如沸提针。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皆愉。三九已至艇抠,卻和暖如春久锥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瑟由。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留青伤,地道東北人暂氯。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓亮蛔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親究流。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349