Web 25 周年快乐!或:拥抱 Web,致敬 Web

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

2014 年 3 月 12 日,也就是昨天,是 互联网 25 周年纪念日 也是 蒂姆·伯纳斯-李爵士 发明万维网 日子。正如他所说:“我只需要将超文本的想法与传输控制协议和域名系统相结合,然后——瞧!——万维网就诞生了。” 真是 简单啊。(哈哈!)

图片(取自维基百科)展示了最初的 NeXT 工作站(其操作系统 NeXTSTEP 是今天 OS X 和 iOS 设计的基础),蒂姆·伯纳斯-李爵士就是在这台机器上构建了最初的 HTTP 服务和客户端。

顺便说一句:我一直很好奇,如果能回到那个实验室,会是什么感觉。如果不小心绊倒,把他的机器(以及整个网络)的电源线拔掉了,那该怎么办——哈哈!试想一下,如果今天能关闭网络一个小时,你的工作效率会提高多少! :)

HTTP 在最初的几个版本中就设计得非常出色 它是一个相当简单的协议,易于实现。它是无状态的,这使得服务分区变得容易。到 1996 年的 1.0 版本时,它已经支持查询和写入数据,以及一些基本的客户端/服务器协商。它使用头部来注释请求和响应,为客户端和服务提供了一种内置机制来丰富所传递的有效负载。HTTP 的设计初衷就是被各种编码的、不同的客户端使用。它旨在从缓存和代理等网络元素中获益。HTTP 1.1 在(形式上)是内容无关的,并具有内容类型概念(包括AcceptContent-Type头部)。

罗伊·菲尔丁(也参与了 HTTP 1.1 的开发)为加州大学欧文分校撰写著名的博士论文,引入 REST 架构约束时,构建平台已经存在了!REST 将 HTTP 中的一些好的想法形式化了。它将 HTTP 动词隐含的状态转换映射到数据生命周期。可以将 REST 理解为HTTP:面向服务的组成部分

HTTP 已经带我们走过了很长的路。REST 也改变了游戏规则。REST 推动了移动革命,推动了 RIA 革命,而且我认为它为大众实现了此前像独角兽一样的 SOA

Spring 的 HTTP 和 REST 支持概述

Spring 平台HTTP 和 REST 为核心。实际上,Spring 框架编写的首批代码就是为了支持Web 编程。Spring 框架提供了一个非常丰富的栈,用于构建基于 HTTP 和 REST 的应用程序。

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

更智能的客户端

Spring 框架包含RestTemplate,它可以将常见的 HTTP 操作简化为与熟悉的JdbcTemplate和新的基于 NIO-2 的AsyncRestTemplate相同的单行代码。文档中写道:“它简化了与 HTTP 服务器的通信,并强制执行 RESTful 原则。它处理 HTTP 连接,让应用程序代码提供 URL(可能包含模板变量)并提取结果。”没错。

RestTemplate支持拦截器模型,该模型可用于插入安全、审计、会话处理等功能。特别是,它可以透明地处理像OAuth这样的基于 HTTP 的有状态交换。

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,仅此而已。

广泛、可扩展、安全的 Web

Spring MVC 作为 Spring 框架的一部分进行分发。Spring MVC 为构建基于 HTTP 和 REST 的应用程序提供了非常丰富的基础。正如我之前提到的,REST 是一种架构约束,而不是一种严格的标准。相反,它提供了一些指导原则。这是一个优势和优点。REST 可能是一个不断变化的目标。莱昂纳德·理查森博士设计了所谓的理查森成熟度模型,该模型本身提供了一个矩阵,你可以根据该矩阵来判断给定的 REST API 与 RESTful 原则的符合程度

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

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

Spring HATEOAS 是 Spring MVC 之上的一个层,它允许你以资源为单位进行操作,这些资源本身具有有效负载和分配给它们的链接集合。这些链接表示与附加它们的资源相关的 URI 表。

使用 Spring HATEOAS 和 Spring MVC 非常简单(除了完全不编写任何代码之外!)。但是,当你仔细想想时,完全不编写代码也是非常棒的……如果你仔细想想,REST 资源是数据的 HTTP 接口。它们通过将资源表示的数据在其生命周期中移动来实现,从创建、读取、更新到删除。如果你曾经使用过 Spring Data 存储库支持,那么你可能已经看出我的意思了。Spring Data REST 允许你声明性地将 Spring Data 存储库(它们本身可以像定义扩展另一个接口的接口一样简单)公开为 REST 端点。不错!

最后但并非最不重要的是,受人尊敬的、开源的且易于使用的 Apache Tomcat Web 服务器驱动着大量的 Java 驱动的 Web。Apache HTTPD 驱动着更多的 Web,并且

那么,安全性如何呢?

一个开放的 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 存储库和一些种子数据(请参阅存储库)。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 改进,进一步增强其在开放 Web 上的安全特性。Apache Tomcat 8 即将问世。

许多技术难题已经得到解决,网络已经成为日常生活的一部分。对于许多开发人员来说,问题不在于 REST 是否是架构的一部分,而在于 REST 服务能够多快地建立或管理。微服务,例如,是一种倾向于松散耦合、专注于单一功能、小型 REST 服务的架构风格。

在 Spring 中,我们拥有丰富的工具集,可以构建基于网络这个惊人、无处不在且不断增长的平台。未来的 25 年必将令人惊叹。

获取 Spring 电子报

通过 Spring 电子报保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

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

了解更多

即将举行的活动

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

查看全部