# 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服务详解