利用OAuth2.0来构建一个基于Spring Boot的安全的端到端通信应用

Posted SpringForAll社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用OAuth2.0来构建一个基于Spring Boot的安全的端到端通信应用相关的知识,希望对你有一定的参考价值。

原文:https://dzone.com/articles/build-a-spring-boot-app-with-secure-server-to-serv

译者:anLA7856

如何利用OAuth2.0客户端授权方式来构建一个端到端的Spring Boot应用呢?

前言: 大多数OAuth2.0的指引都以用户为出发点,例如,通过Google,Github,Okta等等方式登录。虽然这可能会很有用,但是它忽略了服务器到服务器的通信(这里没有用户),并且你只有一个服务连接到另一个服务器,Okta在这里帮助你实现有关这一块的应用安全。

OAuth2.0客户端授权方式专门应用于无用户场景的认证方式,例如CRON任务,计划任务,其他的数据工作负载等。由于没有终端用户或者浏览器介入,这种方式授权比其他的OAuth授权方式更加少见。但是,相比于复杂的以用户为中心的OAuth2.0授权模式,这种方式更加容易理解。在这片文章中,我将介绍OAuth2.0客户端授权协议,并且讲授如何在Spring Boot中应用它实现一个安全的服务端到服务端的通信。

OAuth 2.0客户端授权

客户端授权模式的目的是允许两个机器中间安全的通信。在这个模式中,你有一个端(可以认为是你的应用)向另一个应用发起API请求(可以认为是你的资源服务器)。 为了帮助说明这样模式的重要性,让我们先穿越一下,看看在AAuth2.0以前我们是如何实现上述通信的。

Note:如果你已经对OAuth 2.0有了一定了解,你可以跳过这一节,直接进入到下一段代码示例部分,并且从GitHub中check out。

在OAuth 2.0之前,开发人员是利用基于HTTP的认证方式解决端到端认证。本质上来说,就是把用户名密码作为请求的一部分发送给另一个API应用,这个API应用然后会校验每一次请求的用户名密码来判定是否授权

这样的方式有以下的缺点和暴露点:

  • 以上图表中的每一个应用都处理用户名密码

  • 二次用户名密码可能要连接到存储用户信息的地方

  • 相同的用户名密码在每次请求中都用到

有众多不同的方式来减轻这样的风险,但是这超出了这片文章的范围。

OAuth 2.0客户端授权的出现,是为了解决基于HTTP的基本授权模式,尽管我们仍然使用用户名和密码(称为clientid和clientsecret),通过传输由中间认证服务器产生的token,而不是每次都传输用户名密码。

利用OAuth2.0来构建一个基于Spring Boot的安全的端到端通信应用

认证服务器返回一个临时性的token(过期时间内有效)。当客户端与资源服务器交互时利用这个token,这意味着最敏感的数据(ID和secret)仅仅在过期时间内才在互联网上共享一次,这极大的降低了损害的可能性。

一旦资源服务器收到了带有token的来访请求,它会通过和认证服务器交互来验证这个token。 我会在文章的结尾讨论一些减少网络调用数量的方法,但首先,要介绍一个例子。

构建一个OAuth 2.0客户端授权方式应用

够了,让我们做点什么吧!我将像你们展示如何实现基于Spring的客户端授权类型的例子,它包括client和server。server端将只包含一个端点返回字符串“message of the day.”。client端将会是一个命令行应用,你可以用后端的Web应用程序,CRON作业或任何其他后端脚本轻松替代它。

建立你的认证服务器

为了让事情变得更简单,你可以使用Okta作为你的OAuth 2.0 认证服务器。这样将会解决所有以上提到的客户端授权方式所需要的工作。你需要用到Okta吗?一点也不!你可以使用任何你想要的OAuth 2.0的服务器。因为我们所要实现的服务是免费并且易于使用的,这样的方式加快了我们的进度。

如果你还没有一个免费的开发者帐号,去https://developer.okta.com/ 申请一个吧,为了完成之后,你需要两个信息:

  1. 基于Okta的URL,例如dev-123456.oktapreview.com

当激活了你的Okta帐号之后,你会处于开发者控制台界面,此时你需要创建一个应用以及一个用户OAuth范围(scope)。Okta将会给你应用一个client_id和secret,然而scope将会限制你的token所能获取到应用的资源。

点击Applications ,然后依次选择Service -> Next ,然后你可以讲名字改成你想要的(我打算改成“My MOD App”),然后,点击Done 。

下一步,你将会用到client_id和secret。 然后,为你的应用创建一个custom scope (权限范围)。

从菜单开始,依次选择 API -> Authorization Servers 。记住Issuer URI 的值,你需要这个值来完成下一步的操作。

通过单击铅笔来编辑认证服务器,然后依次点击Scopes -> Add Scope ,将name框填为custom_mod,然后点击create 。

利用OAuth2.0来构建一个基于Spring Boot的安全的端到端通信应用现在,到有趣的东西了!

创建一个资源服务器

资源服务器(即API服务)将会十分简单,并且只有一个端点 /mod 组成,现在让我们用 Spring Initializer 来创建一个项目吧!

 
   
   
 
  1. curl https://start.spring.io/starter.tgz  \

  2.  -d artifactId=creds-example-server \

  3.  -d dependencies=security,web \

  4.  -d language=java \

  5.  -d type=maven-project \

  6.  -d baseDir=creds-example-server \

  7. | tar -xzvf -

  8. # change into the new directory

  9. cd creds-example-server

另外,你需要手工的加入以下的依赖到pom.xml中:

 
   
   
 
  1. <dependency>

  2.    <groupId>org.springframework.security.oauth.boot</groupId>

  3.    <artifactId>spring-security-oauth2-autoconfigure</artifactId>

  4.    <version>2.0.0.RELEASE</version>

  5. </dependency>

NOTE: 从Spring Initializer上面获取的项目,我把名字由 DemoApplication 改为了 ServerApplication ,因为我们将要立刻创建另外一个项目。 下一步,给 ServerApplication加上 @EnableResourceServer 注解。并且实现一个简单的REST控制器。

 
   
   
 
  1. @EnableResourceServer

  2. @SpringBootApplication

  3. public class ServerApplication {

  4.    public static void main(String[] args) {

  5.        SpringApplication.run(ServerApplication.class, args);

  6.    }

  7.    /**

  8.     * Allows for @PreAuthorize annotation processing.

  9.     */

  10.    @EnableGlobalMethodSecurity(prePostEnabled = true)

  11.    protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {

  12.        @Override

  13.        protected MethodSecurityExpressionHandler createExpressionHandler() {

  14.            return new OAuth2MethodSecurityExpressionHandler();

  15.        }

  16.    }

  17.    @RestController

  18.    public class MessageOfTheDayController {

  19.        @GetMapping("/mod")

  20.        @PreAuthorize("#oauth2.hasScope('custom_mod')")

  21.        public String getMessageOfTheDay(Principal principal) {

  22.            return "The message of the day is boring for user: " + principal.getName();

  23.        }

  24.    }

  25. }

现在,该配置文件了!我把application.propertis改为了application.yml 。并且做了如下更新:

 
   
   
 
  1. security:

  2.  oauth2:

  3.    client:

  4.      clientId: {client-id-from-above}

  5.      clientSecret: {client-secret-from-above}

  6.    resource:

  7.      tokenInfoUri: {issuer-uri-from-above}/v1/introspect

就是这样,只需要几行代码的配置,Spring Boot自动的完成对token的校验,你所需要做的就是专注于你的代码。

下一步,启动并运行它。 你可以访问http://localhost:8080/mod 。但是它会返回401UNAUTHORIZED

创建OAuth 2.0客户端

下一步,将创建一个简单的命令行应用,你能够轻易的在任何应用中使用它。 现在,打开一个命令终端,并利用Spring Initializer 创建第二个应用:

 
   
   
 
  1. curl https://start.spring.io/starter.tgz  \

  2.  -d artifactId=creds-example-client \

  3.  -d dependencies=security \

  4.  -d language=java \

  5.  -d type=maven-project \

  6.  -d baseDir=creds-example-client \

  7. | tar -xzvf -

  8. # change into the new directory

  9. cd creds-example-client

然后,和之前一样,往pom文件里面加入Spring OAuth 2.0 依赖:

 
   
   
 
  1. <dependency>

  2.    <groupId>org.springframework.security.oauth.boot</groupId>

  3.    <artifactId>spring-security-oauth2-autoconfigure</artifactId>

  4.    <version>2.0.0.RELEASE</version>

  5. </dependency>

此时,在如下配置文件下,我启动了应用(我同样把 application.properties改为了 application.yml):

 
   
   
 
  1. example:

  2.  baseUrl: http://localhost:8080

  3.  oauth2:

  4.    client:

  5.      grantType: client_credentials

  6.      clientId: {client-id-from-above}

  7.      clientSecret: {client-secret-from-above}

  8.      accessTokenUri: {issuer-uri-from-above}/v1/token

  9.      scope: custom_mod

我设置配置文件的命名空间为 example ,因为你可能会去连接不同的服务端。 我同时还定义了以下的属性:

  • baseUrl:我们示例服务端的主页

  • grantType:连接通信的授权模式

  • clientId和 clientSecret:和上面授权服务器一致的账号密码

  • accessTokenUri:定义用来获取授权的URI

  • scope:我们在上面创建的用户权限范围

最后是我们的 ClientApplication(原先为 DemoApplication,更改为 ClientApplication

 
   
   
 
  1. @SpringBootApplication

  2. public class ClientApplication implements CommandLineRunner {

  3.    private final Logger logger = LoggerFactory.getLogger(ClientApplication.class);

  4.    @Value("#{ @environment['example.baseUrl'] }")

  5.    private String serverBaseUrl;

  6.    public static void main(String[] args) {

  7.        SpringApplication.run(ClientApplication.class, args);

  8.    }

  9.    @Bean

  10.    @ConfigurationProperties("example.oauth2.client")

  11.    protected ClientCredentialsResourceDetails oAuthDetails() {

  12.        return new ClientCredentialsResourceDetails();

  13.    }

  14.    @Bean

  15.    protected RestTemplate restTemplate() {

  16.        return new OAuth2RestTemplate(oAuthDetails());

  17.    }

  18.    @Override

  19.    public void run(String... args) {

  20.        logger.info("MOD: {}", restTemplate().getForObject(serverBaseUrl + "/mod", String.class));

  21.    }

  22. }

以上代码我有以下几点说明以下:

  • CommandLineRunner 接口提供了一个 run方法,当初始化后,它将自动执行,执行完这个方法,应用程序退出。

  • 我创建了一个 ClientCredentialsResourceDetails的bean,它绑定了我的配置文件中的前缀: example.oauth2.client

  • 我使用OAuth2RestTemplate替代标准的RestTemplate。它将自动管理所有的OAuth2.0的token交换,并设置身份认证:设置请求头的值。总的来说,它处理了所有的OAuth认证的细节,所以你不用担心里面任意一点。

下一步,执行以下命令 ./mvnw spring-boot:run启动这个应用,你应该可以看到相似的一条输出:

 
   
   
 
  1. 2018-03-20 12:56:10.058  INFO 15833 --- [main] c.e.c.ClientApplication: MOD: The message of the day is boring for user: 0oabcd12yz2EpHuis75s3

客户端成功的与服务端通信了!不算差是吧?仅仅几行代码,你就能够配置和运行一个OAuth2.0认证服务器,同样也创建了两个可以基于OAuth2.0客户端授权模式的Spring 应用(一个客户端一个服务端),它们可以安全的通信。

NOTE:如果你遇到了 401或者 500错误,再一次检查 application.yml是否配置正确。

额外知识-减少对授权服务器调用次数

第二个序列图看起来似乎比第一个更复杂点,即使考虑访问令牌的重试情况。token格式是不确定的,没有具体定义,这个格式由特定的授权服务器自己去实现。 在Okta中,我们使用签名JWTs,这意味着你能够在本地验证这些token,而不是每次收到请求后都要再一次请求授权服务器去验证。

我们已经实现了一些语言的库,以及一个Spring Boot starter ,用它们来帮助你解决token本地认证。

NOTE:在本文创作时, okta-spring-boot 仅仅基于 SpringBoot1.5.x。你可以从GitHub中获得一个例子。

学习更多OAuth 2.0和Okta

这篇文章中,我结识了OAuth2.0客户端授权方式,并且创建了一个小的demo来实践这个流程(有了Spring Boot的帮助,几行代码就解决了!)。如果你有任何问题,直接在下方(原文)评论或者在Twitter上@我(@briandemers)或者@OktaDev 。

获取更多关于OAuth2.0和Okta的信息,请访问以下资源:

  • What the Heck is OAuth?

  • OAuth.com

  • Secure your SPA with Spring Boot and OAuth

本文原文同样也已经发布在Okta开发者博客2018年4月2日:https://developer.okta.com/blog/2017/10/27/secure-spa-spring-boot-oauth

推荐:

上一篇:

关注公众号

以上是关于利用OAuth2.0来构建一个基于Spring Boot的安全的端到端通信应用的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 实战干货: 简单的认识 OAuth2.0 协议

基于PHP构建OAuth 2.0 认证平台

Spring Cloud Oauth2 初探

基于Spring oauth2.0统一认证登录,返回自定义用户信息

pig 2.0 发布,基于 OAuth2.0 的 Spring Cloud 权限管理系统

使用 oAuth2.0 和表单登录的 Spring Security