ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 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提供程序进行身份验证。