# Spring Boot和OAuth2
本指南將向您展示如何使用 通過“社交登錄”構建示例應用程序來完成各種任務 [OAuth 2.0](https://tools.ietf.org/html/rfc6749) 和 [Spring Boot](https://projects.spring.io/spring-boot/) 。
它從簡單的單一提供商單點登錄開始,并與選擇了身份驗證提供程序的客戶端一起使用: [GitHub](https://github.com/settings/developers) 或 [Google](https://developers.google.com/identity/protocols/OpenIDConnect) 。
這些示例都是在后端使用Spring Boot和Spring Security的所有單頁應用程序。 他們也都 使用普通的 [jQuery](https://jquery.org/) 在前端 。 但是,轉換為其他JavaScript框架或使用服務器端呈現所需的更改將很小。
所有示例都是使用 的本機OAuth 2.0支持實現的 [Spring Boot中](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-security-oauth2) 。
相互之間建立了多個示例,并在每個步驟中添加了新功能:
* [**簡單**](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_simple) :一個非常基本的靜態應用程序,只有一個主頁,并通過Spring Boot的OAuth 2.0配置屬性無條件登錄(如果您訪問該主頁,您將自動重定向到GitHub)。
* [**click**](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_click) :添加一個顯式鏈接,用戶必須單擊該鏈接才能登錄。
* [**注銷**](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_logout) :以及為經過身份驗證的用戶添加注銷鏈接。
* [**兩個提供商**](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_two_providers) :添加第二個登錄提供程序,以便用戶可以在主頁上選擇要使用的登錄提供程序。
* [**custom-error**](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_custom_error) :為未經身份驗證的用戶添加錯誤消息,以及基于GitHub API的自定義身份驗證。
在功能階梯中從一??個應用遷移到下一個應用所需的更改可以在 進行跟蹤 源代碼中 。 該應用程序的每個版本都有其自己的目錄,以便您可以比較它們之間的差異。
每個應用程序都可以導入到IDE中。 您可以運行 `main` 方法中 `SocialApplication`啟動一個應用程序。 他們都提供了位于 的主頁 [http:// localhost:8080](http://localhost:8080) (如果要登錄并查看內容,都要求您至少具有GitHub和Google帳戶)。
您還可以使用以下命令在命令行上運行所有應用程序 `mvn spring-boot:run` 或通過構建jar文件并使用 `mvn package` 和 `java -jar target/*.jar`(根據 [Spring Boot文檔](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-first-application-run) 和其他 [可用文檔](https://spring.io/guides/gs/spring-boot/) )。 如果 使用 無需安裝Maven [包裝器](https://github.com/takari/maven-wrapper) 在頂層 ,則 ,例如
~~~
$ cd simple
$ ../mvnw package
$ java -jar target/*.jar
~~~
所有的應用程序都可以運行 localhost:8080因為他們將使用在GitHub和Google注冊的OAuth 2.0客戶端作為該地址。 要在其他主機或端口上運行它們,您需要以這種方式注冊您的應用程序。 如果使用默認值,則不會將憑據泄漏到本地主機以外的地方。 但是,請注意您在Internet上公開的內容,并且不要將自己的應用程序注冊置于公共源代碼控制中。
## 單一登錄GitHub
在本部分中,您將創建一個使用GitHub進行身份驗證的最小應用程序。 通過利用Spring Boot中的自動配置功能,這將非常容易。
### 創建一個新項目
首先,您需要創建一個Spring Boot應用程序,可以通過多種方式來完成。 最簡單的方法是轉到 [https://start.spring.io](https://start.spring.io) 并生成一個空項目(選擇“ Web”依賴項作為起點)。 同樣,在命令行上執行此操作:
~~~
$ mkdir ui && cd ui
$ curl https://start.spring.io/starter.tgz -d style=web -d name=simple | tar -xzvf -
~~~
然后,您可以將該項目導入到您喜歡的IDE(默認情況下是普通的Maven Java項目),也可以僅處理文件和 `mvn` 在命令行上。
### 添加主頁
在您的新項目中,創建 `index.html` 在里面 `src/main/resources/static`文件夾。 您應該添加一些樣式表和JavaScript鏈接,以便結果如下所示:
index.html
~~~
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>Demo</title>
<meta name="description" content=""/>
<meta name="viewport" content="width=device-width"/>
<base href="/"/>
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css"/>
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<h1>Demo</h1>
<div class="container"></div>
</body>
</html>
~~~
不需要任何這些來演示OAuth 2.0登錄功能,但最終擁有一個令人愉悅的UI會很好,因此您不妨從首頁中的一些基本內容開始。
如果啟動應用程序并加載主頁,則會注意到尚未加載樣式表。 因此,您還需要通過添加jQuery和Twitter Bootstrap來添加它們:
pom.xml
~~~
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
~~~
最終依賴項是webjars“定位器”,由webjars站點以庫的形式提供。 Spring可以使用定位器在webjar中定位靜態資產,而無需知道確切的版本(因此無版本) `/webjars/**` 中的鏈接 `index.html`)。 只要您不關閉MVC自動配置,默認情況下就會在Spring Boot應用程序中激活webjar定位器。
完成這些更改后,您應該為應用程序提供一個漂亮的主頁。
### 使用GitHub和Spring Security保護應用程序安全
為了使應用程序安全,您可以簡單地將Spring Security添加為依賴項。 由于您要進行“社交”登錄(委托給GitHub),因此應包括Spring Security OAuth 2.0 Client啟動程序:
pom.xml
~~~
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
~~~
通過添加它,默認情況下它將使用OAuth 2.0保護您的應用安全。
接下來,您需要配置您的應用程序以使用GitHub作為身份驗證提供程序。 為此,請執行以下操作:
* [添加一個新的GitHub應用](https://spring.io/guides/tutorials/spring-boot-oauth2/#github-register-application)
* [配置application.yml](https://spring.io/guides/tutorials/spring-boot-oauth2/#github-application-config)
* [啟動應用程序](https://spring.io/guides/tutorials/spring-boot-oauth2/#github-boot-application)
#### 添加一個新的GitHub App
要使用GitHub的OAuth 2.0身份驗證系統進行登錄,必須首先 [添加一個新的GitHub應用](https://github.com/settings/developers) 。
選擇“新OAuth應用”,然后顯示“注冊新OAuth應用”頁面。 輸入應用名稱和描述。 然后, ,輸入應用程序的主頁,該主頁應為 [http:// localhost:8080](http://localhost:8080) 在本例中 。 最后,將Authorization回調URL指示為 `[http://localhost:8080/login/oauth2/code/github](http://localhost:8080/login/oauth2/code/github)`并點擊 *注冊應用程序* 。
OAuth重定向URI是最終用戶的用戶代理在通過GitHub進行身份驗證并在“ 上授予對該應用程序的訪問權之后,將重定向到該應用程序中的路徑 *授權應用程序”* 頁面 。
默認重定向URI模板是 {baseUrl}/login/oauth2/code/{registrationId}。 該 registrationId 是用于唯一標識符 ClientRegistration.
#### 配置 `application.yml`
然后,要鏈接到GitHub,請將以下內容添加到您的 `application.yml`:
application.yml
~~~
spring:
security:
oauth2:
client:
registration:
github:
clientId: github-client-id
clientSecret: github-client-secret
# ...
~~~
只需使用您剛剛通過GitHub創建的OAuth 2.0憑據,即可替換 `github-client-id` 使用客戶端ID和 `github-client-secret` 與客戶的秘密。
##### 啟動應用程序
進行此更改后,您可以再次運行您的應用程序,并訪問位于 的主頁 [http:// localhost:8080](http://localhost:8080) 。 現在,應該將您而不是主頁重定向到使用GitHub登錄。 如果您這樣做,并接受要求您進行的任何授權,您將被重定向回本地應用程序,并且主頁將可見。
如果您仍然登錄到GitHub,則即使您在沒有Cookie和緩存數據的全新瀏覽器中打開它,也不必使用此本地應用程序重新進行身份驗證。 (這就是“單點登錄”的意思。)
如果您正在使用示例應用程序來完成本節,請確保清除瀏覽器的cookie和HTTP Basic憑據緩存。 對單個服務器執行此操作的最佳方法是打開一個新的私有窗口。
授予訪問此示例的權限是安全的,因為只有在本地運行的應用程序才能使用令牌,并且它要求的范圍是有限的。 但是,當您登錄到這樣的應用程序時,請注意您正在批準的內容:他們可能會要求您做比您滿意的事情(例如,他們可能會要求您更改個人數據的許可,這不太可能符合您的利益) )。
### 剛剛發生了什么?
您剛才用OAuth 2.0術語編寫的應用程序是一個 *Client Application* ,它使用 [授權代碼授予](https://tools.ietf.org/html/rfc6749#section-4) 從GitHub(授權服務器)獲得訪問令牌。
然后,它使用訪問令牌向GitHub詢問一些個人詳細信息(僅限您允許的操作),包括您的登錄ID和名稱。 在此階段,GitHub充當資源服務器,對您發送的令牌進行解碼,并檢查它是否授予應用訪問用戶詳細信息的權限。 如果該過程成功,則該應用程序會將用戶詳細信息插入到Spring Security上下文中,以便對您進行身份驗證。
如果您在瀏覽器工具(Chrome或Firefox上為F12)中查找并跟蹤所有躍點的網絡流量,則將看到與GitHub進行來回重定向,最后,您將在新頁面上回到首頁 `Set-Cookie`標頭。 這個Cookie( `JSESSIONID` 默認情況下)是用于Spring(或任何基于servlet的)應用程序的身份驗證詳細信息的令牌。
因此,我們擁有一個安全的應用程序,從某種意義上來說,它必須查看用戶必須向外部提供程序(GitHub)進行身份驗證的任何內容。
我們不想將其用于互聯網銀行網站。 但是,出于基本標識的目的,并且為了在站點的不同用戶之間隔離內容,這是一個很好的起點。 這就是為什么這種身份驗證在當今非常流行的原因。
在下一節中,我們將向應用程序添加一些基本功能。 對于用戶,當他們將初始重定向到GitHub時,發生了什么,我們也將使其更加明顯。
## 添加歡迎頁面
在本部分中,您將 修改 的 [簡單](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_simple) 通過添加顯式鏈接來登錄GitHub來 剛剛構建 應用程序。 新的鏈接將在主頁上顯示,而不是立即重定向,并且用戶可以選擇登錄或不進行身份驗證。 僅當用戶單擊鏈接時,才會呈現安全內容。
### 主頁上的條件內容
要在驗證用戶身份的情況下呈現內容,您可以選擇服務器端呈現或客戶端呈現。
在這里,您將使用 更改客戶端 [JQuery](https://jquery.org/) ,盡管如果您喜歡使用其他內容,則翻譯客戶端代碼應該不會很困難。
要開始使用動態內容,您需要標記幾個HTML元素,如下所示:
index.html
~~~
<div class="container unauthenticated">
With GitHub: <a href="/oauth2/authorization/github">click here</a>
</div>
<div class="container authenticated" style="display:none">
Logged in as: <span id="user"></span>
</div>
~~~
默認情況下,第一個 `<div>`將顯示,而第二個則不會。 注意空 `<span>` 帶著 `id` 屬性。
稍后,您將添加一個服務器端端點,該端點將以JSON返回已登錄的用戶詳細信息。
但是,首先,添加以下JavaScript,它將到達該端點。 根據端點的響應,此JavaScript將填充 `<span>` 標記用戶名,然后切換 `<div>` 適當地:
index.html
~~~
<script type="text/javascript">
$.get("/user", function(data) {
$("#user").html(data.name);
$(".unauthenticated").hide()
$(".authenticated").show()
});
</script>
~~~
請注意,此JavaScript期望調用服務器端終結點 `/user`.
### 這 `/user` 終點
現在,您將添加剛才提到的服務器端端點,將其調用 `/user`。 它將發回當前登錄的用戶,我們可以在我們的主類中很容易地做到這一點:
SocialApplication.java
~~~
@SpringBootApplication
@RestController
public class SocialApplication {
@GetMapping("/user")
public Map<String, Object> user(@AuthenticationPrincipal OAuth2User principal) {
return Collections.singletonMap("name", principal.getAttribute("name"));
}
public static void main(String[] args) {
SpringApplication.run(SocialApplication.class, args);
}
}
~~~
注意使用 `@RestController`, `@GetMapping`和 `OAuth2User` 注入處理程序方法。
歸還整筆錢不是一個好主意 OAuth2User 在端點中,因為它可能包含您不希望向瀏覽器客戶端顯示的信息。
### 公開主頁
您需要進行最后的更改。
該應用程序現在可以像以前一樣正常工作并進行身份驗證,但是在顯示頁面之前它仍將進行重定向。 為了使鏈接可見,我們還需要通過擴展擴展名來關閉主頁上的安全性。 `WebSecurityConfigurerAdapter`:
社會應用
~~~
@SpringBootApplication
@RestController
public class SocialApplication extends WebSecurityConfigurerAdapter {
// ...
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(a -> a
.antMatchers("/", "/error", "/webjars/**").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(e -> e
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
)
.oauth2Login();
// @formatter:on
}
}
~~~
Spring Boot賦予特殊含義 `WebSecurityConfigurerAdapter` 在帶注解的課程上 `@SpringBootApplication`:它使用它來配置承載OAuth 2.0身份驗證處理器的安全過濾器鏈。
上面的配置指示了允許端點的白名單,其他所有端點都需要進行身份驗證。
您要允許:
* `/` 因為那是您剛剛創建的頁面,因此未經身份驗證的用戶可以看到其中的某些內容
* `/error` 因為那是用于顯示錯誤的Spring Boot端點,并且
* `/webjars/**` 因為您希望您的JavaScript為所有經過身份驗證的訪問者運行
您不會看到任何有關 `/user`但是在這種配置下。 一切,包括 `/user` 除非另有說明,否則保持安全 `.anyRequest().authenticated()` 最后的配置。
最后,由于我們要通過Ajax與后端接口,因此我們希望配置端點以401響應,而不是重定向到登錄頁面的默認行為。 配置 `authenticationEntryPoint`為我們實現了這一目標。
完成這些更改后,該應用程序便完成了,如果您運行它并訪問主頁,您應該會看到一個漂亮的HTML鏈接,以“使用GitHub登錄”。 該鏈接不會直接將您帶到GitHub,而是將您帶到處理身份驗證的本地路徑(并將重定向發送到GitHub)。 通過身份驗證后,您將重定向回本地應用程序,該應用程序現在將顯示您的姓名(假設您已在GitHub中設置了權限以允許訪問該數據)。
## 添加注銷按鈕
在本部分中,我們 修改 的 [click](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_click) 通過添加允許用戶注銷該應用程序的按鈕來 我們構建 應用程序。 這似乎是一個簡單的功能,但要實現時需要多加注意,因此值得花一些時間來討論確切的操作方法。 大多數更改與以下事實有關:我們正在將應用程序從只讀資源轉換為可讀寫資源(注銷需要狀態更改),因此在任何實際的應用程序中都需要進行相同的更改。不只是靜態內容。
### 客戶端更改
在客戶端上,我們只需要提供一個注銷按鈕和一些JavaScript即可回調服務器以請求取消身份驗證。 首先,在用戶界面的“已驗證”部分中,添加按鈕:
index.html
~~~
<div class="container authenticated">
Logged in as: <span id="user"></span>
<div>
<button onClick="logout()" class="btn btn-primary">Logout</button>
</div>
</div>
~~~
然后我們提供 `logout()` 它在JavaScript中引用的函數:
index.html
~~~
var logout = function() {
$.post("/logout", function() {
$("#user").html('');
$(".unauthenticated").show();
$(".authenticated").hide();
})
return true;
}
~~~
這 `logout()` 函數執行POST到 `/logout`然后清除動態內容。 現在,我們可以切換到服務器端以實現該端點。
### 添加注銷端點
Spring Security內置了對 `/logout`終結點,它將為我們做正確的事情(清除會話并使cookie無效)。 要配置端點,我們只需擴展現有端點 `configure()` 我們的方法 `WebSecurityConfigurerAdapter`:
SocialApplication.java
~~~
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
// ... existing code here
.logout(l -> l
.logoutSuccessUrl("/").permitAll()
)
// ... existing code here
// @formatter:on
}
~~~
這 `/logout`端點要求我們對其進行POST,并且為了保護用戶免受跨站點請求偽造(CSRF,發音為“海上沖浪”)的侵害,它要求在請求中包含令牌。 令牌的值與提供保護的當前會話相關聯,因此我們需要一種將數據導入JavaScript應用程序的方法。
許多JavaScript框架都內置了對CSRF的支持(例如,在Angular中稱其為XSRF),但是它的實現方式通常與Spring Security的開箱即用行為略有不同。 例如,在Angular中,前端希望服務器向其發送一個名為“ XSRF-TOKEN”的cookie,如果看到該cookie,它將把值作為名為“ X-XSRF-TOKEN”的標頭發送回去。 我們可以使用簡單的jQuery客戶端實現相同的行為,然后服務器端的更改將與其他前端實現一起使用,而無需更改或更改很少。 為了向Spring Security講解這一點,我們需要添加一個創建cookie的過濾器。
在里面 `WebSecurityConfigurerAdapter` 我們執行以下操作:
SocialApplication.java
~~~
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
// ... existing code here
.csrf(c -> c
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
// ... existing code here
// @formatter:on
}
~~~
### 在客戶端中添加CSRF令牌
由于在此示例中我們沒有使用更高級別的框架,因此您需要顯式添加CSRF令牌,您剛剛可以從后端將其用作cookie。 為了使代碼更簡單,請添加 `js-cookie` 圖書館:
pom.xml
~~~
<dependency>
<groupId>org.webjars</groupId>
<artifactId>js-cookie</artifactId>
<version>2.1.0</version>
</dependency>
~~~
然后,您可以在HTML中引用它:
index.html
~~~
<script type="text/javascript" src="/webjars/js-cookie/js.cookie.js"></script>
~~~
最后,您可以使用 `Cookies` XHR中的便捷方法:
index.html
~~~
$.ajaxSetup({
beforeSend : function(xhr, settings) {
if (settings.type == 'POST' || settings.type == 'PUT'
|| settings.type == 'DELETE') {
if (!(/^http:.*/.test(settings.url) || /^https:.*/
.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-XSRF-TOKEN",
Cookies.get('XSRF-TOKEN'));
}
}
}
});
~~~
### 準備推出!
完成這些更改后,我們就可以運行該應用程序并嘗試新的注銷按鈕。 啟動應用程序,然后在新的瀏覽器窗口中加載主頁。 單擊“登錄”鏈接將您帶到GitHub(如果您已經登錄到GitHub,則可能不會注意到重定向)。 單擊“注銷”按鈕以取消當前會話,并使應用程序返回未經身份驗證的狀態。 如果您感到好奇,則應該能夠在瀏覽器與本地服務器交換的請求中看到新的cookie和標頭。
請記住,現在注銷端點正在與瀏覽器客戶端一起使用,然后所有其他HTTP請求(POST,PUT,DELETE等)也將同樣起作用。 因此,對于具有某些更實際功能的應用程序來說,這應該是一個很好的平臺。
## 用GitHub登錄
在本部分中,您將修改 的 [注銷](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_logout) 已經構建 應用程序,添加一個貼紙頁面,以便最終用戶可以在多組憑據之間進行選擇。
讓我們將Google添加為最終用戶的第二個選項。
### 初始設置
要使用Google的OAuth 2.0身份驗證系統進行登錄,您必須在Google API控制臺中設置一個項目以獲得OAuth 2.0憑據。
Google的OAuth 2.0 實現 身份驗證 符合 OpenID Connect 1.0 規范,并且已通過 OpenID認證 。
請 按照 上的說明進行操作 [OpenID Connect](https://developers.google.com/identity/protocols/OpenIDConnect) 從“設置OAuth 2.0”部分開始, 頁面 。
完成“獲取OAuth 2.0憑據”說明后,您應該擁有一個新的OAuth客戶端,其憑據由客戶端ID和客戶端密鑰組成。
### 設置重定向URI
另外,您將需要提供重定向URI,就像之前對GitHub所做的那樣。
在“設置重定向URI”子部分中,確保將“ **授權的重定向URI”** 字段設置為 `[http://localhost:8080/login/oauth2/code/google](http://localhost:8080/login/oauth2/code/google)`.
### 添加客戶注冊
然后,您需要配置客戶端以指向Google。 由于Spring Security的構建考慮到了多個客戶端,因此您可以在為GitHub創建的憑據旁邊添加我們的Google憑據:
application.yml
~~~
spring:
security:
oauth2:
client:
registration:
github:
clientId: github-client-id
clientSecret: github-client-secret
google:
client-id: google-client-id
client-secret: google-client-secret
~~~
如您所見,Google是Spring Security提供的開箱即用支持的另一個提供商。
### 添加登錄鏈接
在客戶端中,更改是微不足道的-您可以添加另一個鏈接:
index.html
~~~
<div class="container unauthenticated">
<div>
With GitHub: <a href="/oauth2/authorization/github">click here</a>
</div>
<div>
With Google: <a href="/oauth2/authorization/google">click here</a>
</div>
</div>
~~~
網址中的最終路徑應與中的客戶注冊ID相匹配 application.yml.
Spring Security附帶了一個默認的提供程序選擇頁面,可以通過指向以下頁面來訪問 /login 代替 /oauth2/authorization/{registrationId}.
### 如何添加本地用戶數據庫
即使將身份驗證委派給外部提供商,許多應用程序仍需要在本地保存有關其用戶的數據。 我們這里沒有顯示代碼,但是很容易分兩步進行。
1. 為您的數據庫選擇一個后端,并為自定義設置一些存儲庫(例如使用Spring Data) `User` 滿足您需求的對象,可以通過外部身份驗證完全或部分填充。
2. 實施并公開 `OAuth2UserService`調用授權服務器以及您的數據庫。 您的實現可以委派給默認實現,這將大大減輕調用Authorization Server的負擔。 您的實現應返回一些擴展自定義的內容 `User` 對象和工具 `OAuth2User`.
提示:在 `User` 對象以鏈接到外部提供程序中的唯一標識符(不是用戶名,而是外部提供程序中的帳戶唯一的名稱)。
## 為未經身份驗證的用戶添加錯誤頁面
在本部分中,您將修改 具有 [兩個提供商的](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_two_providers) 您先前構建的 應用程序,以向無法進行身份驗證的用戶提供一些反饋。 同時,您將擴展身份驗證邏輯以包括僅允許用戶屬于特定GitHub組織的規則。 “組織”是GitHub域特定的概念,但是可以為其他提供者設計類似的規則。 例如,對于Google,您可能只想對來自特定域的用戶進行身份驗證。
### 切換到GitHub
有 [兩個提供程序的](https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_two_providers) 示例使用GitHub作為OAuth 2.0提供程序:
application.yml
~~~
spring:
security:
oauth2:
client:
registration:
github:
client-id: bd1c0a783ccdd1c9b9e4
client-secret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
# ...
~~~
### 在客戶端中檢測身份驗證失敗
在客戶端上,您可能想為無法認證的用戶提供一些反饋。 為方便起見,您可以添加一個div,最終將向其中添加信息。
index.html
~~~
<div class="container text-danger error"></div>
~~~
然后,將呼叫添加到 `/error` 端點,填充 `<div>` 結果:
index.html
~~~
$.get("/error", function(data) {
if (data) {
$(".error").html(data);
} else {
$(".error").html('');
}
});
~~~
錯誤功能會與后端檢查是否有任何錯誤要顯示
### 添加錯誤信息
為了支持錯誤消息的檢索,您需要在身份驗證失敗時捕獲它。 為此,您可以配置一個 `AuthenticationFailureHandler`,就像這樣:
~~~
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
// ... existing configuration
.oauth2Login(o -> o
.failureHandler((request, response, exception) -> {
request.getSession().setAttribute("error.message", exception.getMessage());
handler.onAuthenticationFailure(request, response, exception);
})
);
}
~~~
每當身份驗證失敗時,以上方法都會將錯誤消息保存到會話中。
然后,您可以添加一個簡單的 `/error` 控制器,像這樣一個:
SocialApplication.java
~~~
@GetMapping("/error")
public String error() {
String message = (String) request.getSession().getAttribute("error.message");
request.getSession().removeAttribute("error.message");
return message;
}
~~~
這將取代默認 /error 應用中的頁面,適合我們的情況,但可能不夠復雜,無法滿足您的需求。
### 在服務器中生成401
如果用戶不能或不想使用GitHub登錄,Spring Security將已經收到401響應,因此,如果您無法通過身份驗證(例如,通過拒絕令牌授予),則該應用程序已經可以運行。
為了使事情更加有趣,您可以擴展身份驗證規則,以拒絕不在正確組織中的用戶。
您可以使用GitHub API查找有關用戶的更多信息,因此您只需要將其插入身份驗證過程的正確部分即可。
幸運的是,對于這樣一個簡單的用例,Spring Boot提供了一個簡單的擴展點:如果您聲明一個 `@Bean` 類型的 `OAuth2UserService`,它將用于識別用戶主體。 您可以使用該鉤子來斷言該用戶位于正確的組織中,如果不是,則拋出異常:
SocialApplication.java
~~~
@Bean
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService(WebClient rest) {
DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
return request -> {
OAuth2User user = delegate.loadUser(request);
if (!"github".equals(request.getClientRegistration().getRegistrationId())) {
return user;
}
OAuth2AuthorizedClient client = new OAuth2AuthorizedClient
(request.getClientRegistration(), user.getName(), request.getAccessToken());
String url = user.getAttribute("organizations_url");
List<Map<String, Object>> orgs = rest
.get().uri(url)
.attributes(oauth2AuthorizedClient(client))
.retrieve()
.bodyToMono(List.class)
.block();
if (orgs.stream().anyMatch(org -> "spring-projects".equals(org.get("login")))) {
return user;
}
throw new OAuth2AuthenticationException(new OAuth2Error("invalid_token", "Not in Spring Team", ""));
};
}
~~~
請注意,此代碼取決于 `WebClient`代表已認證用戶訪問GitHub API的實例。 完成此操作后,它遍歷組織,尋找與“ spring-projects”匹配的組織(這是用于存儲Spring開源項目的組織)。 如果您希望能夠成功進行身份驗證并且不在Spring Engineering團隊中,則可以在其中替換您自己的值。 如果沒有匹配項,則會拋出一個 `OAuth2AuthenticationException`,然后由Spring Security提取并轉到401響應中。
這 `WebClient` 也必須創建為bean,但這很簡單,因為通過使用 `spring-boot-starter-oauth2-client`:
~~~
@Bean
public WebClient rest(ClientRegistrationRepository clients, OAuth2AuthorizedClientRepository authz) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(clients, authz);
return WebClient.builder()
.filter(oauth2).build();
}
~~~
顯然,以上代碼可以推廣到其他身份驗證規則,其中一些規則適用于GitHub,另一些規則適用于其他OAuth 2.0提供程序。 您需要的是 WebClient 以及提供者的API的一些知識。
## 結論
我們已經了解了如何使用Spring Boot和Spring Security毫不費力地構建多種樣式的應用程序。 所有示例中運行的主要主題是使用外部OAuth 2.0提供程序進行身份驗證。
- springboot概述
- springboot構建restful服務
- spring構建一個RESTful Web服務
- spring定時任務
- 消費RESTful Web服務
- gradle構建項目
- maven構建項目
- springboot使用jdbc
- springboot應用上傳文件
- 使用LDNA驗證用戶
- 使用 spring data redis
- 使用 spring RabbitTemplate消息隊列
- 用no4j訪問nosql數據庫
- springboot驗證web表單
- Spring Boot Actuator構j建服務
- 使用jms傳遞消息
- springboot創建批處理服務
- spring security保護web 安全
- 在Pivotal GemFire中訪問數據
- 使用Spring Integration
- 使用springboot jpa進行數據庫操作
- 數據庫事務操作
- 操作mongodb
- springmvc+tymleaf創建web應用
- 將Spring Boot JAR應用程序轉換為WAR
- 創建異步服務
- spring提交表單
- 使用WebSocket構建交互式Web應用程序
- 使用REST訪問Neo4j數據
- jquery消費restful
- springboot跨域請求
- 消費SOAP Web服務
- springboot使用緩存
- 使用Vaadin創建CRUD UI
- 使用REST訪問JPA數據
- 使用REST訪問Pivotal GemFire中的數據
- 構建soap服務
- 使用rest訪問mongodb數據
- 構建springboot應用docker鏡像
- 從STS部署到Cloud Foundry
- springboot測試web應用
- springboot訪問mysql
- springboot編寫自定義模塊并使用
- 使用Google Cloud Pub / Sub進行消息傳遞
- 構建反應式RESTful Web服務
- 使用Redis主動訪問數據
- Spring Boot 部署到Kubernetes
- 使用反應式協議R2DBC訪問數據
- Spring Security架構
- spring構建Docker鏡像詳解
- Spring Boot和OAuth2
- springboot應用部署到k8s
- spring構建rest服務詳解