OAuth2RestTemplate 简介

1、概览

本文将带你了解如何使用 Spring OAuth2RestTemplate 进行 OAuth2 REST 调用。

创建一个 Spring Web 应用,用于列出 GitHub 账户下的所有仓库。

2、Maven 依赖

首先,需要在 pom.xml 中添加 spring-boot-starter-securityspring-security-oauth2-autoconfigure 依赖。由于构建的是 Web 应用,因此还需要包含 spring-boot-starter-webspring-boot-starter-thymeleaf Starter。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.6.8</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

3、OAuth2 Properties

接下来,在 application.properties 文件中添加 OAuth 配置,以连接到 GitHub 账户:

# 替换为你的 CLIENT_ID
github.client.clientId=[CLIENT_ID]
# 替换为你的 CLIENT_SECRET
github.client.clientSecret=[CLIENT_SECRET]
github.client.userAuthorizationUri=https://github.com/login/oauth/authorize
github.client.accessTokenUri=https://github.com/login/oauth/access_token
github.client.clientAuthenticationScheme=form

github.resource.userInfoUri=https://api.github.com/user
github.resource.repoUri=https://api.github.com/user/repos

注意,需要用 GitHub OAuth App 中的值替换 [CLIENT_ID][CLIENT_SECRET] 。你可以按照 创建 OAuth App 指南在 GitHub 上注册一个新的应用:

注册 Github APP

确保 “Authorization callback URL” 设置为 http://localhost:8080,这会把 OAuth 流程重定向到我们的 Web 应用主页。

4、OAuth2RestTemplate 配置

创建 Security 配置,为应用提供 OAuth2 支持。

4.1、SecurityConfig

@Configuration
@EnableOAuth2Client
public class SecurityConfig {
    OAuth2ClientContext oauth2ClientContext;

    public SecurityConfig(OAuth2ClientContext oauth2ClientContext) {
        this.oauth2ClientContext = oauth2ClientContext;
    }

    ...
}

@EnableOAuth2Client 使我们能够访问 OAuth2 Context,用它来创建 OAuth2RestTemplate

4.2、OAuth2RestTemplate Bean

创建 OAuth2RestTemplate Bean:

@Bean
public OAuth2RestTemplate restTemplate() {
    return new OAuth2RestTemplate(githubClient(), oauth2ClientContext);
}

@Bean
@ConfigurationProperties("github.client")
public AuthorizationCodeResourceDetails githubClient() {
    return new AuthorizationCodeResourceDetails();
}

如上,使用 OAuth2 Properties 和 Context 来创建 Template 实例。

@ConfigurationProperties 注解将所有 github.client 属性注入 AuthorizationCodeResourceDetails 实例。

4.3、Authentication Filter

我们需要一个 Authentication Filter 来处理 OAuth2 流程:

private Filter oauth2ClientFilter() {
    OAuth2ClientAuthenticationProcessingFilter oauth2ClientFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/github");
    OAuth2RestTemplate restTemplate = restTemplate();
    oauth2ClientFilter.setRestTemplate(restTemplate);
    UserInfoTokenServices tokenServices = new UserInfoTokenServices(githubResource().getUserInfoUri(), githubClient().getClientId());
    tokenServices.setRestTemplate(restTemplate);
    oauth2ClientFilter.setTokenServices(tokenServices);
    return oauth2ClientFilter;
}

@Bean
@ConfigurationProperties("github.resource")
public ResourceServerProperties githubResource() {
    return new ResourceServerProperties();
}

如上,指定 Filter 在应用的 /login/github URL 上启动 OAuth2 流程。

4.4、Spring Security 配置

最后,注册 OAuth2ClientContextFilter 并创建 Web Security 配置:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/", "/login**", "/error**")
        .permitAll()
        .anyRequest()
        .authenticated()
        .and()
        .logout()
        .logoutUrl("/logout")
        .logoutSuccessUrl("/")
        .and()
        .addFilterBefore(oauth2ClientFilter(), BasicAuthenticationFilter.class);
    return http.build();
}

@Bean
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
    FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<>();
    registration.setFilter(filter);
    registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
    return registration;
}

保护 Web 应用指定的路径,并确保 OAuth2ClientAuthenticationProcessingFilterBasicAuthenticationFilter 先注册。

5、使用 Auth2RestTemplate

OAuth2RestTemplate 的主要目标是减少进行基于 OAuth2 的 API 调用所需的代码。它基本上满足了应用的两个需求:

  • 处理 OAuth2 认证流程。
  • 继承 RestTemplate 以进行 API 调用。

现在,可以在 Web Controller 中注入 OAuth2RestTemplate

5.1、登录

创建包含 “登录” 和 “主页” 选项的 index.html 文件:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>OAuth2Client</title>
</head>
<body>
<h3>
    <a href="/login/github" th:href="@{/home}" th:if="${#httpServletRequest?.remoteUser != undefined }">
        Go to Home
    </a>
    <a href="/hello" th:href="@{/login/github}" th:if="${#httpServletRequest?.remoteUser == undefined }">
        GitHub Login
    </a>
</h3>
</body>
</html>

未经身份认证的用户将看到登录选项,而通过身份认证的用户则可以访问主页。

5.2、主页

现在,创建一个 Controller 来处理通过身份认证的 GitHub 用户:

@Controller
public class AppController {

    OAuth2RestTemplate restTemplate;

    public AppController(OAuth2RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping("/home")
    public String welcome(Model model, Principal principal) {
        model.addAttribute("name", principal.getName());
        return "home";
    }
}

注意,在 welcome 方法中使用了 Security Principal 参数。以 Principalname 作为视图中 Model 的属性。

home.html template 如下:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Home</title>
</head>
<body>
    <p>
        Welcome <b th:inline="text"> [[${name}]] </b>
    </p>
    <h3>
        <a href="/repos">View Repositories</a><br/><br/>
    </h3>

    <form th:action="@{/logout}" method="POST">
        <input type="submit" value="Logout"/>
    </form>
</body>
</html>

此外,还添加了一个查看用户仓库列表的链接和一个注销选项。

5.3、GitHub 仓库

使用 Controller 中的 OAuth2RestTemplate 来展示用户拥有的所有 GitHub 仓库。

首先,创建 GithubRepo 类来表示一个仓库:

public class GithubRepo {
    Long id;
    String name;

    // get / set 省略

}

接着,在 AppController 添加一个 repos 端点:

@GetMapping("/repos")
public String repos(Model model) {
    Collection<GithubRepo> repos = restTemplate.getForObject("https://api.github.com/user/repos", Collection.class);
    model.addAttribute("repos", repos);
    return "repositories";
}

OAuth2RestTemplate 处理向 GitHub 发起请求的所有模板代码。此外,它还会将 REST 响应转换为 GithubRepo 集合。

最后,创建 repositories.html template 来遍历 repositories 集合:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Repositories</title>
</head>
<body>
    <p>
        <h2>Repos</h2>
    </p>
    <ul th:each="repo: ${repos}">
        <li th:text="${repo.name}"></li>
    </ul>
</body>
</html>

6、总结

本文介绍了如何使用 OAuth2RestTemplate 来简化对 OAuth2 资源服务器(如 GitHub)的 REST 调用。


参考:https://www.baeldung.com/spring-oauth2resttemplate