在微服务的开发中,对于完备安全场景的需求会持续增长。为了满足这种需求,Boot引入了强大完整的Spring Security,并且提供了自动配置的功能,以快速简便地启用安全层。只需在应用的classpath中包含spring-boot-starter-security模块就能使Boot引入一些安全特性,如跨站脚本防护(cross-sitescripting protection)并且会添加头信息以防止点击劫持(click-jacking)。除此之外,添加一条简单的配置指令就能启用基本认证来保护你的应用,如程序清单1.21所示。
程序清单1.21
security: basic: enabled: true
Boot会为你提供一个默认的用户账号user和默认角色USER,并且会在应用启动的时候在控制台上输出随机生成的密码。就像Boot的其他功能那样,对于内置的user账号,我们可以很容易地指定不同的用户名和密码(分别为“secured”和“foo”),这需要通过明确定义的配置指令来实现,如程序清单1.22所示。
程序清单1.22
security: basic: enabled: true user: name: secured password: foo
对于简单的内部应用或开发原型来说,Boot内置的基础设施能够快速地在微服务中启用基本认证,这是非常有用的。随着需求的演化,你的应用毫无疑问会需要更细粒度的安全特性,如保护端点只能由特定的角色访问。从这个角度来看,我们可能希望具有USER角色的调用者只能读取数据(即GET请求),而对具有ADMIN角色的调用者可以读取和写入数据(即POST请求)。为了做到这一点,我们需要在工程的application.yml文件中禁用Boot的基本认证自动配置功能,并且定义我们自己的user和admin账号以及对应的角色。当你的需求超过Boot所提供的默认功能时,它通常很快就能实现,这可以作为佐证这一点的又一个例子。为了更具体地阐述这一点,考虑一下程序清单1.23中的代码。这个样例可以阐述如何发挥Spring Security所有潜在的功能以及更为复杂的认证策略,如基于JDBC后端、OpenID或单点登录(Single-Sign On)。
程序清单1.23
@RestController @RequestMapping("/user") @Configuration @EnableGlobalMethodSecurity(securedEnabled = true) @EnableAutoConfiguration class UserController extends WebSecurityConfigurerAdapter{ @Autowired UserRepositoryrepository @RequestMapping(method = [GET]) @Secured(['ROLE_USER']) def get(Long id){ id ?repository.findOne(id) : repository.findAll() } @RequestMapping(method = [POST]) @Secured(['ROLE_ADMIN']) defcreate(@RequestBody User user) { repository.saveuser user } @Override voidconfigure(AuthenticationManagerBuilder auth) { auth .inMemoryAuthentication() .withUser"user" password "password" roles "USER" and()withUser "admin" password "password" roles"USER", "ADMIN" } @Override voidconfigure(HttpSecurity http) throws Exception { BasicAuthenticationEntryPoint entryPoint = newBasicAuthenticationEntryPoint() entryPoint.realmName = "Spring Boot" http.exceptionHandling().authenticationEntryPoint(entryPoint) http.requestMatchers().antMatchers("/**").anyRequest() .and().httpBasic().and().anonymous().disable().csrf().disable() } public staticvoid main(String[] args) { SpringApplication.run UserController, args } }
在程序清单1.23的样例之中,应用现在被明确地配置为要基于user和admin用户账号进行访问,它们的密码都是password,具有的角色分别是USER和ADMIN。微服务的GET和POST端点分别通过USER和ADMIN角色进行保护,这就意味着普通用户可以访问只读的数据,而执行读取-写入操作的话,需要admin用户凭证。
对于微服务来说,基本认证是很好的一个选择,因为它遵循了很实用且广泛使用的认证协议。换句话说,很多的API调用者,包括移动应用,能够很容易地使用这一点来访问你的微服务。当你的认证需求超过了基本认证的功能时(如OpenID或OAuth),微服务可以使用Spring Security的全部功能来满足你的需求。
在任何的应用中,消息(messaging)都是一种很强大的工具,在一点上,微服务当然也不能例外。使用消息驱动架构开发的应用能够更好地支持可重用性和扩展性。Spring Boot能够让开发人员在编写微服务时将消息作为架构的核心组成部分,它使用到了Spring IO平台的企业集成模式(Enterprise Integration Patterns)实现,即Spring Integration。Spring Integration提供了开发消息驱动架构的基本结构,以及与分布式企业平台集成的模块。这种能力使得微服务可以使用来自抽象消息源的业务对象,这些消息源可以在应用内部,也可能是组织机构内部的其他服务所提供的。
尽管Boot并没有提供明确的Spring上下文自动化配置,但是它为Spring Integration提供了一个starter模块,它会负责引入Spring Integration项目的一系列依赖。这些依赖包括Spring Integration的核心库(Core library)、HTTP模块(用来进行面向HTTP的企业集成)、IP模块(用来进行基于Socket的集成操作)、File模块(用于进行文件系统集成)以及Stream模块(用于支持使用Stream的操作,如stdin和stdout)。这个starter模块为开发人员提供了健壮的消息功能的工具集,可以使已有的基础设施适应微服务API。
除了starter模块,Boot也为通过CLI构建的应用提供了编译器自动配置的功能。对于需要快速构建微服务原型并验证可行性的开发者来说,这种方式提供了一些捷径。使用企业级平台的应用可以快速地进行开发,在转移到正式的工程和构建系统之前,就能确认其价值。使用Spring Boot和Spring Integration使一个消息驱动的微服务运行起来非常简单,如程序清单1.24的样例代码所示。
程序清单1.24
@RestController @EnableIntegrationPatterns class App { @Bean defuserLookupChannel() { newDirectChannel() } @Bean defuserTemplate() { newMessagingTemplate(userLookupChannel()) } @RequestMapping(method=[RequestMethod.GET]) defget(@RequestParam(required=false) Long id) { userTemplate().convertSendAndReceive( id ? id : "") } } class User { Long id } @MessageEndpoint class UserLookupObject { @ServiceActivator(inputChannel="userLookupChannel") def get(Long id){ id ? newUser(id:id) : new User() } }