SpringSecurity整合JWT-詳細版

說明

  1. 這是一個比較詳盡的SpringSecurity整合JWT的例子(代碼直接可以運行,關鍵代碼都有很詳細的注釋)
  2. 本文并沒有使用spring oauth2,不要搞混
  3. 本文中的原理解釋只是大概的介紹,在代碼中有非常多的注釋,配合本文食用更佳
    DEMO地址 https://github.com/nixuechao/security-jwt

運行demo

什么也不需要配置,直接運行就行,提供了一個testController,運行期望如下

   /**任何人都能訪問
     * @return
     */
    @GetMapping("/publicMsg")
    public String getMsg(){
        return "you get the message!";
    }

    /**登錄的用戶才能訪問
     * @return
     */
    @GetMapping("/innerMsg")
    public String innerMsg(){
        return "you get the message!";
    }

    /**管理員(admin)才能訪問
     * @return
     */
    @GetMapping("/secret")
    public String secret(){
        return "you get the message!";
    }

登錄流程-關鍵類介紹

  • JwtLoginFilter 自定義的登錄過濾器,把它加到SpringSecurity的過濾鏈中,攔截登錄請求它干的事有

    1. 設置登錄的url,請求的方式,其實也就是定義這個過濾器要攔截哪個請求

    2. 調用JwtAuthenticationProvider進行登錄校驗

    3. 校驗成功調用LoginSuccessHandler,校驗失敗調用LoginSuccessHandler


  • JwtAuthenticationProvider 自定義的認證器,賬號密碼對不對等校驗就是它干的,主要功能

    1. 首先規定自己支持校驗那種憑證(Authentication)

    2. 進行用戶校驗,調用JwtUserDetailServiceImpl 查詢當前用戶(JwtUser),判斷用戶賬號密碼是否正確,用戶是否過期,被鎖定等等

    3. 若用戶校驗失敗則拋異常給JwtLoginFilter,JwtLoginFilter捕獲異常調用登錄失敗的處理類(LoginFailureHandler)

    4. 若用戶校驗成功,則生成一個已認證的憑證,也就是Authentication,對應本例的JwtLoginToken 并返回給JwtLoginFilter,JwtLoginFilter拿到憑證后調用登陸成功的處理類LoginSuccessHandler


  • JwtLoginToken 它就是上面說的憑證,繼承自Authentication

    1. 保存當前用戶的認證信息,如認證狀態,用戶名密碼,擁有的權限等

  • JwtUser 用戶實體,實現UserDetails,UserDetails為springSecurity默認的用戶實體抽象

    1. 主要需要實現UserDetails的幾個方法,如獲取用戶名,密碼,獲取用戶凍結狀態等

  • JwtUserDetailServiceImpl UserDetailsService的實現,提供根據用戶名查詢用戶信息的功能
    JwtAuthenticationProvider在進行登錄信息校驗時就會通過它查詢用戶信息


  • LoginFailureHandler 登錄失敗的處理類,被JwtLoginFilter調用,JwtLoginFilter捕獲到異常,就會調用它,并且把異常信息傳給它


  • LoginSuccessHandler 登錄成功的處理類,被JwtLoginFilter調用,并把JwtAuthenticationProvider創建的憑證(JwtLoginToken)傳給它,它就可以根據憑證里的認證信息進行登錄成功的處理,如生成token等

token校驗-關鍵類介紹

在登錄過程中,登錄成功,調用LoginSuccessHandler生成了token返回給前端,那么登錄成功后訪問其他路徑,如何根據token進行權限校驗呢

  • JwtKeyConfig 自定義的一個配置類,配置jwt,我這里的簽名驗證用的是RSA加密,在這里配置了密鑰對


  • JwtHeadFilter 實現token校驗的核心,這是自定義的過濾器,主要是請求通過過濾器時,會對其攜帶的token進行解析和校驗

    1. 獲取請求中攜帶的token
    2. 若沒有獲取到token則return,調交給接下來的過濾器鏈處理
    3. 若有token,但是校驗失敗,進行校驗失敗處理
    4. 若token校驗成功,通過從token中獲取的用戶信息生成一個憑證(Authentication),并放置到SecurityContext

在上面的2中沒有獲取到token為什么這么處理,首先springSecurity判斷用戶是否認證成功的標志是SecurityContext中是否有憑證(Authentication),在過濾鏈中,最后部分有一個匿名過濾器(AnonymousAuthenticationFilter),請求經過這個過濾器,若SecurityContext中沒有憑證,會被設置一個匿名憑證.

最后決定請求是否通過的過濾器是FilterSecurityInterceptor,它會調用WebExpressionVoter來決定當前用戶是否是否有權限訪問url,若沒有權限就會拋出AccessDeniedException,當拋出這個異常時就會有兩種處理條件,若SecurityContext 中的憑證是匿名的就表示請求中沒有token,需要登錄,若憑證不是匿名的就表示當前用戶沒有權限訪問次URL.

上面這個判斷邏輯發生在ExceptionTranslationFilter過濾器中,拋出異常時對應的操作可以在WebSecurityConfigurerAdapter中的configure方法中配置

......
http
                //身份驗證入口,當需要登錄卻沒登錄時調用
                //具體為,當拋出AccessDeniedException異常時且當前是匿名用戶時調用
                //匿名用戶: 當過濾器鏈走到匿名過濾器(AnonymousAuthenticationFilter)時,
                //會進行判斷SecurityContext是否有憑證(Authentication),若前面的過濾器都沒有提供憑證,
                //匿名過濾器會給SecurityContext提供一個匿名的憑證(可以理解為用戶名和權限為anonymous的Authentication),
                //這也是JwtHeadFilter發現請求頭中沒有jwtToken不作處理而直接進入下一個過濾器的原因
            .exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("需要登陸");
        })

                //拒絕訪問處理,當已登錄,但權限不足時調用
                //拋出AccessDeniedException異常時且當不是匿名用戶時調用
                .accessDeniedHandler((request, response, accessDeniedException) -> {
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write("沒有權限");
                })
                ......

轉載請注明出處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371