概述

在这个教程中,我们将学习如何使用Spring的OAuth2RestTemplate进行OAuth2 REST调用。

我们将创建一个能够列出GitHub账户仓库的Spring Web应用。


2. Maven配置

首先,我们需要在pom.xml文件中添加以下依赖:spring-boot-starter-securityspring-security-oauth2-autoconfigure。由于我们正在构建Web应用,还需要包括spring-boot-starter-webspring-boot-starter-thymeleaf

<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属性

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

github.client.clientId=[CLIENT_ID]
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

请注意,需要将\[**CLIENT_ID\]**\[CLIENT_SECRET\]**替换为GitHub OAuth应用的值。我们可以参考创建OAuth应用指南,在GitHub上注册一个新的应用:

确保授权回调URL设置为http://localhost:8080,这将引导OAuth流程重定向到我们的Web应用主页。


4. OAuth2RestTemplate配置

现在是时候为我们的应用提供OAuth2支持的配置了。

4.1. SecurityConfig

首先,创建Spring的安全配置:

@Configuration
@EnableOAuth2Client
public class SecurityConfig {
    OAuth2ClientContext oauth2ClientContext;

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

    ...
}

@EnableOAuth2Client提供了访问OAuth2上下文,我们将使用它来创建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属性和上下文创建了模板实例。@ConfigurationProperties注解注入了所有github.client属性到AuthorizationCodeResourceDetails实例中。

4.3. 认证过滤器

第三,我们需要一个认证过滤器来处理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();
}

在这里,我们指示过滤器在应用的/login/github URL上启动OAuth2流程。

4.4. Spring Security配置

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

@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. 使用OAuth2RestTemplate

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

  • 处理OAuth2身份验证流程
  • 扩展Spring的RestTemplate进行API调用

现在我们可以在Web控制器中作为自动注入的bean使用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. 主页

现在,创建一个控制器来向已认证的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方法中有一个Principal参数。我们使用Principal的名字作为UI模型的属性。

看看home.html模板:

<!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仓库

现在,是时候使用之前控制器中创建的OAuth2RestTemplate来展示用户所有的GitHub仓库了。

首先,我们需要创建一个GithubRepo类来表示仓库:

public class GithubRepo {
    Long id;
    String name;

    // getters and setters

}

其次,向之前的AppController添加仓库映射:

@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模板遍历仓库集合:

<!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调用。

我们了解了运行OAuth2流程的Web应用的基本构建块,然后看到了如何调用REST API来获取GitHub用户的全部仓库。

如往常一样,本教程的完整示例可以在GitHub上找到