javascript
具有PreAuthorize的Spring方法安全性
朋友不允許朋友寫用戶身份驗證。 厭倦了管理自己的用戶? 立即嘗試Okta的API和Java SDK。 在幾分鐘之內即可對任何應用程序中的用戶進行身份驗證,管理和保護。
本教程將探討使用Spring Security在Spring Boot中配置身份驗證和授權的兩種方法。 一種方法是創建WebSecurityConfigurerAdapter并使用流暢的API覆蓋HttpSecurity對象上的默認設置。 另一個方法是在控制器方法上使用@PreAuthorize批注,稱為方法級安全性或基于表達式的安全性。 后者將是本教程的重點。 但是,我將通過對比介紹一些HttpSecurity代碼和想法。
第一種身份驗證方法是HttpSecurity ,它是全局的,默認情況下應用于所有請求。 但是,可以通過對端點使用模式匹配來進行更細粒度的控制,并且HttpSecurity公開的流暢API十分強大。 在此處公開了OAuth 2.0,表單登錄和HTTP Basic等配置選項。 這是設置全局身份驗證策略的好地方。
方法級別的安全性是通過將@PreAuthorize批注放置在控制器方法上(實際上是一組可用的批注之一,但最常用)來實現。 該批注包含一個Spring Expression Language(SpEL)代碼段,該代碼段經過評估以確定是否應該對請求進行身份驗證。 如果未授予訪問權限,則不會執行該方法,并返回HTTP Unauthorized。 實際上,在控制器方法上使用@PreAuthorize注釋與在特定端點上使用HttpSecurity模式匹配器非常相似。 但是有一些區別。
區分Spring Security的@PreAuthorize和HttpSecurity
第一個區別是微妙的,但值得一提。 在發生控制器映射之前, HttpSecurity方法會在Web請求過濾器中更早地拒絕該請求。 相反, @PreAuthorize評估是在控制器方法執行之前@PreAuthorize進行的。 這意味著在HttpSecurity 之前應用 @PreAuthorize中的配置。
其次, HttpSecurity綁定到URL端點,而@PreAuthorize綁定到控制器方法,并且實際上位于與控制器定義相鄰的代碼內 。 將所有安全性集中在一處并由Web終結點定義具有一定的整潔性,尤其是在較小的項目中,或者在更全局的設置中; 但是,隨著項目的擴大,使授權策略靠近受保護的代碼可能更有意義,這就是基于注釋的方法所允許的。
另一個優點是@PreAuthorize呈現在HttpSecurity就是利用規劃環境地政司的。 使用Spring Expression Language,您可以基于復雜的表達式來做出授權決策,這些復雜的表達式可以訪問內置的身份驗證對象(例如authentication和principal ),依賴項注入的方法參數和查詢參數。 在本教程中,您將主要看兩個表達式: hasAuthority()和hasRole() 。 Spring文檔再次是深入研究的好地方。
在深入研究該項目之前,我還要提到Spring還提供了@PostAuthorize批注。 毫不奇怪,這是在方法執行后評估的方法級授權注釋。 我們為什么要這樣做? 它允許方法在評估注釋之前根據喜歡的控制器邏輯執行自己的授權檢查。 缺點是,由于控制器方法是在評估注釋之前執行的,因此可能會導致效率低下,具體取決于實現方式。
依存關系
本教程的依賴項非常簡單。 您需要:1)已安裝Java 8+,以及2)Okta開發人員帳戶。
如果沒有安裝Java,請轉到AdoptOpenJDK 。 在* nix系統上,您也可以使用SDKMAN 。
如果您還沒有免費的Okta開發者帳戶,請訪問我們的網站并注冊 。
使用Spring Initializr啟動一個示例項目
要啟動項目,可以使用Spring Initializr 。 但是,盡管值得一游,但您甚至不必費心去那里創建項目。 您可以使用REST API和curl 。
打開終端,然后將cd轉到您希望項目文件.zip結束的任何位置。 運行以下命令,該命令將下載壓縮的Spring Boot項目。
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類文件之外,該項目沒有多少其他內容。 但是,已經為您設置了整個項目結構。
對于本示例, build.gradle文件還具有兩個Spring依賴項:
dependencies {implementation 'org.springframework.boot:spring-boot-starter-security'implementation 'org.springframework.boot:spring-boot-starter-web' }添加一個WebController
當前狀態下的示例應用程序并沒有做太多事情。 您需要添加一個控制器來定義一些端點和響應。
添加一個新文件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!"; } }該控制器定義兩個端點: /和/restricted 。 您將稍后向/restricted端點添加方法級安全性。 但是,目前尚未配置安全性。
繼續運行該應用程序。 在根項目目錄中,運行:
./gradlew bootRunSpring Boot完成啟動后,導航至http://localhost:8080 。
您會注意到出現了一個登錄表單。 哇! 那個是從哪里來的?!
該表單是由Spring Boot自動生成的。 看一下Spring類WebSecurityConfigurerAdapter和方法configure(HttpSecurity http) 。 這是配置默認身份驗證設置的位置。
/** * 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(); }默認情況下,所有請求都需要身份驗證。 為所有請求啟用基于表單的身份驗證和HTTP Basic身份驗證。
如果要登錄,請在控制臺輸出中查找類似于以下內容的行,以告訴您所生成的密碼:
Using generated security password: 9c299bb9-f561-4c12-9810-c9a2bc1dca08用戶名就是“用戶”。 每次運行Spring Boot時都會重新生成此密碼。
身份驗證與授權
在繼續之前,我想快速確保兩個術語明確:身份驗證和授權。 身份驗證回答問題:誰在發出請求? 授權回答了這個問題:他們被允許做什么?
通常,首先進行身份驗證,除非為匿名用戶設置了特定的權限(在某些方面這是隱式身份驗證)。 授權基于經過身份驗證的用戶的價值。 認證實體可以是人類用戶或自動化服務,也可以是代表人類用戶運行的服務。
兩種常見的授權方案基于組和角色。 這兩個術語通常在網絡上不太知名的地方被混用并互換使用,但是在官方上有區別。 組定義用戶組,并向這些用戶組分配權限。 用戶可以是多個組的成員。 角色定義可以分配給用戶的權限集(允許的操作或資源)。 在實踐中,組往往是一種更靜態,更不靈活的控制器訪問方式,而角色通常是精細粒度的,甚至在會話中也可以是動態的,為特定任務分配角色,并在不再需要它們時將其撤銷。 使用Amazon AWS的任何人都已經看到了這種做法,通常會產生令人困惑的效果。
為Spring @PreAuthorize啟用方法級安全性
您現在想要做的是配置Spring Boot以允許home端點上的請求,同時將請求限制為/restricted端點。
您最初可能以為可以將@PreAuthorize("permitAll()")到本地端點,這將允許所有請求。 但是,如果嘗試使用它,您會發現它什么也沒做。 這是因為默認的HttpBuilder實現仍處于活動狀態,并且因為它是在Web請求過濾器鏈中進行評估的,因此它具有優先權。 您還必須顯式啟用方法級別的安全注釋,否則,它們將被忽略。
添加以下SecurityConfig類,將實現上述兩個目標。
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)方法將覆蓋默認的HttpBuilder配置。 因為它是空的,所以它使應用程序未經授權或認證。
使用./gradlew bootRun再次運行該應用程序,您會發現兩個端點都已完全打開。
實施全球安全政策
應用程序通常必須選擇要圍繞其構建安全性的全局安全策略:“默認為允許”或“默認為已驗證”。 該應用程序默認情況下是否打開? 還是默認情況下受限制? 我通常默認為受限,并明確允許任何公共端點。 對于我所使用的專有Web應用程序類型,這些類型通常不是公開的,或者具有相對較小的公開面Kong,因此該方案有意義。 但是,如果您要進行的工作大部分是公開的,并且要有離散的訪問控制支持,例如帶有管理面板的網站,那么可能會采用一種更為寬松的方案。 您將在此處查看兩者。
由于該應用已經開放,因此我將首先向您展示如何限制特定方法。 在那之后,您將研究幾種方法來實施更具全局限制的訪問策略。
在WebController類中,將@PreAuthorize批注添加到/restricted端點,如下所示:
@PreAuthorize("isAuthenticated()") @RequestMapping("/restricted") @ResponseBody public String restricted() { return "You found the secret lair!"; }運行應用程序( ./gradlew bootRun )。
這次,您將能夠導航到主頁,但是轉到/restricted端點將為您提供(非常難看的)whitelabel錯誤頁面。
在生產應用程序中,您需要重寫此代碼以返回更好的自定義錯誤頁面,或者以其他方式處理錯誤(如果要創建自定義錯誤頁面,請查看主題的Spring文檔 )。 但是您可以看到該應用返回的是403 /未經授權 ,這就是您想要的。
大。 現在,改為更改您的WebController類以匹配以下內容:
@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批注將整個控制器類限制為經過身份驗證的用戶,并明確允許所有請求(無論身份驗證狀態如何)到本地端點。
我知道我們一直將其稱為“方法級”安全性,但實際上,也可以將這些@PreAuthorize批注添加到控制器類中,以為整個類設置默認值。 這也是@PreAuthorize("permitAll()")派上用場的地方,因為它可以覆蓋類級別的注釋。
如果運行該應用程序( ./gradlew bootRun )并嘗試使用端點,則會發現home端點處于打開狀態,而/restricted端點處于關閉狀態。
請注意,如果添加了另一個單獨的Web控制器,則默認情況下,其所有方法仍將處于打開狀態,并且不需要身份驗證。
第三種選擇(我最喜歡的是大多數中小型應用程序)是使用HttpBuilder在默認情況下要求對所有請求進行身份驗證并顯式允許任何公共端點。 這使您可以使用@PreAuthorize來基于用戶或角色或組來優化對特定方法的訪問控制,但要明確指出, 除非明確允許 , 否則所有路徑都將應用一些基本安全性。 這也意味著公共路徑定義在中心位置。 同樣,這適用于某種類型的項目,但可能不是所有項目的最佳結構。
要實現此目的,請將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類顯式允許本地終結點計算機上的所有請求,同時要求在所有其他終結點計算機上進行身份驗證。 這為您的應用設置了全面的最低身份驗證要求。 您還重新啟用了基于表單的身份驗證。
嘗試一下!
使用以下./gradlew bootRun運行應用程序: ./gradlew bootRun 。
導航到打開的home端點: http://localhost:8080 。
以及需要認證的受限端點: http://localhost:8080/restricted 。
當Spring的登錄表單出現時,別忘了您可以使用默認憑據。 用戶是“用戶”,并且在控制臺輸出中找到了密碼(查找Using generated security password: 。
前進到OAuth 2.0登錄
這些天,基于表單的身份驗證感覺非常破舊。 用戶越來越希望能夠使用第三方站點登錄,由于安全威脅的增加,在您自己的服務器上管理用戶憑據的動機也越來越少。 Okta是一家提供服務的軟件即服務身份和訪問管理公司。 在本部分中,您將使用它們來使用OAuth 2.0和OIDC(OpenID Connect)快速實現登錄表單。
非常非常簡短:OAuth 2.0是一種行業標準的授權協議,而OIDC是OAuth之上的另一個開放標準,它添加了一個身份層(身份驗證)。 它們共同為程序提供了一種結構化的方式來管理身份驗證和授權,以及跨網絡和Internet進行通信。 但是,OAuth和OIDC均未提供實現。 它們只是規格或協議。 這就是Okta的用處。Okta實現了OAuth 2.0和OIDC規范的實現,該規范允許程序使用其服務來快速提供登錄,注冊和單點登錄(或社交登錄)服務。 在本教程中,您將僅實現登錄功能,但是在本教程的最后,您可以找到指向其他資源的鏈接,以向您展示如何實現社交登錄和注冊。
首先,注冊一個免費的Okta Developer帳戶: https : //developer.okta.com/signup/ 。
如果這是您首次登錄或剛剛注冊,則可能需要單擊Admin按鈕才能進入開發人員控制臺。
接下來,您需要配置OIDC應用程序。
在Okta開發人員儀表板的頂部菜單中,單擊應用程序 。
- 單擊綠色的“ 添加應用程序”按鈕
- 單擊“ Web應用程序類型”,然后單擊“下一步”。
- 為應用命名。 任何名字。
- 將登錄重定向URI設置為http://localhost:8080/login/oauth2/code/okta
- 單擊完成 。
記下頁面底部的客戶端ID和客戶端密鑰 。 在下一部分中,您將需要這些。
在Okta方面就是這樣。
現在,您需要配置Spring Boot以將Okta用作OAuth 2.0提供程序。
為OAuth 2.0配置Spring Boot應用
使用OAuth 2.0和Okta使Spring Boot變得非常容易。 第一步是添加Okta Spring Boot Starter依賴項。 無需使用我們的啟動程序就可以完全使用Okta OAuth 2.0 / OIDC; 但是,啟動器簡化了配置。 它還處理從JSON Web令牌中提取組聲明,并將其轉換為Spring Security授權(稍后會介紹)。 查看Okta Spring Boot Starter GitHub頁面以獲取更多信息。
更新build.gradle文件的依賴項部分:
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目錄中,有一個application.properties文件。 將其重命名為application.yml 。 添加以下內容:
okta:oauth2: issuer: https://{yourOktaDomain}/oauth2/default client-id: {yourClientId}client-secret: {yourClientSecret}不要忘記更新client-id , client-secret和issuer值以匹配Okta開發人員帳戶和OIDC應用程序中的值。 Okta發行者的外觀應類似于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} }請注意,您在這里真正進行的更改只是從formLogin()到oauth2Login() 。
運行應用程序: ./gradlew bootRun (您可能需要退出Okta開發人員儀表板,或使用隱身窗口查看登錄屏幕)。
/端點仍然打開,但是當您訪問受限端點時: http://localhost:8080/restricted 。
您將看到Okta登錄屏幕。
使用您的Okta憑據登錄,即可通過身份驗證!
檢查OAuth 2.0用戶屬性
開發OAuth應用程序時,我發現能夠檢查Spring Boot關于客戶端和已認證用戶的信息會很有幫助。 為此,添加一個名為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>"; } }請注意,該類定義了全類的請求映射路徑/user ,從而形成了實際的oauthUserInfo()端點/user/oauthinfo 。
第二種方法prettyPrintAttributes()只是一些用來格式化用戶屬性的糖,使它們更具可讀性。
運行應用程序: ./gradlew bootRun 。
導航到http://localhost:8080/user/oauthinfo 。
您會看到以下內容:
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我想指出幾件事。 首先,請注意, 用戶名實際上是Okta的OIDC應用程序的客戶端ID。 這是因為,從Spring Boot OAuth的角度來看,客戶端應用程序是“用戶”。 要找到實際的用戶名和信息,您必須在User Attributes下查看 。 請記住,這些屬性的實際內容在OAuth提供程序之間會有所不同,因此,例如,如果您支持Okta,GitHub和Twitter,那么您將需要檢查每個OAuth提供程序的這些屬性以查看它們返回的內容。
另一個要點是用戶權限 。 如Spring在這里所使用的,授權機構是授權信息的元術語。 它們只是字符串。 它們的值是從OAuth / OIDC信息中提取的。 客戶端應用程序可以正確使用它們。 它們可能是角色,作用域,組等。就OAuth / OIDC而言,它們的用法基本上是任意的。
要查看其工作原理,在接下來的幾節中,您將在Okta中添加一個Admin組,將用戶分配給該組,然后使用@PreAuthorize批注將方法限制為Admin組。
ROLE_USER :您會注意到,Spring已為所有經過身份驗證的用戶分配了ROLE_USER 。 這是自動分配的默認最低級別角色。
SCOPE_ :還應注意,OAuth范圍已映射到Spring授權,并且可以用于授權,例如,在@PreAuthorize和@PostAuthorize批注中,正如您將在@PostAuthorize秒鐘后看到的那樣。
激活對Okta的團體聲明
Okta默認情況下不將組聲明包括在JSON Web令牌(JWT)中。 JWT是Okta用于將身份驗證和授權信息傳達到客戶端應用程序的工具。 在本文末尾鏈接的其他一些博客文章中,可以找到更深入的了解。
要將Okta配置為添加組聲明,請轉到Okta開發人員儀表板。
在頂部菜單中,轉到API并選擇授權服務器 。
選擇默認授權服務器。
單擊“ 索賠”選項卡。
您將創建兩個索賠映射。 您本身并不是在創建兩個聲明,而是指示Okta將組聲明添加到訪問令牌和ID令牌中。 您需要執行此操作,因為根據OAuth流的不同,可能會從任一組中提取組聲明。 在我們的案例中,對于OIDC流程而言,實際上實際上是ID令牌,但是最好將它們同時添加到兩者中,以免日后沮喪。 資源服務器流要求組聲明位于訪問令牌中。
首先,為令牌類型Access Token添加一個聲明映射。
點擊添加聲明 。
更新以下值(其他默認值也可以):
- 名稱:團體
- 包含在令牌類型中 :訪問令牌
- 值類型:組
- 過濾器:匹配正則表達式.*
其次,為令牌類型ID Token添加第二個聲明映射。
點擊添加聲明 。
更新以下值(除了令牌類型外,與上面的值相同):
- 名稱:團體
- 包含在令牌類型中 :ID令牌
- 值類型:組
- 過濾器:匹配正則表達式.*
大! 因此,現在Okta會將其所有組映射到有關訪問令牌和ID令牌的groups聲明。
在Spring,這些團體聲稱發生的事情不一定是顯而易見的,也不是自動的。 Spring Boot啟動程序的好處之一是,它會自動從JWT中提取組聲明并將其映射到Spring授權機構。 否則,您將需要實現自己的GrantedAuthoritiesExtractor 。
僅供參考:可以使用application.yml文件中的okta.oauth2.groupsClaim字段配置組聲明的名稱。 它默認為groups 。
使用組檢查用戶屬性
運行應用程序: ./gradlew bootRun 。
導航到http://localhost:8080/user/oauthinfo 。
您將看到類似這樣的內容(為清楚起見,省略了許多冗余行):
User Name: 00ab834zk7eJ18e8Y0h7 User Authorities: [Everyone, ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile] Client Name: Okta User Attributes: ...groups: ["Everyone"]...注意一個新的組用戶屬性(映射的組聲明)。 值Everyone是映射到Everyone的默認組。 這被映射到用戶權限Everyone 。
這是基本思想。 添加管理員組后,下一步將更加令人興奮。
在Okta中創建一個管理員組
現在,您想在Okta上添加一個管理員組。 登錄到Okta開發人員儀表板。
在頂部菜單中,轉到“ 用戶”,然后選擇“ 組” 。
單擊添加組 。
在彈出窗口中:
- 將組命名為“ Admin”。
- 描述可以是您喜歡的任何內容。
- 單擊添加組 。
至此,您已經創建了Admin組, 但實際上尚未分配任何人!
使用方法級授權來限制端點
在WebController類中,更新/restricted終結點方法。 您將在方法中添加以下注釋:
@PreAuthorize("hasAuthority('Admin')")像這樣:
@PreAuthorize("hasAuthority('Admin')") @RequestMapping("/restricted") @ResponseBody public String restricted() { return "You found the secret lair!"; }這告訴Spring檢查經過身份驗證的用戶是否具有Admin權限,如果沒有,則拒絕該請求。
運行應用程序: ./gradlew bootRun 。
導航到http://localhost:8080/restricted 。
您將收到403 /未經授權的白頁錯誤。
將您的用戶添加到管理員組
現在,您需要將Okta用戶添加到Admin組。 從頂部菜單中,選擇“ 用戶” ,然后單擊“ 組” 。 單擊管理員組。 單擊添加成員 。 在彈出窗口中搜索您的用戶,然后單擊添加 。
測試管理員組成員身份
做完了! 讓我們看看這樣做了。 再次運行Spring Boot應用程序: ./gradlew bootRun 。
導航到http://localhost/user/oauthinfo 。
這次,您將看到Admin組和權限。 (您可能需要一個新的瀏覽器會話才能看到更改,或使用隱身模式。)
User Name: 00ab834zk7eJ18e8Y0h7 User Authorities: [Admin, Everyone, ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile] Client Name: Okta User Attributes: ...groups: ["Everyone", "Admin"]...并且,如果您導航到http://localhost:8080/restricted ,則將被允許訪問。
比較Spring Security角色和權限
最初讓我感到困惑的一件事是hasRole()與hasAuthority() 。
Spring中的角色是具有ROLE_前綴的授權機構(就像Spring中的所有其他事物一樣,該前綴是可配置的)。 考慮這種情況的一種方法是,角色應用于大量權限,而權限可用于更精細的控制。 但是,這只是一種可能的用法。 實際的實現取決于開發人員。 在本教程中,您實際上是在使用權限映射到授權組。
要記住的重要一點是,如果要使用hasRole() ,則需要聲明中的授權名稱以ROLE_ 。 例如,如果您添加了一個ROLE_ADMIN組,并將您的用戶添加到該組,并將該組添加到OIDC應用程序,則可以使用hasRole('ADMIN') 。
基于OAuth 2.0范圍的授權和Spring PreAuthorize
您還可以使用@PreAuthorize批注來限制基于OAuth范圍的訪問。 從OAuth 2.0范圍文檔中 :
范圍是OAuth 2.0中的一種機制,用于限制應用程序對用戶帳戶的訪問。 一個應用程序可以請求一個或多個范圍,然后在同意屏幕中將此信息呈現給用戶,并且頒發給該應用程序的訪問令牌將限于所授予的范圍。
如果查看從/user/oauthinfo端點返回的經過檢查的User Authorities ,則會看到三個以SCOPE_開頭的SCOPE_ :
- SCOPE_email
- SCOPE_openid
- SCOPE_profile
這些對應于電子郵件,openid和配置文件范圍。 要將方法限制為具有特定范圍的用戶,應使用諸如: @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文件來定制客戶端應用程序從Okta授權服務器請求的scopes 。 例如,在下面,我將application.yml文件設置為僅請求openid范圍,這對于OAuth是必需的。
okta: oauth2: ... scopes: openid...如果在/user/oauthinfo端點上運行此請求,則會得到以下信息:
User Name: 00ab834zk7eJ18e8Y0h7 User Authorities: [Admin, Everyone, ROLE_USER, SCOPE_openid] Client Name: Okta ...請注意,只有一個范圍已分配給用戶權限。
嘗試添加自定義范圍。 更改application.yml文件中的okta.oauth2.scopes屬性以使其匹配:
okta: oauth2: ... scopes: openid email profile custom...在運行應用程序并嘗試之前,您需要將自定義范圍添加到Okta授權服務器(如果現在運行它,將會出現錯誤)。
打開您的Okta開發人員儀表板。
在頂部菜單中,轉到API并選擇授權服務器 。
選擇默認授權服務器。
單擊“ 作用域”選項卡。
單擊添加范圍按鈕。
- 名稱 : custom
- 描述 : Custom test scope
點擊創建 。
您剛剛向默認的Okta授權服務器添加了一個自定義范圍(俗稱為custom )。
最后一次,運行Spring Boot應用程序: ./gradlew bootRun 。
導航到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 ...成功! 您應該在用戶權限中看到新的SCOPE_custom 。
Spring Boot中的Spring PreAuthorize,HttpSecurity和Security
您覆蓋了一大堆土地! 您使用@PreAuthorize很好地了解了Spring方法級別的安全性,并了解了它與HttpSecurity 。 您使用了一些基本的SpEL(Spring表達式語言)語句來配置授權。 您檢查了授權和身份驗證之間的區別。 您將Spring Boot配置為使用Okta作為OAuth 2.0 / OIDC單一登錄提供程序,并向身份驗證服務器和客戶端應用添加了組聲明。 您甚至還創建了一個新的Admin組,并了解了如何使用映射到Spring授權的組聲明來限制訪問。 最后,您了解了OAuth 2.0范圍如何用于定義授權方案并在應用程序中實現它們。
下一站:火箭科學!
如果您想查看這個完整的項目,可以在GithHub上找到該倉庫 。
如果您想了解有關Spring Boot,Spring Security或安全用戶管理的更多信息,請查看以下任何出色的教程:
- Spring Boot,OAuth 2.0和Okta入門
- 15分鐘內將單一登錄添加到您的Spring Boot Web App
- 使用多重身份驗證保護您的Spring Boot應用程序安全
- 使用Spring Boot和GraphQL構建安全的API
如果您想更深入地學習,請查看Okta Spring Boot Starter GitHub Project 。
如果您對此帖子有任何疑問,請在下面添加評論。 有關更多精彩內容, 請在Twitter上關注@oktadev , 在Facebook上關注我們,或訂閱我們的YouTube頻道 。
“帶有PreAuthorize的Spring方法安全性”最初于2019年6月20日發布在Okta Developer博客上。
朋友不允許朋友寫用戶身份驗證。 厭倦了管理自己的用戶? 立即嘗試Okta的API和Java SDK。 在幾分鐘之內即可對任何應用程序中的用戶進行身份驗證,管理和保護。
翻譯自: https://www.javacodegeeks.com/2019/09/spring-method-security-preauthorize.html
總結
以上是生活随笔為你收集整理的具有PreAuthorize的Spring方法安全性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: q7goodies事例_Java 8 F
- 下一篇: 跨级报考备案(备案跨级)