网络诞生 25 周年快乐!或者:通过拥抱网络来致敬

工程 | Josh Long | 2014 年 3 月 13 日 | ...

昨天,即 2014 年 3 月 12 日,是 蒂姆 伯纳斯-爵士 发明 网络 25 周年。正如他所解释的:“我只需要把超文本思想和传输控制协议以及域名系统思想连接起来——*当当!*——万维网就诞生了。” *真是小事一桩*。(哈!)

图示(图片来自维基媒体)是蒂姆·伯纳斯-李爵士在其上组装初始 HTTP 服务和客户端的原始 NeXT 工作站(其操作系统 NeXT Step 奠定了今天 OS X 和 iOS 设计的基础)。

顺便说一句:我一直想知道能在那个实验室里是怎样的感觉。或许不小心绊倒摔倒并拔掉他的机器插头,(从而)让整个网络瘫痪——哈!想象一下,如果今天你能把网络关闭一小时,你的效率会高多少!:)

HTTP 在 最初的几个版本 设计上就有很多 正确 的地方。这是一个相当简单的协议实现。它是无状态的,这使得服务易于分区。到 1996 年的 1.0 版本,它支持查询、写入数据和一些基本的客户端/服务器协商。它使用头来注释请求和响应,提供了一种内置机制,让客户端和服务能够*丰富*传输的有效负载。HTTP 的构建是为了供使用不同编码的不同客户端使用。它的设计受益于缓存和代理等网络元素。HTTP 1.1(正式)内容无关,并且有一个内容类型的概念(包括 AcceptContent-Type 头)。

Roy Fielding(他也参与了 HTTP 1.1 的工作)为加州大学欧文分校撰写了引入 REST 架构约束的著名博士论文时,平台就已经准备好了!REST 正式化了 HTTP 中已有的许多优秀思想。它将 HTTP 动词暗示的状态变化映射到数据的生命周期。可以将 REST 视为 *HTTP:面向服务的部分*。

HTTP 带我们走过了很长的路。REST 也是一个游戏规则改变者。REST 推动了移动革命,它驱动了 RIA 革命,而且——我认为——它为大众实现了迄今为止如同独角兽般难以捉摸的 SOA

Spring 的 HTTP 和 REST 支持概览

Spring 平台*渗透着* HTTP 和 REST。事实上,为 Spring framework 编写的第一行代码就是为了支持*网络编程*。Spring framework 为构建基于 HTTP 和 REST 的应用提供了非常丰富的技术栈。

在这篇文章中,我希望简要介绍一下 Spring 丰富的 REST 支持。

更智能的客户端

Spring framework 包含了 RestTemplate,它将常见的 HTTP 操作简化为单行代码,风格与熟悉的 JdbcTemplate 和新的 NIO-2 驱动的 AsyncRestTemplate 相似。根据文档:“它简化了与 HTTP 服务器的通信,并遵循 RESTful 原则。它处理 HTTP 连接,让应用代码提供 URL(可能包含模板变量)并提取结果。” 没错。

RestTemplate 支持拦截器模型,可用于插件安全、审计、会话处理等许多功能。特别是,它可以透明地处理基于 HTTP 的有状态交互,例如 OAuth

OAuth 是一种协议,允许从 Web、移动和桌面应用程序以简单标准的方式进行安全授权。它保护着无数的 Web 服务,包括 Facebook、Twitter、GitHub、LinkedIn、TripIt 和 Google 的各种 API。如果你想在 Web 上消费 Web 服务,OAuth 是难以忽视的,而多亏了 Spring Social ,你就不必(自己处理这些复杂性)了!

Building atop, or integrating with, another platform can give your application new reach.

Spring Social 是一个伞形项目。它在其基础上提供了构建 OAuth 服务客户端(使用 RestTemplate)的机制,然后在之上提供了各种 API 的类型安全的 Java 绑定,包括 Spring Social TwitterSpring Social FacebookSpring Social LinkedIn。还有*许多*其他(已知和未知)的第三方绑定,支持众多替代 API。

Spring Social 还进一步与 Spring MVC 应用程序紧密集成,为你处理 OAuth 认证的*流程*。Spring Social 使得消费安全的 OAuth API 变得极其简单,并支持其他常见用例,如*使用 Facebook 登录*。

Spring for Android 项目 为 Android 客户端提供了 RestTemplate。你还可以从 Android 使用 Spring Social 客户端,进一步扩大你的应用范围。

你可以找到许多*入门指南*,详细介绍了如何消费 REST 服务(以及其他内容!),不仅使用 RestTemplate,还包括 从 Android、流行的 JavaScript 客户端等 进行消费。此外,还有一些方便的入门指南,介绍了如何使用 Spring Social TwitterFacebook 等等。

宽广、可扩展、安全的网络

Spring MVC 作为 Spring framework 的一部分发布。Spring MVC 为构建基于 HTTP 和 REST 的应用程序提供了非常丰富的基础。REST 正如我之前提到的,是一种架构约束,*而不是*一个严格的标准。相反,它是一套指导方针。这既是优点也是长处。REST 可能是一个不断变化的目标。Leonard Richardson 博士设计了所谓的 Richardson 成熟度模型,该模型本身提供了一个矩阵,你可以对照它来 判断给定的 REST API 是否符合 RESTful 原则

使用 Spring MVC 的核心 REST 支持很容易达到 Level 2。要达到下一级,即 Level 3,需要对 超媒体 有一些了解,或者说 HTTP 资源通过*链接*相互连接的能力,以及 HATEOAS。来自维基百科关于 HATEOAS 的页面:“REST 客户端通过一个简单的固定 URL 进入 REST 应用程序。客户端将来可能采取的所有操作都在服务器返回的资源表示中被发现。用于这些表示的媒体类型以及它们可能包含的链接关系是标准化的。客户端通过从表示中的链接中选择或以其媒体类型允许的其他方式操作表示来转换应用程序状态。通过这种方式,RESTful 交互由超媒体驱动,而不是带外信息。”

有许多很棒的 构建 REST Web 服务以及 保护它们 的指南。当然,还有 许多 其他 优秀 的示例。

Spring HATEOAS 是 Spring MVC 之上的一个层,它允许你基于*资源*进行工作,这些资源本身具有有效负载以及分配给它们的一系列*链接*。这些链接表示与它们所附属资源*相关*的 URI 表。

使用 Spring HATEOAS 和 Spring MVC 工作非常简单(除非完全不写代码!)。但仔细想想,几乎不写代码也相当不错…… 如果你考虑一下,REST 资源是数据的 HTTP 接口。它们负责将资源所代表的数据在其生命周期中进行移动,从创建、读取、更新到删除。如果你曾经使用过 Spring Data 的 Repository 支持,那么你可能就知道我要说什么了。Spring Data REST 允许你以声明式方式将你的 Spring Data Repository(本身可以像定义一个继承另一个接口的接口一样简单!)作为 REST 端点暴露出来。不错吧!

最后但同样重要的是,受人尊敬的、开源且易于使用的 Apache Tomcat Web 服务器驱动着大量的 Java 支持的 Web 应用。Apache HTTPD 则驱动着更多,而且

那么,安全性呢?

一个开放的 Web 应用程序或 REST API 就像一个完全开放、暴露的数据库;它*可能*不是你想要的。*保护 Web 应用程序的安全*对不同的人意味着许多不同的事情:客户端和服务器之间的连接是否加密?应用程序知道谁在发出请求吗?我们是在保护 Web 应用程序?还是 REST 服务?或者消息通道?幸运的是,答案可以只有一个:Spring Security。Spring Security 是你的一站式安全商店,它集成了后端身份提供商(LDAP、Active Directory)、认证和授权、加密以及对应用程序登录和登出等常见功能的支持。

例如,你可以使用 Spring Security OAuth 来保护你的 REST 端点,当然你随后就可以从 Spring Social 消费这个端点!

Boot 的魅力所在

Spring Boot 为所有这些技术提供了一个理想的入口。使用 Spring Boot 部署的应用程序开箱即用,带有管理和指标功能。上述大多数入门指南都基于 Spring Boot。Spring Boot 在 REST 开发方面有很多值得推荐的地方,但其中一个衡量标准肯定 是它有多容易发推文!。当然,另一个是 Spring Boot 使构建 Spring 应用程序易如反掌,并且它包含了各种各样的功能,旨在以一致的方式简化生产部署的任务。

以下代码是 稍加修改David Syer 博士的*史诗级*示例,该示例演示了如何使用 Spring Boot 和 Spring Security OAuth 来保护 REST 端点。我所做的只是添加了一个由 Spring Data REST 支持的 JPA Repository 和一些初始数据(参见 Repository)。Spring Data REST 管理着数据库(默认的嵌入式 H2 javax.sql.DataSource,尽管我们也可以轻松地使用任何其他 DataSource,或者实际上,完全使用其他一些后端 NoSQL 存储中的任何一个!

package demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.OAuth2ResourceServerConfigurer;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

// standard @Configuration class 
@Configuration
@ComponentScan
@EnableAutoConfiguration
@Import(RepositoryRestMvcConfiguration.class) // import Spring Data REST
public class Application {

    public static final String CRM_RESOURCE_ID = "crm";
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
	
	    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration
    	 extends ResourceServerConfigurerAdapter {

		// tell SS OAuth which URI paths to secure (optional!)
        @Override
        public void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                    .requestMatchers().antMatchers("/*", "/admin/beans").and()
                    .authorizeRequests()
                    .anyRequest().access("#oauth2.hasScope('read')");
            // @formatter:on
        }
		
        @Override
        public void configure(OAuth2ResourceServerConfigurer resources) throws Exception {
            resources.resourceId(CRM_RESOURCE_ID);
        }

    }

    @Configuration
    @EnableAuthorizationServer
    protected static class OAuth2Configuration
    		 extends AuthorizationServerConfigurerAdapter {
		
        @Autowired
        private AuthenticationManager authenticationManager;

		// plugin a Spring Security AuthenticationManager. One is created for us by Boot,
		// though we could plugin to *any* Identity Provider (LDAP, ActiveDirectory, etc)
        @Override
        public void configure(OAuth2AuthorizationServerConfigurer oauthServer) throws Exception {
            oauthServer.authenticationManager(authenticationManager);
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // @formatter:off
            
			// we could describe multiple clients with varying settings
            clients.inMemory()    
            		 .withClient("ios-crm")  // describe how our iOS client should connect
                    .authorizedGrantTypes("client_credentials", "password")
                    .authorities("ROLE_CLIENT")
                    .scopes("read")
                    .resourceIds(CRM_RESOURCE_ID)
                    .secret("secret");
            // @formatter:on
        }

    }
}


// a Spring Data JPA repository that's 
// exposed as a REST endpoint using Spring Data REST
@RepositoryRestResource
interface CustomerRepository extends JpaRepository<Customer, Long> {
}

// JPA entity.
@Entity
class Customer {

    @Id
    @GeneratedValue
    private Long id;
    private String firstName, lastName;


    Customer() {
    } // for JPA

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getLastName() {
        return lastName;
    }

    public Long getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }
}

不错吧!

下一个 25 年

长期来看,很难猜测会发生什么。不过,短期内有一些有趣的发展值得关注。CA Technologies 旗下的 Layer 7 刚刚发布了一项专注于 API 设计和部署的调查结果。这项调查有很多值得关注的地方,务必阅读,但其中一个我特别看好的方面是:API 开发者中超媒体感知 API 的预测增长。Java 8 即将发布,将带来 显著的 SSL/TLS 改进,进一步增强其在开放网络上的安全性。Apache Tomcat 8 也即将推出。

许多技术难题已经解决,网络已成为日常生活的一部分。对于许多开发者来说,问题不再是 REST 是否是架构的一部分,而是如何快速构建或管理 REST 服务。微服务,例如,就是一种架构风格,倾向于松耦合、单一关注、微小的 REST 服务。

在 Spring 中,我们拥有一套丰富的工具集,可以构建在令人惊叹、无处不在、持续增长的网络平台之上。未来的 25 年必将更加精彩。

订阅 Spring 新闻通讯

保持与 Spring 新闻通讯的连接

订阅

保持领先

VMware 提供培训和认证,助力你快速提升。

了解更多

获取支持

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

了解更多

近期活动

查看 Spring 社区的所有近期活动。

查看全部