JHipster UAA
来源:JHipster |
时间:2018-11-04
|
|

JHipster UAA是一个使用OAuth2认证协议、为JHipster微服务提供用户账户和授权的服务。

应当清晰的了解JHipster UAA和其他的UAA(例如cloudfoundry UAA)。 JHipster UAA是一个完全配置的使用内置的user和roleOAuth2授权认证服务,被打包成一个普通的JHipster应用。这允许开发人员深入的配置他的user领域模型的每个部分,而不限制它访问UAA的策略。

架构图

这里写图片描述

微服务架构的安全要求

在深入的研究位于JHipster微服务上的OAuth2与应用之前,需要清楚地了解可靠的安全解决方案的要求

  1. 集中认证

大多数微服务都是独立自主的应用。因此希望有一个一致的认证体验而不想用户注意到他的请求来自于不同的安全配置的应用

  1. 无状态

最大的好处是构建的微服务可扩展,因此选择的安全解决方案不能影响扩展性。在服务器上保持用户会话状态较为棘手,因此一个无状态的解决方案成为首选.

  1. 用户/机器访问区别

需要清除的区分不同的用户和不同的机器。使用微服务架构可以构建一个大型的不同领域的数据资源中心,因此需要限制不同的客户端,例如本地的应用、多个SPAs等等的访问.

  1. 细粒度的访问控制

在维护集中的角色时,需要在每个微服务中配置详细的访问控制策略。微服务不应该起识别用户的责任,并且每个进入的请求都经过了授权.

  1. 免受攻击

无论一个安全解决方案能够解决多少问题,它都应该尽可能强大

  1. 可扩展

使用无状态协议并不是安全解决方案的保证。最后,不应该有单点问题。一个反例是单节点共享的授权数据库或者单节点的授权服务实例。

理解在此场景下的OAuth2

使用OAuth2**协议**能够满足以上6点要求。它遵循严格的标准,使得它可以与所有的微服务或者其他的远程系统相容。JHipster基于此提供了一些解决方案:

JHipster OAuth2通信流程

  • 请求架构中的任意端点都由一个“client”执行

  • “client”是一个对于客户端的抽象,它可以是”angular $http_client”,可以是REST-Client,也可以是curl以及一切可以发起请求的事物

  • 在端点(包括UAA)上提供服务的每个微服务都是资源服务器

  • 蓝色的箭头显示客户在Oauth授权服务器上的身份认证

  • 绿色箭头显示客户端对资源服务器的请求

  • UAA服务是授权服务和资源服务的组合

  • UAA服务是微服务的数据提供者(它批准自动访问资源服务器)

  • 客户端使用用户身份认证信息访问资源,认证使用密码授权,而授权的信息clientId和密钥都安全的存储在网关的配置文件中

  • 客户端不使用用户身份访问资源,就需要通过客户端认证授权

  • 刻个客户端都在UAA内部定义

该设计可以应用于任何独立的语言或者框架的微服务架构体系

另外,以下规则也可以用于访问控制:

  • 用户访问可以被配置为使用rolesRBAC

  • 机器访问控制可以被配置文scopes和RBAC

  • 复杂访问配置可以使用ABAC,通过boolean表达式处理rolesscopes 

    • 例子:hasRole(“ADMIN”) and hasScope(“shop-manager.read”, “shop-manager.write”)

使用UAA

在生成Jhipster微服务脚手架的时候,你可以选择使用UAA选项代替JWT认证

**注意**UAA解决方案也使用了JWT,它可以使用spring-cloud-security自定义JWT

基本设置

最基本的设置包括:

  1. 一个JHipster UAA服务

  2. 至少一个其他的微服务(使用UAA认证)

  3. 一个JHipster网关(使用UAA认证)

这是生成它的顺序。

除了提供身份验证类型之外还需要提供UAA的位置

这个设置的工作方式与JWT类型相同,但会有一个服务

理解组件

Jhipster UAA 服务做了三件事:

  • 提供了默认的用户领域模型,包含user和account资源

  • 它实现了AuthorizationServerConfigurerAdapter定义了基本的客户端(“web_app”和”internal”)

  • 它提供了JWT的公钥在/oauth/token_key,将会被其他的微服务消费

选择数据库、缓存方案、搜索引擎、构建工具以及更多的选项都开放给开发人员。

当一个微服务启动时,通常UAA已经启动并开始共享公钥。服务首先会调用/oauth/token_key获取公钥并使用此密钥配置签名(JwtAccessTokenConverter)

如果UAA没有启动,应用将会继续启动并在稍后获取公钥。有两个属性可以空多久(uaa.signature-verification.ttl)获取一次公钥和限制请求次数(uaa.signature-verification.public-key-refresh-rate-limit)避免频繁请求。这些值通常有默认值。在任何情况下,如果检查失败,微服务将会检查是否有新的公钥。这样,公钥可以被替换而服务一直可用.

从这点来看可能有两个用例使用这个基本设置:用户调用和机器调用(应用调用)

对于用户调用,登录请求发送至网关的/auth/login端点。这个端点使用OAuth2TokenEndpointClientAdapter发送请求到UAA进行密码去身份验证。因为这个请求发生在网关上,所以clientId和密钥在客户端没有存储而用户无法访问到。网关将返回一个包含令牌的Cookie,在客户端后续的请求中这个cookie将随着客户端请求发送至jhipster后端。

对于机器(应用)调用,该应用需要通过客户端证书使用UAA进行身份验证。JHipster提供了一个标准的解决方案,后续有详细描述。

Refresh Tokens

刷新访问令牌通常发生在网关上,如下所示:

  • 身份验证是通过AuthResource调用OAuth2AuthenticationService的认证(设置cookies)

  • 对于每个请求,RefreshTokenFilter(在RefreshTokenFilterConfigurer配置)会检查访问令牌是否有效、是否过期

  • 如果过期或者失效了,将会触发刷新令牌的流程,通过OAuth2AuthenticationService刷新令牌

  • 使用OAuth2TokenEndpointClient接口发送请求刷新令牌授权到OAuth2服务器,在我们的案例中是UAA(通过UaaTokenEndpointClient

  • 刷新的结果将被以新cookie传递到下游,并将上游(浏览器)设置为新cookie

常见的误区

下面是一个开发人员应该知道的事情的简要列表.

生产和演示环境使用相同的签名密钥

非常严肃的推荐大家尽可能的使用不同的密钥。一旦一个签名密钥落入一个错误的人手中,就可能在不知道任何用户凭据的情况下获取完全的访问授权密钥.

不使用TLS

如果攻击者设法截取到令牌,他将获得该令牌的所有权限,知道令牌过期。可以有很多种方式实现这一点,特别是再没有TLS加密的情况下。这在OAuth1的时候是不会出现的因为协议要求强制加密.

在url中使用访问令牌

根据标准规范,访问令牌可以在url、header或者cookie中。从TLS的观点来看,这三种方法都是安全,实际上URL的安全性较低,有很多种方式从url记录中获取。

选在对称加密签名

JWT签名并不一定需要RSA,Spring Security也提供了对称的令牌签名。这虽然解决了一些问题,但使得开发困难。这也是不安全的,攻击者只需要获取一个节点的微服务就可以生成自己的JWT令牌。

使用Feign进行安全的跨服务通信

目前,只有JHipster UAA提供了一种可伸缩的安全的跨服务通信方法。

使用JWT身份验证无需手动将JWT请求转发至内部以强制微服务之间通过网关互相调用(主请求的内部请求)。但即使自动转发,也不能将用户和应用的身份验证分离。

由于JHipster使用了OAuth2,所有的问题在这个协议上都得到了解决。

这个章节讲述了如何轻松的使用它。

使用Eureka, Ribbon, Hystrix以及Feign

当一个服务想要请求其他服务的数据时,会有4个组件参与进来。因此了解每个的职责是非常重要的:

  • Eureka: 注册服务,你从这里可以获取某个服务注册了的所有实例IP

  • Ribbon: 当有人请求服务时,根据服务的IP集合进行负载均衡

综上所述,当我们需要访问一个URLhttp://uaa/oauth/token/(此URL由运行在10.10.10.1:999910.10.10.2:9999上的两个实例提供服务),我们可以使用EUreka和Ribbon轮询算法快速的转换为http://10.10.10.1:9999/oauth/token或者http://10.10.10.2:9999/oauth/token

  • Hystrix: 一个在服务失败时的熔断器系统

  • Feign: 使用所有的声明式风格

在现实场景中,所有的服务实例都有可能出现问题。因此Hystrix作为一个断路器,以一种快速失败的方式来处理调用失败的场景

但是手工编码这些东西工作量巨大。而Feign提供使用一些带注解的接口为访问注册到Eureka端点的带负载均衡的REST客户端选项,并使用Hystrix实现了回退。

所以,对于内部通信,Feign是非常有用的。当一个服务需要通过REST客户端访问另一个服务”othser-service”时,可能的接口定义如:

@FeignClient(name = "other-service")
interface OtherServiceClient {  @RequestMapping(value = "/api/other-resources")
  List<OtherResource> getResourcesFromOtherService();
}123456

接下来通过依赖注入进行使用:

@Serviceclass SomeService {  private OtherServiceClient otherServiceClient;  @Inject
  public SomeService(OtherServiceClient otherServiceClient) {    this.otherServiceClient = otherServiceClient;
  }
}123456789

类似于Spring Data JPA,这里也不需要实现任何的接口。当然如果你使用Hystrix时你也可以这样做。Feign接口的实现类作为回退的实现。

一个存在的问题是使用UAA保障通信安全。为了解决这个,有一些用于Feign的拦截器,它实现了来自于OAuth的客户端认证,以授权当前服务请求其他服务。在Jhipster中,你只需要使用@AuthorizedFeignClients即可。这注解是由JHipster提供的,它确实做到了这一点。

使用@AuthorizedFeignClients

考虑到上边的”other-service“服务的资源是受保护的,这个接口的注解如下:

@AuthorizedFeignClient(name = "other-service")
interface OtherServiceClient {  @RequestMapping(value = "/api/other-resources")
  List<OtherResource> getResourcesFromOtherService();
}123456

当没有优点的令牌在内存中的时候,REST客户端会自动的从UAA获取认证。

测试UAA应用

模拟Feign客户端

与Feign一起工作的组件都是可测试的。在测试中使用Feign和生产中使用的方式一样,将迫使Jhipster Registry与UAA运行在同一台机器(测试用例执行的机器)。大多数场景中,你不需要测试Feign自己,但是使用Feign客户端的组件需要。

要测试内部使用Feign的组件可以使用@MockBean(自spring-boot 1.4.0引入)

这有一个例子,测试SomeService使用模拟数据的客户端:

@RunWith(SpringRunner.class)@SpringBootTest(App.class)public class SomeServiceTest {

    @MockBean
    private OtherServiceClient otherServiceClient;    @Inject
    private SomeService someService;    @Test
    public void testSomeService() {
        given(otherServiceClient.getResourcesFromOtherService())
        .willReturn(Arrays.asList(new OtherResource(...));

        someService.performActionWhichInkvokesTheAboveMentionedMethod();        //assert that your application is in the desired state
    }
}123456789101112131415161718192021

因此,通过这种技术,你可以模拟其他服务的行为并提供预期的资源实体。所有Bean都可以注入模拟的客户端,而你只要关注Bean中具体的逻辑。

模拟OAuth2认证

使用Spring对REST控制器的集成测试通常会绕过安全配置,因为它会使测试变得很困难,当唯一的意图是证明控制器在执行它应该做的事情时。但是有时候,测试控制器的安全性行为也是测试的一部分。

在这个用例中,JHipster提供了一个叫做OAuth2TokenMockUtil的组件,它可以模拟有效的认证而无需客户端或者用户存在。

要使用这个特性,有两件事情必须完成:

  1. 在模拟SpringMVC的环境中启用安全并注入模拟工具

 @Inject
    private OAuth2TokenMockUtil tokenUtil;    @PostConstruct
    public void setup() {        this.restMockMvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())
            .build();

    }1234567891011
  1. 使用OAuth2TokenMockUtil

这个工具提供另一个方法oaut2authentication,可使MockMvn的”with”操作可用。它而已被配置使用以下字段模拟认证:

  • username

  • roles(Set)

  • scope(Set)

例子:

@Test
public void testInsufficientRoles() {
    restMockMvc.peform(
        get("url/requiring/ADMIN/role")        .with(tokenUtil.oauth2Authentication("unpriveleged.user@example.com", Sets.newSet("some-scope"), Sets.newSet("ROLE_USER")))
    ).andExpect(status().isForbidden());}1234567


提交
查看更多评论
没有更多评论