将 OAuth2 应用从 Spring Boot 1.2 迁移到 1.3

工程 | Dave Syer | 2015年11月30日 | ...

Spring Boot 1.3 中,有一些关于OAuth2客户端和服务器以及Spring Security OAuth2的新功能。其中一些功能是从Spring Cloud Security移植过来的,因此包含在Spring Cloud的 Angel 版本列车中,但在 Brixton 版本列车中没有。本文将帮助您了解这些更改,并更新任何现有应用程序以使用新功能。

依赖管理

如果您没有使用 Spring Cloud,您应该只需更改 Spring Boot 依赖项的版本号即可。由于一些 OAuth2 功能从 Spring Cloud Security 迁移到 Spring Boot 1.3,因此情况可能比这稍微复杂一些。一篇单独的文章 讨论了将 Spring Cloud 应用从 Spring Boot 1.2 升级到 1.3。如果您使用的是 Spring Cloud Angel 版本列车,那么您应该参考该文章了解如何管理依赖项(与任何特定功能无关)。

授权服务器

OAuth2 授权服务器最主要的任务是颁发访问令牌。为此,它必须能够对客户端应用程序(以及可选的用户)进行身份验证。

在 Spring Boot 1.3 中,可以通过约定和一些配置属性实现一个具有单个客户端的非常简单的 OAuth2 授权服务器。因此,像 spring.io 上Angular JS Spring Securrity 教程 中的普通身份验证服务器这样的真正基本的示例可以简化很多。在 Spring Boot 1.2 中,我们有

@SpringBootApplication
@RestController
@EnableResourceServer
public class AuthserverApplication {
       
  @Configuration
  @EnableAuthorizationServer
  protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
         
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Excep
      endpoints.authenticationManager(authenticationManager);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
      clients.inMemory()
        .withClient("acme")
          .secret("acmesecret")
          .authorizedGrantTypes("authorization_code", "refresh_token",
              "password")
          .scopes("openid").autoApprove(true);
    }

  }

  ...
}

在 Spring Boot 1.3 中,我们只有

@SpringBootApplication
@RestController
@EnableResourceServer
@EnableAuthorizationServer
public class AuthserverApplication {
  ...
}

以及在application.properties

security.oauth2.client.clientId: acme
security.oauth2.client.clientSecret: acmesecret
security.oauth2.client.authorized-grant-types: authorization_code,refresh_token,password
security.oauth2.client.scope: openid

这是一个通用的授权服务器,非常适合演示,但在实践中并不十分现实,因为它只有一个客户端(“acme”)。然而,作为快速开始使用 OAuth2 的方法,能够用这么少的代码做这么多事情是很好的。

要扩展基本示例并控制授权服务器功能,您只需返回旧的 Spring Boot 1.2 版本(任何具有其自身AuthorizationServerConfigurer的应用程序都会关闭自动配置的功能)。

资源服务器

资源服务器通过要求有效的访问令牌(由授权服务器创建)来保护其端点。

与授权服务器类似,资源服务器可以在 Spring Boot 1.3 中通过约定和一些配置属性来实现,也可以在 Spring Boot 1.2 中结合 Spring Cloud Security 来实现。例如,考虑 spring.io 上的普通资源服务器Angular JS Spring Security 教程

使用 Spring Boot 1.2,我们必须使用 Spring Cloud Security 的@EnableOAuth2Resource注解

@SpringBootApplication
@RestController
@EnableOAuth2Resource
class ResourceApplication {
  ...
}

以及一些配置来帮助解码application.properties中的访问令牌

spring.oauth2.resource.userInfoUri: https://127.0.0.1:9999/uaa/user

使用 Spring Boot 1.3,可以删除 Spring Cloud 依赖项,并将注解替换为来自 Spring Security OAuth2 的普通@EnableResourceServer注解

@SpringBootApplication
@RestController
@EnableResourceServer
class ResourceApplication {
  ...
}

要完成应用程序,需要稍微不同的配置(注意密钥前缀从spring.oauth2更改为security.oauth2

security.oauth2.resource.userInfoUri: https://127.0.0.1:9999/uaa/user

客户端应用程序

在 OAuth2 中,客户端是获取令牌的代理(通常是应用程序),通常代表用户。单点登录可以通过拥有单个授权服务器和作为 OAuth2 客户端的依赖身份验证应用程序来实现。

普通单点登录

在 Spring Boot 1.3 的客户端,您可以使用注解和一些配置属性来实现单点登录模式。如果您也使用 Spring Cloud Security,则可以使用 Spring Boot 1.2 执行相同的操作。

使用 Spring Boot 1.2 和 Spring Cloud Angel 的一个非常通用的示例将使用单个@EnableOAuth2Sso注解

@SpringBootApplication
@EnableOAuth2Sso
public class SsoApplication {
  ...
}

以及application.yml(或等效的application.properties)中客户端的一些配置。这是一个在 localhost:8080 上运行的应用程序与 Facebook 进行身份验证的示例

spring:
  oauth2:
    client:
      clientId: 233668646673605
      clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d
      accessTokenUri: https://graph.facebook.com/oauth/access_token
      userAuthorizationUri: https://www.facebook.com/dialog/oauth
      tokenName: oauth_token
      authenticationScheme: query
      clientAuthenticationScheme: form
    resource:
      userInfoUri: https://graph.facebook.com/me

Spring Boot 1.3 中的相同应用程序看起来几乎完全相同,但不需要 Spring Cloud Security。注解也移动到了不同的包,并且配置前缀从spring更改为security。因此,删除 Cloud 依赖项、更改注解的导入以及将application.yml中的前缀从spring更改为security是迁移此应用程序所需做的全部工作。

单点登录和自定义访问规则

在 Spring Cloud Security 1.0(来自 Angel 版本列车)中,用户可以使用特殊的回调OAuth2ClientConfigurerspring.oauth2.sso.*中的一些配置属性来自定义请求匹配和访问规则。例如,spring.io 上Angular JS Spring Security 教程中的普通客户端应用程序在 Spring Boot 1.2 中具有这种通用的实现模式

@SpringBootApplication
@EnableOAuth2Sso
public class UiApplication {

  @Configuration
  protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter {

    @Override
    public void match(RequestMatchers matchers) {
      matchers.anyRequest();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
      http
        .authorizeRequests()
          .antMatchers("/index.html", "/home.html", "/").permitAll()
          .anyRequest().authenticated()
        .and().csrf()
          .csrfTokenRepository(csrfTokenRepository())
        .and()
          .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
    }

  }

  ...
  
}

以及与普通示例类似的application.yml

spring:
   oauth2:
    sso:
      home:
        secure: false
        path: /,/**/*.html
     client:
       accessTokenUri: https://127.0.0.1:9999/uaa/oauth/token
       userAuthorizationUri: https://127.0.0.1:9999/uaa/oauth/authorize
     resource:
       userInfoUri: https://127.0.0.1:9999/uaa/user

使用 Spring Boot 1.3,不需要 Spring Cloud Security,并且自定义不需要过时的OAuth2SsoConfigurerAdapter。相反,它们只需要所有相同的代码,加上一个请求匹配器,在一个携带@EnableOAuth2Sso注解的常规WebSecurityConfigurerAdapter

@SpringBootApplication
@EnableZuulProxy
@EnableOAuth2Sso
public class UiApplication extends WebSecurityConfigurerAdapter {

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/**")
      .authorizeRequests()
        .antMatchers("/index.html", "/home.html", "/").permitAll()
        .anyRequest().authenticated()
      .and().csrf()
        .csrfTokenRepository(csrfTokenRepository())
      .and()
        .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
  }
  
  ...

}

更新版本中的配置属性大多相同:前缀从spring.oauth2更改为security.oauth2,并且不需要*.oauth2.sso.*属性,因为它们是由用户在WebSecurityConfigurerAdapter中显式配置的。

单点登录和 Zuul 代理

spring.io 上的Angular JS Spring Security 教程中的实际客户端应用与上面定制的应用类似,但它也是一个 Zuul 代理,负责将浏览器客户端的请求转发到后端服务。在 Spring Boot 1.3 中,此应用仍然需要 Spring Cloud Security 来进行令牌中继(它需要将用于身份验证的访问令牌发送到后端资源),但它不需要它来实现基本的 SSO 功能,因此实现与之前的示例相同,只是添加了一个@EnableZuulProxy注解。

注意:Spring Boot 1.3.0 中的一个 bug 导致了当前实现(撰写本文时)的客户端应用在Angular JS Spring Security 教程中的一个变通方法。此变通方法仅是因为它使用了 Spring Cloud Security 才需要,并且在 Spring Boot 1.3.1 中也将是多余的。

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
class WorkaroundRestTemplateCustomizer implements UserInfoRestTemplateCustomizer {

  public void customize(OAuth2RestTemplate template) {
    template.setInterceptors(new ArrayList<>(template.getInterceptors()));
  }

}

Cloud Foundry 中的单点登录

使用 Spring Boot 1.2 和 Angel 版本系列中的 Spring Cloud Security,如果绑定到具有正确凭据的服务,则应用程序能够自行配置 OAuth2 SSO。例如,您可以像这样创建一个用户提供的服务:

cf create-user-provided-service sso -p '{"userInfoUri":"https://uaa.run.pivotal.io/userinfo", "tokenUri":"login.run.pivotal.io/oauth/token", "authorizationUri":"login.run.pivotal.io/oauth/authorize", "clientId":"[client]", "clientSecret":"[secret]"}'

然后,绑定到该服务的具有@EnableOAuth2Sso的应用程序将绑定 SSO 所需的配置,而无需用户更改任何配置。

Spring Boot 1.3 和 Spring Cloud Brixton 中也提供了相同的特性,但是您需要使用新的spring-cloud-cloudfoundry-web库来获取 SSO 配置绑定行为,而不是使用 Spring Cloud Security。您也可以使用 Cloud Foundry 中的内置 SSO 服务,而不是用户提供的服务(在 Pivotal Web Services 的特定帐户或 Pivotal Cloud Foundry 中可用)。

结论

如果您正在使用 Spring Boot 以及可能使用了 Spring Cloud 和 OAuth2,希望您现在能够顺利地从 Spring Boot 1.2 升级到 1.3,或者至少您将有一些工具来帮助您思考在遇到问题时发生了什么。Spring Boot 1.3 几乎拥有 Spring Cloud Security 1.0 的所有功能,因此您需要考虑的主要问题是依赖项。此外,Spring Boot 还有一些新功能,例如授权服务器的自动配置。与 Spring Boot 一样,您可以(也应该)采用默认行为,直到您需要更改它为止,这时应该没有任何障碍。

Spring 指南中的示例应用程序现已全部更新到 Spring Boot 1.3,即使这意味着它们依赖于 Spring Cloud 的里程碑版本(这仅适用于 Zuul 代理示例)。许多不再需要 Spring Cloud。如果您需要 Spring Cloud 的 GA 版本,您现在需要保留 Spring Boot 1.2。该组合的示例可以从 git 历史记录中提取。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加快您的进度。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部