OAuth是一個(gè)關(guān)于授權(quán)的開(kāi)放網(wǎng)絡(luò)標(biāo)準(zhǔn)西饵,在全世界得到的廣泛的應(yīng)用汽纠,目前是2.0的版本。OAuth2在“客戶端”與“服務(wù)提供商”之間,設(shè)置了一個(gè)授權(quán)層(authorization layer)抹剩。“客戶端”不能直接登錄“服務(wù)提供商”栏饮,只能登錄授權(quán)層吧兔,以此將用戶與客戶端分離∨坻遥“客戶端”登錄需要OAuth提供的令牌境蔼,否則將提示認(rèn)證失敗而導(dǎo)致客戶端無(wú)法訪問(wèn)服務(wù)。下面我們就來(lái)講解下SpringBoot項(xiàng)目中是如何配置使用OAuth2服務(wù)器端伺通,并讓OAuth2整合SpringSecurity來(lái)保護(hù)我們的REST接口箍土。
免費(fèi)專題文章匯總
恒宇少年在博客整理出來(lái)了SpringBoot、ApiBoot罐监、SpringCloud的文章匯總【SpringBoot基礎(chǔ)教程專題】吴藻,【SpringCloud基礎(chǔ)教程專題】,【ApiBoot組件使用專題】
本章目標(biāo)
基于SpringBoot項(xiàng)目提供一個(gè)繼承OAuth2安全框架的REST API服務(wù)端弓柱,必須獲取訪問(wèn)授權(quán)令牌后才可以訪問(wèn)資源沟堡。
OAuth2授權(quán)方式
我們?cè)谖恼麻_(kāi)始已經(jīng)說(shuō)過(guò)了侧但,我們的保護(hù)資源必須通過(guò)授權(quán)得到的令牌才可以訪問(wèn)。那么我們這個(gè)授權(quán)令牌要通過(guò)什么方式獲取呢航罗?
OAuth2為我們提供了四種授權(quán)方式:
1禀横、授權(quán)碼模式(authorization code)
2、簡(jiǎn)化模式(implicit)
3粥血、密碼模式(resource owner password credentials)
4柏锄、客戶端模式(client credentials)
授權(quán)碼模式
授權(quán)碼相對(duì)其他三種來(lái)說(shuō)是功能比較完整、流程最安全嚴(yán)謹(jǐn)?shù)氖跈?quán)方式复亏,通過(guò)客戶端的后臺(tái)服務(wù)器與服務(wù)提供商的認(rèn)證服務(wù)器交互來(lái)完成趾娃。流程如下圖2所示:
簡(jiǎn)化模式
這種模式不通過(guò)服務(wù)器端程序來(lái)完成,直接由瀏覽器發(fā)送請(qǐng)求獲取令牌缔御,令牌是完全暴露在瀏覽器中的抬闷,這種模式極力不推崇。流程如下圖3所示:
密碼模式
密碼模式也是比較常用到的一種耕突,客戶端向授權(quán)服務(wù)器提供用戶名饶氏、密碼然后得到授權(quán)令牌。這種模式不過(guò)有種弊端有勾,我們的客戶端需要存儲(chǔ)用戶輸入的密碼,但是對(duì)于用戶來(lái)說(shuō)信任度不高的平臺(tái)是不可能讓他們輸入密碼的古程。流程如下圖4所示:
客戶端模式
客戶端模式是客戶端以自己的名義去授權(quán)服務(wù)器申請(qǐng)授權(quán)令牌蔼卡,并不是完全意義上的授權(quán)。如下圖5所示:
上述簡(jiǎn)單的介紹了OAuth2內(nèi)部的四種授權(quán)方式挣磨,我們下面使用密碼模式來(lái)進(jìn)行測(cè)試雇逞,并且我們使用數(shù)據(jù)庫(kù)中的用戶數(shù)據(jù)來(lái)做驗(yàn)證處理,下面我們先來(lái)構(gòu)建項(xiàng)目茁裙。
構(gòu)建項(xiàng)目
我們使用IndeiiJ IDEA工具來(lái)構(gòu)建一個(gè)SpringBoot項(xiàng)目塘砸,目前最新版本的是1.5.3,應(yīng)該是昨天剛正式發(fā)布晤锥。項(xiàng)目我們預(yù)先引入幾個(gè)模塊掉蔬,Web、JPA矾瘾、MySQL女轿、Security、SpringSecurityOAuth2壕翩、Druid等蛉迹,項(xiàng)目結(jié)構(gòu)如下圖6所示:
項(xiàng)目構(gòu)建完成后我們要配置數(shù)據(jù)庫(kù)表結(jié)構(gòu),因?yàn)槲覀円菙?shù)據(jù)庫(kù)內(nèi)保存AccessToken以及RefershToken還有我們的SpringSecurity用戶驗(yàn)證信息以及用戶角色信息等放妈。
配置數(shù)據(jù)庫(kù)
安全用戶信息表
用戶信息表包含了簡(jiǎn)單的登錄名北救、密碼荐操、郵箱、狀態(tài)等珍策。表結(jié)構(gòu)如下圖7所示:
安全角色信息表
角色信息表結(jié)構(gòu)如下圖8所示:
用戶角色關(guān)聯(lián)表
用戶與角色關(guān)聯(lián)表結(jié)構(gòu)如下圖9所示:
AccessToken信息表
我們使用的是SpringSecurityOAuth2提供的Jdbc方式進(jìn)行操作Token托启,所以需要根據(jù)標(biāo)準(zhǔn)創(chuàng)建對(duì)應(yīng)的表結(jié)構(gòu),access_token信息表結(jié)構(gòu)如下圖10所示:
RefreshToken信息表
刷新Token時(shí)需要用到refresh_token信息表結(jié)構(gòu)如下圖11所示:
我們的數(shù)據(jù)庫(kù)表結(jié)構(gòu)已經(jīng)建完了膛壹,下面我們只需要?jiǎng)?chuàng)建用戶信息驾中、角色信息的實(shí)體即可,因?yàn)镺Auth2內(nèi)部操作數(shù)據(jù)庫(kù)使用的JdbcTemplate我們只需要傳入一個(gè)DataSource對(duì)象就可以了模聋,實(shí)體并不需要配置肩民。
創(chuàng)建用戶實(shí)體
用戶實(shí)體如下圖12所示:
創(chuàng)建角色實(shí)體
角色實(shí)體如下圖13所示:
用戶實(shí)體以及角色實(shí)體是用來(lái)配置SpringSecurity時(shí)用到的實(shí)體,我們配置SpringSecurity時(shí)需要使用SpringDataJPA從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)链方,下我們來(lái)配置UserJPA以及AuthorityJPA持痰。
UserJPA
配置訪問(wèn)數(shù)據(jù)庫(kù)獲取用戶信息,代碼如下圖14所示:
我們?cè)赨serJPA內(nèi)添加了一個(gè)自定義查詢祟蚀,使用了HQL語(yǔ)法來(lái)構(gòu)建的語(yǔ)句工窍,根據(jù)用戶名不區(qū)分大小寫(xiě)進(jìn)行查詢。
Application.yml配置文件
我們從之前的項(xiàng)目中第十三章:SpringBoot實(shí)戰(zhàn)SpringDataJPA中源碼復(fù)制一個(gè)application.yml配置文件到項(xiàng)目resources下(注意:需要修改對(duì)應(yīng)的數(shù)據(jù)庫(kù)配置)前酿,如下圖所示:
AuthorityJPA
配置訪問(wèn)數(shù)據(jù)庫(kù)中的角色列表患雏,代碼如下圖15所示:
下面我們來(lái)配置兩個(gè)控制器用來(lái)區(qū)分我們配置OAuth2是否已經(jīng)生效。
HelloWorldController
我在HelloWorldController內(nèi)只添加一個(gè)字符串的輸出罢维,這個(gè)控制器我們開(kāi)放淹仑,讓SpringSecurity不去管理,配置將會(huì)在下面展現(xiàn)肺孵,控制器代碼如下圖16所示:
SecureController
這個(gè)控制器是需要我們獲取授權(quán)Token后使用Token才可以訪問(wèn)到的匀借,代碼如下圖17所示:
綜上所述我們的項(xiàng)目基礎(chǔ)的構(gòu)建已經(jīng)完成,大家都知道SpringSecurity在使用數(shù)據(jù)庫(kù)的數(shù)據(jù)時(shí)需要自定義UserDetailsService用來(lái)從數(shù)據(jù)庫(kù)中根據(jù)用戶名查詢用戶信息以及角色信息并返回給SpringSecurity存放到內(nèi)存中平窘。
自定義UserDetailsService
我們創(chuàng)建一個(gè)名叫HengYuUserDetailsService的類并且實(shí)現(xiàn)UserDetailsService接口吓肋,代碼如下圖18所示:
我們?cè)贖engYuUserDetailsService類中做了從數(shù)據(jù)庫(kù)讀取用戶的操作,如果沒(méi)有查詢到用戶直接拋出異常提示瑰艘,如果查詢到并且設(shè)置對(duì)應(yīng)的角色后返回SpringSecurity內(nèi)置的User對(duì)象實(shí)例是鬼。
開(kāi)啟SpringSecurity配置
下面我們來(lái)配置SpringSecurity相關(guān)的內(nèi)容,我們新創(chuàng)建一個(gè)配置類SecurityConfiguration磅叛,代碼如下圖19所示:
我們?cè)谂渲妙愔凶⑷肓松厦嫖覀冏远x的HengYuUserDetailsService以及用戶密碼驗(yàn)證規(guī)則屑咳,我們使用ignoring()方法排除了HelloWorldController內(nèi)的公開(kāi)方法,這里可以配置通配符的形式排除弊琴。
配置安全資源服務(wù)器
下面我們開(kāi)始配置相關(guān)OAuth2的內(nèi)容兆龙,我們創(chuàng)建一個(gè)OAuth2總配置類OAuth2Configuration,類內(nèi)添加一個(gè)子類用于配置資源服務(wù)器,如下圖20所示:
我們?cè)贠Auth2Configuration配置類中添加子類ResourceServerConfiguration繼承自ResourceServerConfigurerAdapter完成資源服務(wù)器的配置紫皇,使用@EnableResourceServer注解來(lái)開(kāi)啟資源服務(wù)器慰安,因?yàn)檎蟂pringSecurity的緣故,我們需要配置登出時(shí)清空對(duì)應(yīng)的access_token控制以及自定義401錯(cuò)誤內(nèi)容(authenticationEntryPoint)聪铺,在配置類中我們排除了對(duì)/hello公開(kāi)地址攔截以及/secure下的所有地址都必須授權(quán)才可以訪問(wèn)化焕。
自定義401錯(cuò)誤碼內(nèi)容
我們上圖已經(jīng)用到了對(duì)應(yīng)的類CustomAuthenticationEntryPoint,該類是用來(lái)配置如果沒(méi)有權(quán)限訪問(wèn)接口時(shí)我們返回的錯(cuò)誤碼以及錯(cuò)誤內(nèi)容铃剔,代碼如下圖21所示:
定義登出控制
當(dāng)我們退出系統(tǒng)時(shí)需要訪問(wèn)SpringSecrutiy的logout方法來(lái)清空對(duì)應(yīng)的session信息撒桨,那我們退出后改用戶的access_token還依然存在那就危險(xiǎn)了,一旦別人知道該token就可以使用之前登錄用戶的權(quán)限來(lái)操作業(yè)務(wù)键兜。logout控制代碼如下圖22所示:
開(kāi)啟OAuth2驗(yàn)證服務(wù)器
我們還是在OAuth2Configuration配置類中添加一個(gè)子類凤类,用于開(kāi)啟OAuth2的驗(yàn)證服務(wù)器,代碼如下圖23普气、24所示:
圖23中我們創(chuàng)建了一個(gè)名叫AuthorizationServerConfiguration的類繼承自AuthorizationServerConfigurerAdapter并且實(shí)現(xiàn)了EnvironmentAware(讀取properties文件需要)接口谜疤,并使用@EnableAuthorizationServer注解開(kāi)啟了驗(yàn)證服務(wù)器,可以看到我們使用SpringSecurityOAuth2內(nèi)定義的JdbcStore來(lái)操作數(shù)據(jù)庫(kù)中的Token现诀,當(dāng)然需要有需要我們可以通過(guò)SpringDataJPA自定義Sotre夷磕。
圖24中我們的OAuth2的客戶端配置并沒(méi)有從數(shù)據(jù)庫(kù)中讀取而是使用了內(nèi)存中獲取,因?yàn)楸菊碌膬?nèi)容比較多仔沿,所以在后期文章中我們會(huì)再次講到如何從數(shù)據(jù)庫(kù)中獲取clients進(jìn)行驗(yàn)證坐桩。我們?cè)趧?chuàng)建客戶端信息時(shí)使用到了application.properties配置文件的自定義配置,具體配置內(nèi)容如下圖25所示:
運(yùn)行測(cè)試
項(xiàng)目編寫(xiě)完成封锉,接下來(lái)我們使用SpringBootApplication形式來(lái)運(yùn)行項(xiàng)目進(jìn)行測(cè)試撕攒,運(yùn)行項(xiàng)目時(shí)查詢控制臺(tái)輸出日志是否正確!
我們先來(lái)使用Postman工具訪問(wèn)一下我們公開(kāi)的地址127.0.0.1:8080/hello烘浦,如下圖26所示:
可以看到我們是可以正確的訪問(wèn)到接口輸出內(nèi)容的,下面我們?cè)賮?lái)訪問(wèn)一下被oauth2管理的地址127.0.0.1:8080/secure萍鲸,如下圖27所示:
我們可以看到直接給我們返回了一個(gè)頁(yè)面闷叉,這樣就不對(duì)了,我們應(yīng)該得到一個(gè)401的錯(cuò)誤碼以及自定義的信息才對(duì)脊阴,當(dāng)然我們需要添加一些配置來(lái)完成這個(gè)功能握侧,我們打開(kāi)application.properties配置文件添加如下圖28配置:
圖中畫(huà)紅色框的就是我們新添加的配置內(nèi)容,這個(gè)配置的意思時(shí)嘿期,將我們的資源攔截的過(guò)濾器運(yùn)行順序放到第3個(gè)執(zhí)行品擎,也就是在oauth2的認(rèn)證服務(wù)器后面執(zhí)行,我們重啟下項(xiàng)目再來(lái)訪問(wèn)下剛才的地址备徐,輸出內(nèi)容如下圖29所示:
可以看到正如我們預(yù)期一樣萄传,返回了401錯(cuò)誤以及我們自定義的錯(cuò)誤碼”Access Denied“,下面我們來(lái)獲取access_token蜜猾。
獲取AccessToken
我們?cè)讷@取token之前需要在數(shù)據(jù)庫(kù)中添加幾條對(duì)應(yīng)的數(shù)據(jù)秀菱,具體的SQL我會(huì)放到源碼項(xiàng)目的resources目錄下振诬,文章地址有源碼地址。我們來(lái)訪問(wèn)/oauth/token地址獲取access_token衍菱,如下圖30所示:
可以看到我們?cè)L問(wèn)的地址赶么,grant_type使用到了password模式,我們?cè)谏厦娴呐渲弥芯褪桥渲梦覀兊目蛻舳耍▂uqiyu_home_pc)可以執(zhí)行的模式有兩種:password脊串、refresh_token辫呻。獲取access_token需要添加客戶端的授權(quán)信息clientid、secret琼锋,通過(guò)Postman工具的頭授權(quán)信息即可輸出對(duì)應(yīng)的值就可以完成Basic Auth的加密串生成放闺。
成功訪問(wèn)后oauth2給我們返回了幾個(gè)參數(shù):
access_token:本地訪問(wèn)獲取到的access_token,會(huì)自動(dòng)寫(xiě)入到數(shù)據(jù)庫(kù)中斩例。
token_type:獲取到的access_token的授權(quán)方式
refersh_token:刷新token時(shí)所用到的授權(quán)token
expires_in:有效期(從獲取開(kāi)始計(jì)時(shí)雄人,值秒后過(guò)期)
scope:客戶端的接口操作權(quán)限(read:讀,write:寫(xiě))
使用AccessToken訪問(wèn)
我們使用獲取到的access_token值來(lái)訪問(wèn)對(duì)應(yīng)的地址http://127.0.0.1:8080/secure?access_token=9ca7fd9b-1289-440b-b1a1-0303782f660e念赶,效果如下圖31所示:
可以看到我們已經(jīng)可以正常的訪問(wèn)到數(shù)據(jù)內(nèi)容了础钠,證明我們的access_token是有效的。當(dāng)我們用到的token已經(jīng)過(guò)期時(shí)效果如下圖32所示:
oauth2告訴我們需要刷新Token了叉谜,您傳入的token值已經(jīng)過(guò)期了旗吁。
刷新AccessToken
我們的access_token過(guò)期我們需要刷新后返回新的token,使用新token才能繼續(xù)操作數(shù)據(jù)接口停局。刷新access_token如下圖33所示:
看到上圖33紅色框內(nèi)的值了嗎很钓?這個(gè)就是我們之前獲取token時(shí),oauth2給我們返回的refresh_token值董栽,我們需要用到該值來(lái)進(jìn)行刷新token码倦。新的token值得有效期可以看到又是我們配置的默認(rèn)1800秒,刷新token時(shí)oauth2還是給我們返回了一個(gè)refersh_token值锭碳,該值要作為下次刷新token時(shí)使用袁稽。
總結(jié)
綜上內(nèi)容就是本章的全部?jī)?nèi)容,本章的內(nèi)容比較多希望讀者可以仔細(xì)閱讀擒抛,本章主要講解了SpringBoot作為框架基礎(chǔ)上配置SpringSecurity安全框架整合OAuth2安全框架做雙重安全推汽,講解如果通過(guò)數(shù)據(jù)庫(kù)的形式獲取到授權(quán)用戶信息以及角色列表,通過(guò)內(nèi)存配置的OAuth2的客戶端配置來(lái)獲取access_token以及如何使用access_token訪問(wèn)受保護(hù)的資源接口歧沪。
本章代碼已經(jīng)上到碼云:
SpringBoot配套源碼地址:https://gitee.com/hengboy/spring-boot-chapter
SpringCloud配套源碼地址:https://gitee.com/hengboy/spring-cloud-chapter
SpringBoot相關(guān)系列文章請(qǐng)?jiān)L問(wèn):目錄:SpringBoot學(xué)習(xí)目錄
QueryDSL相關(guān)系列文章請(qǐng)?jiān)L問(wèn):QueryDSL通用查詢框架學(xué)習(xí)目錄
SpringDataJPA相關(guān)系列文章請(qǐng)?jiān)L問(wèn):目錄:SpringDataJPA學(xué)習(xí)目錄
SpringBoot相關(guān)文章請(qǐng)?jiān)L問(wèn):目錄:SpringBoot學(xué)習(xí)目錄歹撒,感謝閱讀!
歡迎微信掃碼加入知識(shí)星球诊胞,恒宇少年帶你走以后的技術(shù)道路E病!!
知識(shí)星球 - 恒宇少年