javascript
spring安全性_具有PreAuthorize的Spring方法安全性
spring安全性
朋友不允許朋友寫用戶身份驗(yàn)證。 厭倦了管理自己的用戶? 立即嘗試Okta的API和Java SDK。 數(shù)分鐘之內(nèi)即可在任何應(yīng)用程序中對(duì)用戶進(jìn)行身份驗(yàn)證,管理和保護(hù)。
本教程將探討使用Spring Security在Spring Boot中配置身份驗(yàn)證和授權(quán)的兩種方法。 一種方法是創(chuàng)建WebSecurityConfigurerAdapter并使用流暢的API覆蓋HttpSecurity對(duì)象上的默認(rèn)設(shè)置。 另一個(gè)方法是在控制器方法上使用@PreAuthorize批注,稱為方法級(jí)安全性或基于表達(dá)式的安全性。 后者將是本教程的重點(diǎn)。 但是,我將通過對(duì)比介紹一些HttpSecurity代碼和想法。
第一種身份驗(yàn)證方法是HttpSecurity ,它是全局的,默認(rèn)情況下應(yīng)用于所有請(qǐng)求。 但是,可以使用端點(diǎn)的模式匹配來進(jìn)行更細(xì)粒度的控制,并且HttpSecurity公開的流暢API十分強(qiáng)大。 在此處公開了OAuth 2.0,表單登錄和HTTP Basic等配置選項(xiàng)。 這是設(shè)置全局身份驗(yàn)證策略的好地方。
方法級(jí)別的安全性是通過將@PreAuthorize批注放置在控制器方法上(實(shí)際上是一組可用的批注之一,但最常用)來實(shí)現(xiàn)。 該批注包含一個(gè)Spring Expression Language(SpEL)代碼段,該代碼段經(jīng)過評(píng)估以確定是否應(yīng)該對(duì)該請(qǐng)求進(jìn)行身份驗(yàn)證。 如果未授予訪問權(quán)限,則不執(zhí)行該方法,并返回HTTP Unauthorized。 實(shí)際上,在控制器方法上使用@PreAuthorize注釋與在特定端點(diǎn)上使用HttpSecurity模式匹配器非常相似。 但是有一些區(qū)別。
區(qū)分Spring Security的@PreAuthorize和HttpSecurity
第一個(gè)區(qū)別是微妙的,但值得一提。 在發(fā)生控制器映射之前, HttpSecurity方法會(huì)在Web請(qǐng)求篩選器中更早地拒絕該請(qǐng)求。 相比之下, @PreAuthorize評(píng)估在控制器方法執(zhí)行之前@PreAuthorize進(jìn)行。 這意味著在HttpSecurity 之前應(yīng)用 @PreAuthorize中的配置。
其次, HttpSecurity綁定到URL端點(diǎn),而@PreAuthorize綁定到控制器方法,并且實(shí)際上位于與控制器定義相鄰的代碼內(nèi) 。 將所有安全性集中在一處并由Web終結(jié)點(diǎn)定義具有一定的整潔性,尤其是在較小的項(xiàng)目或更全局的設(shè)置中; 但是,隨著項(xiàng)目的擴(kuò)大,使授權(quán)策略靠近受保護(hù)的代碼可能更有意義,這就是基于注釋的方法所允許的。
另一個(gè)優(yōu)點(diǎn)是@PreAuthorize呈現(xiàn)在HttpSecurity就是利用規(guī)劃環(huán)境地政司的。 使用Spring Expression Language,您可以基于復(fù)雜的表達(dá)式來做出授權(quán)決策,這些復(fù)雜的表達(dá)式可以訪問內(nèi)置的身份驗(yàn)證對(duì)象(例如authentication和principal ),注入依賴項(xiàng)的方法參數(shù)和查詢參數(shù)。 在本教程中,您將主要看兩個(gè)表達(dá)式: hasAuthority()和hasRole() 。 Spring文檔再次是深入研究的好地方。
在深入研究項(xiàng)目之前,我還要提到Spring還提供了@PostAuthorize批注。 毫不奇怪,這是在方法執(zhí)行后評(píng)估的方法級(jí)授權(quán)注釋。 我們?yōu)槭裁匆@樣做? 它允許方法在評(píng)估注釋之前根據(jù)喜歡的任何控制器邏輯執(zhí)行自己的授權(quán)檢查。 缺點(diǎn)是,由于控制器方法是在評(píng)估注釋之前執(zhí)行的,因此可能會(huì)導(dǎo)致效率低下,具體取決于實(shí)現(xiàn)方式。
依存關(guān)系
本教程的依賴項(xiàng)非常簡(jiǎn)單。 您需要:1)已安裝Java 8+,以及2)Okta開發(fā)人員帳戶。
如果沒有安裝Java,請(qǐng)轉(zhuǎn)到AdoptOpenJDK 。 在* nix系統(tǒng)上,您也可以使用SDKMAN 。
如果您還沒有免費(fèi)的Okta開發(fā)者帳戶,請(qǐng)?jiān)L問我們的網(wǎng)站并注冊(cè) 。
使用Spring Initializr啟動(dòng)示例項(xiàng)目
要啟動(dòng)項(xiàng)目,可以使用Spring Initializr 。 但是,盡管值得一游,但您甚至不必費(fèi)心去那里創(chuàng)建項(xiàng)目。 您可以使用REST API和curl 。
打開終端,然后將cd轉(zhuǎn)到您希望項(xiàng)目文件.zip結(jié)束的任何位置。 運(yùn)行以下命令,該命令將下載壓縮的Spring Boot項(xiàng)目。
curl https://start.spring.io/starter.zip \-d dependencies=web,security \-d type=gradle-project \-d bootVersion=2.1.5.RELEASE \-d groupId=com.okta.preauthorize \-d artifactId=application \-o PreAuthorizeProject.zip unzip PreAuthorizeProject.zip除了build.gradle文件和DemoApplication.java類文件之外,該項(xiàng)目沒有多少其他內(nèi)容。 但是,已經(jīng)為您設(shè)置了整個(gè)項(xiàng)目結(jié)構(gòu)。
對(duì)于本示例, build.gradle文件還具有兩個(gè)Spring依賴項(xiàng):
dependencies {implementation 'org.springframework.boot:spring-boot-starter-security'implementation 'org.springframework.boot:spring-boot-starter-web' }添加一個(gè)WebController
處于當(dāng)前狀態(tài)的示例應(yīng)用程序執(zhí)行的操作不多。 您需要添加一個(gè)控制器來定義一些端點(diǎn)和響應(yīng)。
添加一個(gè)新文件src/main/java/com/okta/preauthorize/application/WebController.java :
package com.okta.preauthorize.application; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class WebController { @RequestMapping("/") @ResponseBody public String home() { return "Welcome home!"; } @RequestMapping("/restricted") @ResponseBody public String restricted() { return "You found the secret lair!"; } }該控制器定義兩個(gè)端點(diǎn): /和/restricted 。 您將稍后向/restricted端點(diǎn)添加方法級(jí)安全性。 但是,目前尚未配置安全性。
繼續(xù)運(yùn)行該應(yīng)用程序。 從項(xiàng)目根目錄運(yùn)行:
./gradlew bootRunSpring Boot完成啟動(dòng)后,導(dǎo)航至http://localhost:8080 。
您會(huì)注意到出現(xiàn)了一個(gè)登錄表單。 哇! 那個(gè)是從哪里來的?!
該表單是由Spring Boot自動(dòng)生成的。 看一下Spring類WebSecurityConfigurerAdapter和方法configure(HttpSecurity http) 。 這是配置默認(rèn)身份驗(yàn)證設(shè)置的位置。
/** * Override this method to configure the {@link HttpSecurity}. Typically subclasses * should not invoke this method by calling super as it may override their * configuration. The default configuration is: * * * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic(); * * * @param http the {@link HttpSecurity} to modify * @throws Exception if an error occurs */ protected void configure(HttpSecurity http) throws Exception { ... http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); }默認(rèn)情況下,所有請(qǐng)求都需要身份驗(yàn)證。 對(duì)所有請(qǐng)求都啟用了基于表單的身份驗(yàn)證和HTTP Basic身份驗(yàn)證。
如果要登錄,請(qǐng)?jiān)诳刂婆_(tái)輸出中查找類似于以下內(nèi)容的行,以告訴您所生成的密碼:
Using generated security password: 9c299bb9-f561-4c12-9810-c9a2bc1dca08用戶名就是“用戶”。 每次運(yùn)行Spring Boot時(shí)都會(huì)重新生成此密碼。
身份驗(yàn)證與授權(quán)
在繼續(xù)進(jìn)行之前,我想快速確保兩個(gè)術(shù)語明確:身份驗(yàn)證和授權(quán)。 身份驗(yàn)證回答問題:誰在發(fā)出請(qǐng)求? 授權(quán)回答了這個(gè)問題:他們被允許做什么?
通常,首先進(jìn)行身份驗(yàn)證,除非為匿名用戶設(shè)置了特定的權(quán)限(在某些方面這是隱式身份驗(yàn)證)。 授權(quán)基于經(jīng)過身份驗(yàn)證的用戶的價(jià)值。 認(rèn)證的實(shí)體可以是人類用戶或自動(dòng)化服務(wù),也可以是代表人類用戶運(yùn)行的服務(wù)。
兩種常見的授權(quán)方案基于組和角色。 這兩個(gè)術(shù)語通常在網(wǎng)絡(luò)上不太知名的地方被混用并互換使用,但是在官方上有區(qū)別。 組定義用戶組,并向這些用戶組分配權(quán)限。 用戶可以是多個(gè)組的成員。 角色定義可以分配給用戶的權(quán)限集(允許的操作或資源)。 在實(shí)踐中,組往往是一種更靜態(tài),更不靈活的控制器訪問方式,而角色通常是精細(xì)劃分的,甚至在會(huì)話中也可以是動(dòng)態(tài)的,為特定任務(wù)分配角色,并在不再需要它們時(shí)將其撤銷。 任何使用過Amazon AWS的人都已經(jīng)看到了這種效果,通常會(huì)產(chǎn)生令人困惑的效果。
為Spring @PreAuthorize啟用方法級(jí)安全性
您現(xiàn)在想要做的是配置Spring Boot以允許home端點(diǎn)上的請(qǐng)求,同時(shí)將請(qǐng)求限制為/restricted端點(diǎn)。
您最初可能以為可以將@PreAuthorize("permitAll()")到本地端點(diǎn),這將允許所有請(qǐng)求。 但是,如果嘗試使用它,您會(huì)發(fā)現(xiàn)它什么也沒做。 這是因?yàn)槟J(rèn)的HttpBuilder實(shí)現(xiàn)仍處于活動(dòng)狀態(tài),并且因?yàn)樗窃赪eb請(qǐng)求過濾器鏈中進(jìn)行評(píng)估的,因此它具有優(yōu)先權(quán)。 您還必須顯式啟用方法級(jí)安全注釋,否則,它們將被忽略。
添加以下SecurityConfig類,該類將實(shí)現(xiàn)以上兩個(gè)目標(biāo)。
src/main/java/com/okta/preauthorize/application/SecurityConfig.java
package com.okta.preauthorize.application; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(final HttpSecurity http) throws Exception {} }@EnableGlobalMethodSecurity(prePostEnabled = true)注釋啟用了@PreAuthorize注釋。 可以使用@Configuration批注將其添加到任何類。 在這里,我將不做任何深入的介紹,但是您也可以啟用@Secured ,較舊的Spring Security注釋和JSR-250注釋。
configure(final HttpSecurity http)方法將覆蓋默認(rèn)的HttpBuilder配置。 因?yàn)樗强盏?#xff0c;所以它使應(yīng)用程序未經(jīng)授權(quán)或認(rèn)證。
使用./gradlew bootRun再次運(yùn)行該應(yīng)用程序,您會(huì)發(fā)現(xiàn)兩個(gè)端點(diǎn)現(xiàn)已完全打開。
實(shí)施全球安全政策
應(yīng)用程序通常必須選擇要圍繞其構(gòu)建安全性的全局安全策略:“默認(rèn)為允許”或“默認(rèn)為已驗(yàn)證”。 該應(yīng)用程序默認(rèn)情況下是否打開? 還是默認(rèn)情況下受限制? 我通常默認(rèn)為受限,并明確允許任何公共端點(diǎn)。 對(duì)于我所使用的專有Web應(yīng)用程序類型,這些類型通常不是公開的,或者具有相對(duì)較小的公開面Kong,因此該方案有意義。 但是,如果您要進(jìn)行的工作大部分是公開的,并且要有離散的訪問控制支持,例如帶有管理面板的網(wǎng)站,那么可能會(huì)采用更寬松的方案。 您將在此處查看兩者。
由于該應(yīng)用已經(jīng)開放,因此我將首先向您展示如何限制特定方法。 在那之后,您將研究幾種方法來實(shí)施更具全局限制的訪問策略。
在WebController類中,將@PreAuthorize批注添加到/restricted端點(diǎn),如下所示:
@PreAuthorize("isAuthenticated()") @RequestMapping("/restricted") @ResponseBody public String restricted() { return "You found the secret lair!"; }運(yùn)行應(yīng)用程序( ./gradlew bootRun )。
這次,您將能夠?qū)Ш降街黜?#xff0c;但是轉(zhuǎn)到/restricted端點(diǎn)將為您提供(非常丑陋的)whitelabel錯(cuò)誤頁面。
在生產(chǎn)應(yīng)用程序中,您需要重寫此代碼以返回更好的自定義錯(cuò)誤頁面,或者以其他方式處理錯(cuò)誤(如果您要?jiǎng)?chuàng)建自定義錯(cuò)誤頁面,請(qǐng)查看主題的Spring文檔 )。 但是您可以看到該應(yīng)用返回的是403 /未經(jīng)授權(quán) ,這就是您想要的。
大。 現(xiàn)在,改為更改您的WebController類以匹配以下內(nèi)容:
@Controller @PreAuthorize("isAuthenticated()") public class WebController { @PreAuthorize("permitAll()") @RequestMapping("/") @ResponseBody public String home() { return "Welcome home!"; } @RequestMapping("/restricted") @ResponseBody public String restricted() { return "You found the secret lair!"; } }在這里,您已使用@PreAuthorize批注將整個(gè)控制器類限制為經(jīng)過身份驗(yàn)證的用戶,并明確允許所有請(qǐng)求(無論身份驗(yàn)證狀態(tài)如何)到本地端點(diǎn)。
我知道我們一直將其稱為“方法級(jí)”安全性,但是實(shí)際上,也可以將這些@PreAuthorize批注添加到控制器類中,以為整個(gè)類設(shè)置默認(rèn)值。 這也是@PreAuthorize("permitAll()")派上用場(chǎng)的地方,因?yàn)樗梢愿采w類級(jí)別的注釋。
如果運(yùn)行該應(yīng)用程序( ./gradlew bootRun )并嘗試使用端點(diǎn),則會(huì)發(fā)現(xiàn)home端點(diǎn)處于打開狀態(tài),而/restricted端點(diǎn)處于關(guān)閉狀態(tài)。
請(qǐng)注意,如果添加了另一個(gè)單獨(dú)的Web控制器,則默認(rèn)情況下,其所有方法仍將處于打開狀態(tài),并且不需要身份驗(yàn)證。
第三種選擇(我最喜歡的是大多數(shù)中小型應(yīng)用程序)是使用HttpBuilder在默認(rèn)情況下要求對(duì)所有請(qǐng)求進(jìn)行身份驗(yàn)證并顯式允許任何公共端點(diǎn)。 這使您可以使用@PreAuthorize來基于用戶或角色或組來優(yōu)化對(duì)特定方法的訪問控制,但要明確指出, 除非明確允許 , 否則所有路徑都將應(yīng)用一些基本安全性。 這也意味著公共路徑定義在中心位置。 同樣,這適用于某種類型的項(xiàng)目,但可能不是所有項(xiàng)目的最佳結(jié)構(gòu)。
要實(shí)現(xiàn)此目的,請(qǐng)將WebController類更改為此(刪除所有@PreAuthorize批注):
@Controller public class WebController { @RequestMapping("/") @ResponseBody public String home() { return "Welcome home!"; } @RequestMapping("/restricted") @ResponseBody public String restricted() { return "You found the secret lair!"; } }并將WebSecurity類更改為:
Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(final HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated().and().formLogin(); } }您要做的是使用SecurityConfig類顯式允許本地終結(jié)點(diǎn)計(jì)算機(jī)上的所有請(qǐng)求,同時(shí)要求在所有其他終結(jié)點(diǎn)計(jì)算機(jī)上進(jìn)行身份驗(yàn)證。 這為您的應(yīng)用設(shè)置了全面的最低身份驗(yàn)證要求。 您還重新啟用了基于表單的身份驗(yàn)證。
試試吧!
使用以下./gradlew bootRun運(yùn)行應(yīng)用程序: ./gradlew bootRun 。
導(dǎo)航到Home端點(diǎn),該端點(diǎn)已打開: http://localhost:8080 。
以及需要認(rèn)證的受限端點(diǎn): http://localhost:8080/restricted 。
當(dāng)Spring的登錄表單出現(xiàn)時(shí),別忘了您可以使用默認(rèn)憑據(jù)。 用戶是“用戶”,并且在控制臺(tái)輸出中找到了密碼(查找Using generated security password: 。
前進(jìn)到OAuth 2.0登錄
這些天,基于表單的身份驗(yàn)證感覺非常破舊。 用戶越來越希望能夠使用第三方站點(diǎn)登錄,由于安全威脅的增加,在您自己的服務(wù)器上管理用戶憑據(jù)的動(dòng)機(jī)也越來越少。 Okta是一家提供服務(wù)的軟件即服務(wù)身份和訪問管理公司。 在本部分中,您將使用它們通過OAuth 2.0和OIDC(OpenID Connect)快速實(shí)現(xiàn)登錄表單。
非常非常簡(jiǎn)短:OAuth 2.0是一種行業(yè)標(biāo)準(zhǔn)的授權(quán)協(xié)議,而OIDC是OAuth之上的另一個(gè)開放標(biāo)準(zhǔn),它添加了一個(gè)身份層(身份驗(yàn)證)。 它們共同為程序提供了一種結(jié)構(gòu)化的方式來管理身份驗(yàn)證和授權(quán),以及跨網(wǎng)絡(luò)和Internet進(jìn)行通信。 但是,OAuth和OIDC均未提供實(shí)現(xiàn)。 它們只是規(guī)格或協(xié)議。 這就是Okta的用處。Okta實(shí)現(xiàn)了OAuth 2.0和OIDC規(guī)范的實(shí)現(xiàn),該規(guī)范允許程序使用其服務(wù)來快速提供登錄,注冊(cè)和單點(diǎn)登錄(或社交登錄)服務(wù)。 在本教程中,您將僅實(shí)現(xiàn)登錄功能,但是在本教程的最后,您可以找到指向其他資源的鏈接,以向您展示如何實(shí)現(xiàn)社交登錄和注冊(cè)。
首先,注冊(cè)一個(gè)免費(fèi)的Okta Developer帳戶: https : //developer.okta.com/signup/ 。
如果這是您首次登錄或剛剛注冊(cè),則可能需要單擊Admin按鈕才能進(jìn)入開發(fā)人員控制臺(tái)。
接下來,您需要配置OIDC應(yīng)用程序。
在Okta開發(fā)人員儀表板的頂部菜單中,單擊應(yīng)用程序 。
- 單擊綠色的“ 添加應(yīng)用程序”按鈕
- 單擊“ Web應(yīng)用程序類型”,然后單擊“下一步”。
- 為應(yīng)用命名。 任何名字。
- 將登錄重定向URI設(shè)置為http://localhost:8080/login/oauth2/code/okta
- 單擊完成 。
記下頁面底部的客戶端ID和客戶端密鑰 。 在下一部分中,您將需要這些。
在Okta方面就是這樣。
現(xiàn)在,您需要配置Spring Boot以將Okta用作OAuth 2.0提供程序。
為OAuth 2.0配置Spring Boot應(yīng)用
使用OAuth 2.0和Okta使Spring Boot變得非常簡(jiǎn)單。 第一步是添加Okta Spring Boot Starter依賴項(xiàng)。 無需使用我們的啟動(dòng)程序就可以完全使用Okta OAuth 2.0 / OIDC; 但是,啟動(dòng)器簡(jiǎn)化了配置。 它還處理從JSON Web令牌中提取組聲明,并將其轉(zhuǎn)變?yōu)镾pring Security授權(quán)(稍后將介紹)。 查看Okta Spring Boot Starter GitHub頁面以獲取更多信息。
更新build.gradle文件的依賴項(xiàng)部分:
dependencies { implementation 'com.okta.spring:okta-spring-boot-starter:1.2.1' // <-- ADDEDimplementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' }在src/main/resources目錄中,有一個(gè)application.properties文件。 將其重命名為application.yml 。 添加以下內(nèi)容:
okta:oauth2: issuer: https://{yourOktaDomain}/oauth2/default client-id: {yourClientId}client-secret: {yourClientSecret}不要忘記更新client-id , client-secret和issuer值以匹配Okta開發(fā)人員帳戶和OIDC應(yīng)用程序中的值。 Okta發(fā)行者的外觀應(yīng)類似于https://dev-123456.okta.com/oauth2/default 。
最后,更新SecurityConfiguration.java文件:
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(final HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated() .and().oauth2Login(); // <-- THIS WAS CHANGED} }請(qǐng)注意,您在這里真正進(jìn)行的更改只是從formLogin()到oauth2Login() 。
運(yùn)行應(yīng)用程序: ./gradlew bootRun (您可能需要退出Okta開發(fā)人員儀表板,或者使用隱身窗口查看登錄屏幕)。
/端點(diǎn)仍然打開,但是當(dāng)您訪問受限端點(diǎn)時(shí): http://localhost:8080/restricted 。
您將看到Okta登錄屏幕。
使用您的Okta憑據(jù)登錄,即可通過身份驗(yàn)證!
檢查OAuth 2.0用戶屬性
開發(fā)OAuth應(yīng)用程序時(shí),我發(fā)現(xiàn)能夠檢查Spring Boot關(guān)于客戶端和已認(rèn)證用戶的信息會(huì)很有幫助。 為此,添加一個(gè)名為UserInfoController.java的新控制器。
src/main/java/com/okta/preauthorize/application/UserInfoController.java
package com.okta.preauthorize.application; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Map; @RequestMapping("/user") @Controller public class UserInfoController { @RequestMapping("/oauthinfo") @ResponseBody public String oauthUserInfo(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient, @AuthenticationPrincipal OAuth2User oauth2User) { return "User Name: " + oauth2User.getName() + "<br/>" + "User Authorities: " + oauth2User.getAuthorities() + "<br/>" + "Client Name: " + authorizedClient.getClientRegistration().getClientName() + "<br/>" + this.prettyPrintAttributes(oauth2User.getAttributes()); } private String prettyPrintAttributes(Map<String, Object> attributes) { String acc = "User Attributes: <br/><div style='padding-left:20px'>"; for (String key : attributes.keySet()){ Object value = attributes.get(key); acc += "<div>"+key + ": " + value.toString() + "</div>"; } return acc + "</div>"; } }請(qǐng)注意,此類定義了全類請(qǐng)求映射路徑/user ,從而形成了實(shí)際的oauthUserInfo()端點(diǎn)/user/oauthinfo 。
第二種方法prettyPrintAttributes()只是一些格式化用戶屬性的糖,使它們更具可讀性。
運(yùn)行應(yīng)用程序: ./gradlew bootRun 。
導(dǎo)航到http://localhost:8080/user/oauthinfo 。
您會(huì)看到以下內(nèi)容:
User Name: 00ab834zk7eJ18e8Y0h7 User Authorities: [ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile] Client Name: Okta User Attributes: at_hash: 1yq0lbHDupcb8AhBNShkeQsub: 00ue9mlzk7eW24e8Y0h7zoneinfo: America/Los_Angelesver: 1email_verified: trueamr: ["pwd"]iss: https://dev-123456.oktapreview.com/oauth2/defaultpreferred_username: andrew.hughes@mail.comlocale: en-USgiven_name: Andrewaud: [0oakz4teswoV7sDZI0h7]updated_at: 1558380884idp: 00oe9mlzh0xuqOT5z0h7auth_time: 1558454889name: Andrew Hughesexp: 2019-05-21T17:46:28Zfamily_name: Hughesiat: 2019-05-21T16:46:28Zemail: andrew.hughes@mail.comjti: ID.CnwVJ_h1Dq5unqkwherWyf8ZFTETX_X4TP39ythQ-ZE我想指出幾件事。 首先,請(qǐng)注意, 用戶名實(shí)際上是Okta的OIDC應(yīng)用程序的客戶端ID。 這是因?yàn)?#xff0c;從Spring Boot OAuth的角度來看,客戶端應(yīng)用程序是“用戶”。 要找到實(shí)際的用戶名和信息,您必須在User Attributes下查看 。 請(qǐng)記住,這些屬性的實(shí)際內(nèi)容在OAuth提供程序之間會(huì)有所不同,因此,例如,如果您支持Okta,GitHub和Twitter,則需要檢查每個(gè)OAuth提供程序的這些屬性以查看它們返回的內(nèi)容。
另一個(gè)要點(diǎn)是用戶權(quán)限 。 如Spring在這里所使用的,授權(quán)機(jī)構(gòu)是授權(quán)信息的元術(shù)語。 它們只是字符串。 它們的值是從OAuth / OIDC信息中提取的。 客戶端應(yīng)用程序可以正確使用它們。 它們可以是角色,作用域,組等。就OAuth / OIDC而言,它們的用法基本上是任意的。
要查看其工作原理,在接下來的幾節(jié)中,您將在Okta中添加一個(gè)Admin組,將用戶分配給該組,并使用@PreAuthorize批注將方法限制為Admin組。
ROLE_USER :您會(huì)注意到,Spring已為所有經(jīng)過身份驗(yàn)證的用戶分配了ROLE_USER 。 這是自動(dòng)分配的默認(rèn)最低級(jí)別角色。
SCOPE_ :還應(yīng)注意,OAuth范圍已映射到Spring授權(quán),并且可以用于授權(quán),例如,在@PreAuthorize和@PostAuthorize批注中,您將在@PostAuthorize秒鐘后看到。
激活對(duì)Okta的團(tuán)體聲明
Okta默認(rèn)情況下不將組聲明包括在JSON Web令牌(JWT)中。 JWT是Okta用于將身份驗(yàn)證和授權(quán)信息傳達(dá)到客戶端應(yīng)用程序的工具。 在本文章末尾鏈接的其他一些博客文章中,可以進(jìn)行更深入的研究。
要將Okta配置為添加組聲明,請(qǐng)轉(zhuǎn)到Okta開發(fā)人員儀表板。
在頂部菜單中,轉(zhuǎn)到API并選擇授權(quán)服務(wù)器 。
選擇默認(rèn)授權(quán)服務(wù)器。
單擊“ 索賠”選項(xiàng)卡。
您將要?jiǎng)?chuàng)建兩個(gè)索賠映射。 您本身并不是在創(chuàng)建兩個(gè)聲明,而是指示Okta將組聲明添加到訪問令牌和ID令牌中。 您需要執(zhí)行此操作,因?yàn)楦鶕?jù)OAuth流的不同,可能會(huì)從任一組中提取組聲明。 在我們的案例中,對(duì)于OIDC流程而言,實(shí)際上實(shí)際上是ID令牌,但最好將它們同時(shí)添加到兩者中,以免日后沮喪。 資源服務(wù)器流要求這些組聲明在訪問令牌中。
首先,為令牌類型Access Token添加一個(gè)聲明映射。
點(diǎn)擊添加聲明 。
更新以下值(其他默認(rèn)值也可以):
- 名稱:團(tuán)體
- 包含在令牌類型中 :訪問令牌
- 值類型:組
- 過濾器:匹配正則表達(dá)式.*
其次,為令牌類型ID Token添加第二個(gè)索賠映射。
點(diǎn)擊添加聲明 。
更新以下值(除了令牌類型外,與上面的值相同):
- 名稱:團(tuán)體
- 包含在令牌類型中 :ID令牌
- 值類型:組
- 過濾器:匹配正則表達(dá)式.*
大! 因此,現(xiàn)在Okta會(huì)將其所有組映射到有關(guān)訪問令牌和ID令牌的groups聲明。
在Spring,這些團(tuán)體聲稱發(fā)生的事情不一定是顯而易見的,也不是自動(dòng)的。 Spring Boot啟動(dòng)程序的好處之一是,它會(huì)自動(dòng)從JWT中提取組聲明并將其映射到Spring授權(quán)機(jī)構(gòu)。 否則,您將需要實(shí)現(xiàn)自己的GrantedAuthoritiesExtractor 。
僅供參考:可以使用application.yml文件中的okta.oauth2.groupsClaim字段來配置組聲明的名稱。 它默認(rèn)為groups 。
使用組檢查用戶屬性
運(yùn)行應(yīng)用程序: ./gradlew bootRun 。
導(dǎo)航到http://localhost:8080/user/oauthinfo 。
您將看到類似以下內(nèi)容(為清楚起見,省略了許多多余的行):
User Name: 00ab834zk7eJ18e8Y0h7 User Authorities: [Everyone, ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile] Client Name: Okta User Attributes: ...groups: ["Everyone"]...請(qǐng)注意一個(gè)新的組用戶屬性(映射的組聲明)。 值Everyone是映射到Everyone的默認(rèn)組。 這被映射到用戶權(quán)限Everyone 。
這是基本思想。 添加管理員組后,下一步將更加令人興奮。
在Okta中創(chuàng)建一個(gè)管理員組
現(xiàn)在,您想在Okta上添加一個(gè)管理員組。 登錄到Okta開發(fā)人員儀表板。
在頂部菜單中,轉(zhuǎn)到“ 用戶”,然后選擇“ 組” 。
單擊添加組 。
在彈出窗口中:
- 將組命名為“ Admin”。
- 描述可以是您喜歡的任何內(nèi)容。
- 單擊添加組 。
至此,您已經(jīng)創(chuàng)建了Admin組, 但實(shí)際上尚未分配任何人!
使用方法級(jí)授權(quán)來限制端點(diǎn)
在WebController類中,更新/restricted終結(jié)點(diǎn)方法。 您將在方法中添加以下注釋:
@PreAuthorize("hasAuthority('Admin')")像這樣:
@PreAuthorize("hasAuthority('Admin')") @RequestMapping("/restricted") @ResponseBody public String restricted() { return "You found the secret lair!"; }這告訴Spring檢查經(jīng)過身份驗(yàn)證的用戶是否具有Admin權(quán)限,如果沒有,則拒絕該請(qǐng)求。
運(yùn)行應(yīng)用程序: ./gradlew bootRun 。
導(dǎo)航到http://localhost:8080/restricted 。
您將收到403 /未經(jīng)授權(quán)的白頁錯(cuò)誤。
將您的用戶添加到管理員組
現(xiàn)在,您需要將Okta用戶添加到Admin組。 從頂部菜單中,選擇“ 用戶” ,然后單擊“ 組” 。 單擊管理員組。 單擊添加成員 。 在彈出窗口中搜索您的用戶,然后單擊添加 。
測(cè)試管理員組成員身份
做完了! 讓我們看看這樣做了。 再次運(yùn)行Spring Boot應(yīng)用程序: ./gradlew bootRun 。
導(dǎo)航到http://localhost/user/oauthinfo 。
這次,您將看到Admin組和權(quán)限。 (您可能需要一個(gè)新的瀏覽器會(huì)話才能看到更改,或使用隱身模式。)
User Name: 00ab834zk7eJ18e8Y0h7 User Authorities: [Admin, Everyone, ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile] Client Name: Okta User Attributes: ...groups: ["Everyone", "Admin"]...并且,如果您導(dǎo)航到http://localhost:8080/restricted ,則將被允許訪問。
比較Spring Security角色和權(quán)限
最初讓我感到困惑的一件事是hasRole()與hasAuthority() 。
Spring中的角色是具有ROLE_前綴的授權(quán)機(jī)構(gòu)(就像Spring中的所有其他事物一樣,該前綴是可配置的)。 考慮這種情況的一種方法是,角色應(yīng)用于大量權(quán)限,而權(quán)限可以用于更細(xì)粒度的控制。 但是,這只是一種可能的用法。 實(shí)際的實(shí)現(xiàn)取決于開發(fā)人員。 在本教程中,您實(shí)際上是在使用權(quán)限映射到授權(quán)組。
要記住的重要一點(diǎn)是,如果要使用hasRole() ,則需要聲明中的授權(quán)名稱以ROLE_ 。 例如,如果您添加了一個(gè)ROLE_ADMIN組,并將您的用戶添加到該組,并將該組添加到OIDC應(yīng)用程序,則可以使用hasRole('ADMIN') 。
基于OAuth 2.0范圍的授權(quán)和Spring PreAuthorize
您還可以使用@PreAuthorize批注來限制基于OAuth范圍的訪問。 從OAuth 2.0范圍文檔中 :
范圍是OAuth 2.0中的一種機(jī)制,用于限制應(yīng)用程序?qū)τ脩魩舻脑L問。 應(yīng)用程序可以請(qǐng)求一個(gè)或多個(gè)范圍,然后在同意屏幕中將此信息呈現(xiàn)給用戶,并且頒發(fā)給該應(yīng)用程序的訪問令牌將限于所授予的范圍。
如果查看從/user/oauthinfo端點(diǎn)返回的經(jīng)過檢查的User Authorities ,則會(huì)看到三個(gè)以SCOPE_開頭的SCOPE_ :
- SCOPE_email
- SCOPE_openid
- SCOPE_profile
這些對(duì)應(yīng)于電子郵件,openid和配置文件范圍。 要將方法限制為具有特定范圍的用戶,應(yīng)使用諸如: @PreAuthorize("hasAuthority('SCOPE_email')")的注釋。
我還將指出,您可以使用SecurityConfig類中的HttpSecurity完成完全一樣的事情(或多或少完全一樣):
protected void configure(final HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/restricted").hasAuthority("SCOPE_custom") // <- LOOK AT ME! .anyRequest().authenticated() .and().oauth2Login(); }通過將scopes屬性添加到application.yml文件,可以自定義客戶端應(yīng)用程序從Okta授權(quán)服務(wù)器請(qǐng)求的scopes 。 例如,在下面,我將application.yml文件設(shè)置為僅請(qǐng)求openid范圍,這對(duì)于OAuth是必需的。
okta: oauth2: ... scopes: openid...如果在/user/oauthinfo端點(diǎn)上運(yùn)行此請(qǐng)求,則會(huì)得到以下信息:
User Name: 00ab834zk7eJ18e8Y0h7 User Authorities: [Admin, Everyone, ROLE_USER, SCOPE_openid] Client Name: Okta ...請(qǐng)注意,只有一個(gè)范圍已分配給用戶權(quán)限。
嘗試添加自定義范圍。 更改application.yml文件中的okta.oauth2.scopes屬性以使其匹配:
okta: oauth2: ... scopes: openid email profile custom...在運(yùn)行該應(yīng)用并嘗試之前,您需要將自定義范圍添加到Okta授權(quán)服務(wù)器(如果現(xiàn)在運(yùn)行它,將會(huì)出現(xiàn)錯(cuò)誤)。
打開您的Okta開發(fā)人員儀表板。
在頂部菜單中,轉(zhuǎn)到API并選擇授權(quán)服務(wù)器 。
選擇默認(rèn)授權(quán)服務(wù)器。
單擊“ 作用域”選項(xiàng)卡。
單擊添加范圍按鈕。
- 名稱 : custom
- 描述 : Custom test scope
點(diǎn)擊創(chuàng)建 。
您剛剛向默認(rèn)的Okta授權(quán)服務(wù)器添加了一個(gè)自定義范圍(俗稱為custom )。
最后一次,運(yùn)行Spring Boot應(yīng)用程序: ./gradlew bootRun 。
導(dǎo)航到http://localhost:8080/user/oauthinfo 。
User Name: 00ab834zk7eJ18e8Y0h7 User Authorities: [Admin, Everyone, ROLE_USER, SCOPE_custom, SCOPE_email, SCOPE_openid, SCOPE_profile] Client Name: Okta ...成功! 您應(yīng)該在用戶權(quán)限中看到新的SCOPE_custom 。
Spring Boot中的Spring PreAuthorize,HttpSecurity和Security
您覆蓋了一大堆土地! 您已經(jīng)使用@PreAuthorize很好地了解了Spring方法級(jí)別的安全性,并了解了它與HttpSecurity 。 您使用了一些基本的SpEL(Spring表達(dá)式語言)語句來配置授權(quán)。 您檢查了授權(quán)和身份驗(yàn)證之間的區(qū)別。 您將Spring Boot配置為使用Okta作為OAuth 2.0 / OIDC單一登錄提供程序,并向身份驗(yàn)證服務(wù)器和客戶端應(yīng)用添加了組聲明。 您甚至還創(chuàng)建了一個(gè)新的Admin組,并看到了如何使用映射到Spring授權(quán)的組聲明來限制訪問。 最后,您了解了OAuth 2.0范圍如何用于定義授權(quán)方案并在應(yīng)用程序中實(shí)現(xiàn)它們。
下一站:火箭科學(xué)!
如果您想查看這個(gè)完整的項(xiàng)目,可以在GithHub上找到該倉庫 。
如果您想了解有關(guān)Spring Boot,Spring Security或安全用戶管理的更多信息,請(qǐng)查看以下任何出色的教程:
- Spring Boot,OAuth 2.0和Okta入門
- 15分鐘內(nèi)將單一登錄添加到您的Spring Boot Web App
- 使用多重身份驗(yàn)證保護(hù)您的Spring Boot應(yīng)用程序安全
- 使用Spring Boot和GraphQL構(gòu)建安全的API
如果您想更深入地學(xué)習(xí),請(qǐng)查看Okta Spring Boot Starter GitHub Project 。
如果您對(duì)此帖子有任何疑問,請(qǐng)?jiān)谙旅嫣砑釉u(píng)論。 有關(guān)更多精彩內(nèi)容, 請(qǐng)?jiān)赥witter上關(guān)注@oktadev , 在Facebook上關(guān)注我們,或訂閱我們的YouTube頻道 。
“帶有PreAuthorize的Spring方法安全性”最初于2019年6月20日發(fā)布在Okta Developer博客上。
朋友不允許朋友寫用戶身份驗(yàn)證。 厭倦了管理自己的用戶? 立即嘗試Okta的API和Java SDK。 數(shù)分鐘之內(nèi)即可在任何應(yīng)用程序中對(duì)用戶進(jìn)行身份驗(yàn)證,管理和保護(hù)。
翻譯自: https://www.javacodegeeks.com/2019/09/spring-method-security-preauthorize.html
spring安全性
總結(jié)
以上是生活随笔為你收集整理的spring安全性_具有PreAuthorize的Spring方法安全性的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不二臣是什么意思 不二臣的意思
- 下一篇: 苹果手机人像模式怎么调距离