Spring Boot 与 Docker
观察 GraphQL 的实际运行

本指南展示了如何使用 OAuth 2.0Spring Boot 构建一个示例应用程序,实现各种“社交登录”功能。

它从简单的单一提供者单点登录开始,逐步构建一个可以选择认证提供者(如 GitHubGoogle)的客户端。

这些示例都是单页应用程序,后端使用 Spring Boot 和 Spring Security。它们在前端都使用纯 jQuery。但是,转换为其他 JavaScript 框架或使用服务器端渲染所需的改动将非常少。

所有示例都是使用 Spring Boot 中的原生 OAuth 2.0 支持实现的。

多个示例相互构建,每一步都添加了新功能:

  • simple: 一个非常基础的静态应用,只有一个主页,并通过 Spring Boot 的 OAuth 2.0 配置属性进行无条件登录(如果您访问主页,您将自动重定向到 GitHub)。

  • click: 添加了一个显式的链接,用户必须点击该链接才能登录。

  • logout: 为已认证的用户添加了一个注销链接。

  • two-providers: 添加了第二个登录提供者,因此用户可以在主页上选择使用哪一个。

  • custom-error: 为未认证的用户添加了一个错误消息,并基于 GitHub 的 API 实现了自定义认证。

在功能阶梯中从一个应用程序迁移到下一个应用程序所需的变化可以在源代码中进行追踪。每个版本的应用程序都有自己的目录,以便您可以比较它们之间的差异。

每个应用程序都可以导入到IDE中。您可以运行SocialApplication中的main方法来启动应用程序。它们都会在http://localhost:8080上提供一个主页(并且如果您想登录并查看内容,所有应用都要求您至少拥有GitHub和Google账户)。

您也可以在命令行中使用mvn spring-boot:run运行所有应用程序,或者通过构建jar文件并使用mvn packagejava -jar target/*.jar来运行(根据Spring Boot文档和其他可用文档)。如果您在顶层使用wrapper,则无需安装Maven,例如:

$ cd simple
$ ../mvnw package
$ java -jar target/*.jar

所有应用程序都在 localhost:8080 上运行,因为它们将使用在该地址上注册的 GitHub 和 Google 的 OAuth 2.0 客户端。要在不同的主机或端口上运行它们,您需要以这种方式注册您的应用程序。如果使用默认值,不会有泄露您凭证的风险。但是,请注意在互联网上暴露的内容,并且不要将您自己的应用程序注册信息放入公共源代码控制中。

使用 GitHub 实现单点登录

在本节中,您将创建一个使用 GitHub 进行身份验证的最小应用程序。通过利用 Spring Boot 的自动配置功能,这将变得非常简单。

创建新项目

首先,您需要创建一个 Spring Boot 应用程序,这可以通过多种方式完成。最简单的方法是访问 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 进行操作。

添加主页

在您的新项目中,在 src/main/resources/static 文件夹中创建 index.html。您应该添加一些样式表和 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 "locator",它是由 webjars 站点提供的库。Spring 可以使用 locator 来定位 webjars 中的静态资源,而无需知道确切的版本(因此在 index.html 中使用无版本的 /webjars/** 链接)。只要您没有关闭 MVC 自动配置,webjar locator 在 Spring Boot 应用程序中默认是激活的。

有了这些更改,您应该会为您的应用程序拥有一个美观的主页。

使用 GitHub 和 Spring Security 保护应用程序

为了使应用程序更加安全,您可以简单地添加 Spring Security 作为依赖项。由于您希望实现“社交”登录(委托给 GitHub),您应该包含 Spring Security OAuth 2.0 Client starter:

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

通过添加该配置,您的应用程序将默认使用 OAuth 2.0 进行保护。

接下来,您需要将应用程序配置为使用 GitHub 作为身份验证提供者。为此,请执行以下操作:

添加一个新的 GitHub 应用

要使用 GitHub 的 OAuth 2.0 认证系统进行登录,您首先需要添加一个新的 GitHub 应用

选择“New OAuth App”,然后会显示“Register a new OAuth application”页面。输入应用名称和描述。然后,输入您应用的主页,在本例中应为 http://localhost:8080。最后,将授权回调 URL 指定为 http://localhost:8080/login/oauth2/code/github,并点击 Register Application

OAuth 重定向 URI 是应用程序中的路径,最终用户的用户代理在 GitHub 认证并授予应用程序访问权限后,将被重定向回该路径。

默认的重定向 URI 模板是 {baseUrl}/login/oauth2/code/{registrationId}registrationIdClientRegistration 的唯一标识符。

配置 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。现在,您应该会被重定向到使用GitHub登录,而不是直接进入主页。如果您完成登录并接受所有请求的授权,您将被重定向回本地应用程序,此时主页将可见。

如果您保持登录GitHub的状态,即使您在一个没有cookie和缓存数据的新浏览器中打开此本地应用程序,也无需重新进行身份验证。(这就是单点登录的含义。)

如果您正在使用示例应用程序进行本节操作,请确保清除浏览器缓存中的 cookies 和 HTTP Basic 凭据。对于单个服务器,最好的方法是打开一个新的隐私窗口。

授权访问此示例是安全的,因为只有本地运行的应用程序可以使用这些令牌,并且它请求的权限范围是有限的。不过,在登录此类应用程序时,请注意您所批准的内容:它们可能会请求超出您舒适范围的权限(例如,它们可能会请求更改您的个人数据的权限,这不太可能符合您的利益)。

发生了什么?

您刚刚编写的应用程序,在 OAuth 2.0 的术语中,是一个客户端应用程序,它使用授权码授权从 GitHub(授权服务器)获取访问令牌。

然后,它使用访问令牌向 GitHub 请求一些个人信息(仅限于您允许的范围),包括您的登录 ID 和姓名。在这个阶段,GitHub 充当资源服务器,解码您发送的令牌并检查它是否授予应用程序访问用户信息的权限。如果该过程成功,应用程序会将用户信息插入到 Spring Security 上下文中,从而使您通过身份验证。

如果您在浏览器工具中(Chrome 或 Firefox 中的 F12)查看并跟踪所有跳转的网络流量,您会看到与 GitHub 之间的重定向,最终您会回到主页,并带有一个新的 Set-Cookie 头。此 cookie(默认为 JSESSIONID)是 Spring(或任何基于 servlet 的)应用程序的身份验证信息的令牌。

因此,我们拥有一个安全的应用程序,因为用户必须通过外部提供者(GitHub)进行身份验证才能查看任何内容。

我们不会将其用于网上银行网站。但对于基本的身份识别目的,以及区分您网站的不同用户内容来说,这是一个极好的起点。这就是为什么这种身份验证方式如今非常流行。

在下一节中,我们将为应用程序添加一些基本功能。我们还会让用户在初次重定向到 GitHub 时更清楚地了解正在发生的事情。

添加欢迎页面

在本节中,您将通过添加一个显式的 GitHub 登录链接来修改刚刚构建的简单应用程序。与立即重定向不同,新的链接将显示在主页上,用户可以选择登录或保持未认证状态。只有当用户点击了该链接后,安全内容才会被渲染。

首页上的条件内容

要在用户已认证的条件下渲染内容,您可以选择服务器端渲染或客户端渲染。

在这里,您将使用 JQuery 更改客户端,不过如果您更倾向于使用其他工具,将客户端代码进行转换应该不会太困难。

要开始使用动态内容,您需要像这样标记几个 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> 会显示,而第二个不会。还要注意带有 id 属性的空 <span>

稍后,您将添加一个服务器端端点,该端点将以 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 来关闭主页的安全性:

SocialApplication

@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 对带有 @SpringBootApplication 注解的类中的 WebSecurityConfigurerAdapter 赋予了特殊意义:它使用该适配器来配置承载 OAuth 2.0 认证处理器的安全过滤器链。

上述配置表示允许访问的端点白名单,其他所有端点都需要进行认证。

您希望允许:

  • / 因为这是您刚刚动态化的页面,其中部分内容对未认证的用户可见

  • /error 因为这是 Spring Boot 用于显示错误的端点

  • /webjars/** 因为您希望您的 JavaScript 对所有访问者运行,无论是否经过认证

在这个配置中,您不会看到任何关于 /user 的内容。所有内容,包括 /user,除非特别说明,都会保持安全,这是因为在配置的最后使用了 .anyRequest().authenticated() 设置。

最后,由于我们通过 Ajax 与后端进行交互,我们希望配置端点以响应 401 状态码,而不是默认的重定向到登录页面的行为。通过配置 authenticationEntryPoint,我们可以实现这一点。

完成这些更改后,应用程序就完成了。如果您运行它并访问主页,您应该会看到一个样式精美的 HTML 链接,显示为“使用 GitHub 登录”。这个链接不会直接带您到 GitHub,而是带您到处理身份验证的本地路径(并发送重定向到 GitHub)。一旦您完成身份验证,您将被重定向回本地应用程序,现在它会显示您的名字(假设您已经在 GitHub 中设置了允许访问该数据的权限)。

添加一个注销按钮

在本节中,我们通过添加一个允许用户退出应用程序的按钮来修改我们构建的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>

然后我们在 JavaScript 中提供它引用的 logout() 函数:

index.html

var logout = function() {
    $.post("/logout", function() {
        $("#user").html('');
        $(".unauthenticated").show();
        $(".authenticated").hide();
    })
    return true;
}

logout() 函数会向 /logout 发送 POST 请求,然后清除动态内容。现在我们可以切换到服务端来实现该端点。

添加登出端点

Spring Security 内置支持 /logout 端点,它会为我们执行正确的操作(清除会话并使 cookie 失效)。要配置该端点,我们只需在 WebSecurityConfigurerAdapter 中扩展现有的 configure() 方法:

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,发音为“sea surf”)攻击,它要求在请求中包含一个令牌。该令牌的值与当前会话相关联,从而提供保护,因此我们需要一种方法将这些数据传递到我们的 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>

最后,您可以在XHR中使用Cookies的便捷方法:

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(如果您已经登录,可能不会注意到重定向)。点击“注销”按钮以取消当前会话,并将应用程序返回到未认证状态。如果您感兴趣,应该能够在浏览器与本地服务器交换的请求中看到新的 cookie 和头部信息。

请记住,现在注销端点已经可以与浏览器客户端一起工作,那么所有其他HTTP请求(POST、PUT、DELETE等)也同样可以正常工作。因此,这对于一个具有更多实际功能的应用程序来说,应该是一个很好的平台。

使用 GitHub 登录

在本节中,您将修改已经构建的注销应用程序,添加一个贴纸页面,以便最终用户可以在多组凭证之间进行选择。

让我们将Google添加为最终用户的第二个选项。

初始设置

要使用 Google 的 OAuth 2.0 认证系统进行登录,您必须在 Google API 控制台中设置一个项目以获取 OAuth 2.0 凭证。

Google 的 OAuth 2.0 实现 用于身份验证,符合 OpenID Connect 1.0 规范,并且通过了 OpenID 认证

按照 OpenID Connect 页面上的说明进行操作,从“设置 OAuth 2.0”部分开始。

完成“获取 OAuth 2.0 凭证”的步骤后,您应该会获得一个新的 OAuth 客户端,其中包含由客户端 ID 和客户端密钥组成的凭证。

设置重定向 URI

此外,您需要提供重定向 URI,就像之前为 GitHub 所做的那样。

在“设置重定向 URI”小节中,确保 Authorized redirect URIs 字段设置为 http://localhost:8080/login/oauth2/code/google

添加客户端注册

然后,您需要配置客户端以指向 Google。因为 Spring Security 是考虑到多个客户端而构建的,您可以将我们的 Google 凭证与为 GitHub 创建的凭证一起添加:

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>

URL 中的最终路径应与 application.yml 中的客户端注册 ID 匹配。

Spring Security 自带一个默认的提供者选择页面,可以通过访问 /login 而不是 /oauth2/authorization/{registrationId} 来访问。

如何添加本地用户数据库

许多应用程序需要在本地保存用户数据,即使认证委托给外部提供者。我们在此不展示代码,但只需两个步骤即可轻松实现。

  1. 为您的数据库选择一个后端,并设置一些存储库(例如使用 Spring Data),用于自定义的 User 对象,该对象可以完全或部分从外部身份验证中填充,以满足您的需求。

  2. 实现并暴露 OAuth2UserService,以调用授权服务器以及您的数据库。您的实现可以委托给默认实现,默认实现将负责调用授权服务器的繁重工作。您的实现应返回一个扩展了自定义 User 对象并实现了 OAuth2User 的对象。

提示:在 User 对象中添加一个字段,用于链接到外部提供者中的唯一标识符(不是用户名,而是该外部提供者账户中唯一的标识符)。

为未认证用户添加错误页面

在本节中,您将修改之前构建的two-providers应用程序,向无法认证的用户提供一些反馈。同时,您将扩展认证逻辑,包含一条规则,即只允许属于特定 GitHub 组织的用户进行认证。"组织"是 GitHub 特定领域的概念,但可以为其他提供商设计类似的规则。例如,对于 Google,您可能只想认证来自特定域的用户。

切换到 GitHub

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(HttpServletRequest request) {
    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 已经提供了一个简单的扩展点:如果您声明一个类型为 @BeanOAuth2UserService,它将被用来识别用户主体。您可以使用这个钩子来断言用户是否在正确的组织中,如果不在,则抛出异常:

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 提供程序进行身份验证。

所有示例应用程序都可以轻松扩展和重新配置,以适应更具体的用例,通常只需更改配置文件即可。请记住,如果您在您自己的服务器上使用这些示例的版本,请向 GitHub(或类似平台)注册并获取您自己主机地址的客户端凭据。并且请记住,不要将这些凭据放入源代码控制中!

本页目录