JHipster技术栈定制 - 基于UAA的微服务之间安全调用

本文通过代码实例演示如何通过UAA实现微服务之间的安全调用。

uaa: 身份认证服务,同时也作为被调用的资源服务。服务端口9999。

microservice1: 调用uaa的消费者服务,服务端口8081。

1 准备工作

1.1 工程目录

--| appstack
  |-- uaa
  |-- microservice1

1.2 启动相关组件

为了简单起见,这里都使用容器启动相关组件,需要2个镜像,最好提前下载好。

  • jhipster/jhipster-registry:v4.0.0
  • mysql:5
a, 启动一个Jhipster-Registry
$ docker container run --name registry-app -e JHIPSTER.SECURITY.AUTHENTICATION.JWT.SECRET=dkk20dldkf0209342334 -e SPRING.PROFILES.ACTIVE=dev -d -p 8761:8761 jhipster/jhipster-registry:v4.0.0
b, 启动2个MySql容器。
$ docker container run --name uaa-mysql --privileged -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -p 32900:3306 mysql:5
$ docker container run --name microservice1-mysql --privileged -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -p 32800:3306 mysql:5

1.3 生成微服务工程

3个微服务都是通过Jhipster生成。 工程代码生成完之后,根据上一节启动的组件的实际情况,修改微服务配置文件中Eureka和database相关的配置。

这里使用的Jhipster版本为5.1.0。具体生成和配置详情,可以参考这里

2 核心代码

2.1 uaa源码

在uaa里面新增一个controller类,提供一个GET方法,作为被调用的API。

$ vi com.mycompany.appstack.web.rest.Provider
# 这里提供一个简单的GET API

package com.mycompany.appstack.web.rest;

import org.springframework.web.bind.annotation.*;

/**
 * REST controller for managing the current user‘s account.
 */
@RestController
@RequestMapping("/api")
public class ProviderResource {

    public ProviderResource () {
    }

    /**
     * GET  /provider:
     */
    @GetMapping("/provider")
    public String provider() {
        return "Hello, I‘m uaa provider.";
    }

}

2.2 microservice源码

a, 用于服务间调用的FeignClient注解类。

com.mycompany.appstack.config.client.AuthorizedFeignClient

生成的代码中,这个类是默认存在的,不需要修改,除非你要修改这个默认的配置类名。

Class<?>[] configuration() default OAuth2InterceptedFeignConfiguration.class;
b, 将自定义OAuth2拦截器类注册到当前服务中的配置类。

com.mycompany.appstack.client.OAuth2InterceptedFeignConfiguration

生成的代码中,这个类是默认存在的,需要修改如下:

package com.mycompany.appstack.client;

import java.io.IOException;
import org.springframework.context.annotation.Bean;
import feign.RequestInterceptor;

public class OAuth2InterceptedFeignConfiguration {
    @Bean(name = "serviceFeignClientInterceptor")
    public RequestInterceptor getFeignClientInterceptor() throws IOException {
        return new ServiceFeignClientInterceptor();
    }
}
c, 自定义OAuth2拦截器类。

com.mycompany.appstack.client.ServiceFeignClientInterceptor

这是一个新增的类,内容如下:

package com.mycompany.appstack.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Component;

import com.mycompany.appstack.security.oauth2.ServiceTokenEndpointClient;

import feign.RequestInterceptor;
import feign.RequestTemplate;

@Component
public class ServiceFeignClientInterceptor implements RequestInterceptor {

    private final Logger log = LoggerFactory.getLogger(ServiceFeignClientInterceptor.class);

    private static final String AUTHORIZATION_HEADER = "Authorization";

    private static final String BEARER_TOKEN_TYPE = "Bearer";

    @Autowired
    private ServiceTokenEndpointClient serviceTokenEndpointClient ;

    @Override
    public void apply(RequestTemplate template) {

        OAuth2AccessToken oauthToken = serviceTokenEndpointClient .sendClentCredentialsGrant();
        if (oauthToken != null) {
            template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, oauthToken.getValue()));
        }

    }
}
d, 与UAA通讯的客户端接口,增加一个抽象方法。

com.mycompany.appstack.security.oauth2.OAuth2TokenEndpointClient

生成的代码中,这个类是默认存在的,需要增加如下方法:

    /**
     * Send a client grant to the token endpoint.
     *
     * @return
     */
    OAuth2AccessToken sendClentCredentialsGrant();
e, d的适配器类,增加对应的实现方法。

com.company.appstack.security.oauth2.OAuth2TokenEndpointClientAdapter

生成的代码中,这个类是默认存在的,需要增加如下方法:

   /**
     * Sends a credentials grant to the token endpoint.
     *
     * @return the access token.
     */
    @Override
    public OAuth2AccessToken sendClentCredentialsGrant() {
        HttpHeaders reqHeaders = new HttpHeaders();
        reqHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>();
        formParams.set("grant_type", "client_credentials");
        addAuthentication(reqHeaders, formParams);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(formParams, reqHeaders);
        log.debug("contacting OAuth2 token endpoint to authenticate internal service.");
        ResponseEntity<OAuth2AccessToken> responseEntity = restTemplate.postForEntity(getTokenEndpoint(), entity,
                OAuth2AccessToken.class);
        if (responseEntity.getStatusCode() != HttpStatus.OK) {
            log.debug("failed to authenticate user with OAuth2 token endpoint, status: {}",
                    responseEntity.getStatusCodeValue());
            throw new HttpClientErrorException(responseEntity.getStatusCode());
        }
        OAuth2AccessToken accessToken = responseEntity.getBody();
        return accessToken;
    }

    protected String getJhipsterClientSecret() {
        String clientSecret = jHipsterProperties.getSecurity().getClientAuthorization().getClientSecret();
        if (clientSecret == null) {
            throw new InvalidClientException("no client-secret configured in application properties");
        }
        return clientSecret;
    }

    protected String getJhipsterClientId() {
        String clientId = jHipsterProperties.getSecurity().getClientAuthorization().getClientId();
        if (clientId == null) {
            throw new InvalidClientException("no client-id configured in application properties");
        }
        return clientId;
    }
f, e的实现类,增加对应的实现方法。

com.mycompany.appstack.security.oauth2.ServiceTokenEndpointClient

这是一个新增的类,内容如下:

package com.mycompany.appstack.security.oauth2;

import com.mycompany.appstack.config.oauth2.OAuth2Properties;
import io.github.jhipster.config.JHipsterProperties;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;

/**
 * Client talking to UAA‘s token endpoint to do different OAuth2 grants.
 */
@Component
public class ServiceTokenEndpointClient extends OAuth2TokenEndpointClientAdapter implements OAuth2TokenEndpointClient {

    public ServiceTokenEndpointClient(@Qualifier("loadBalancedRestTemplate") RestTemplate restTemplate,
                                  JHipsterProperties jHipsterProperties, OAuth2Properties oAuth2Properties) {
        super(restTemplate, jHipsterProperties, oAuth2Properties);
    }

    @Override
    protected void addAuthentication(HttpHeaders reqHeaders, MultiValueMap<String, String> formParams) {
        reqHeaders.add("Authorization", getAuthorizationHeader());
    }

    /**
     * @return a Basic authorization header to be used to talk to UAA.
     */
    protected String getAuthorizationHeader() {
        String clientId = getJhipsterClientId();
        String clientSecret = getJhipsterClientSecret();
        String authorization = clientId + ":" + clientSecret;
        return "Basic " + Base64Utils.encodeToString(authorization.getBytes(StandardCharsets.UTF_8));
    }

}
g, 调用uaa服务的Feign客户端类

com.mycompany.appstack.client.feign.BaseUaaAuthFeignClient

这是一个新增的类,内容如下:


package com.mycompany.appstack.client.feign;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.mycompany.appstack.client.AuthorizedFeignClient;

@AuthorizedFeignClient(name = "uaa", fallback = CallUaaAuthFeignClientHystrix.class)
public interface CallUaaAuthFeignClient {

    @RequestMapping(value = "/api/provider", method = RequestMethod.GET)
    String callProvider();
}
h, g类的断路器类

com.mycompany.appstack.client.feign.CallUaaAuthFeignClientHystrix

这是一个新增的类,内容如下:

package com.mycompany.appstack.client.feign;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class CallUaaAuthFeignClientHystrix implements CallUaaAuthFeignClient {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public String callProvider() {
        log.error("调用uaa provider接口失败!");
        return "调用uaa provider接口失败!";
    }

}

2.3 microservice1配置文件

application.yml
# 防止第一次初始化restTemplate时超时
hystrix:
    share-security-context: true
    command:
        default:
            execution:
                isolation:
                    thread:
                        timeoutInMilliseconds: 10000
application-dev.yml
jhipster:
    security:
        client-authorization:
            access-token-uri: http://uaa/oauth/token   // 从uaa获取token的uri
            token-service-id: uaa
            client-id: internal             // 和uaa的对应配置文件项保持一致
            client-secret: internal         // 和uaa的对应配置文件项保持一致

3 测试效果

3.1 通过UAA获取安全令牌的访问

a, 在microservice1中新增一个controller类

这个类提供一个测试API,我们通过浏览器访问这个API,间接调用CallUaaAuthFeignClient。

package com.mycompany.appstack.web.rest;

import com.mycompany.appstack.client.feign.CallUaaAuthFeignClient;
import com.mycompany.appstack.service.RoleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * REST controller for Test AuthFeignClient.
 */
@RestController
@RequestMapping("/test")
public class CallUaaResource {

    private final Logger log = LoggerFactory.getLogger(CallUaaResource.class);

    @Autowired
    private CallUaaAuthFeignClient callUaaAuthFeignClient;

    public CallUaaResource(RoleService roleService) {

    }   

    /**
     * GET  /servicecall :
     *
     * @return
     */
    @GetMapping("/servicecall")
    public String getProvider() {
        log.debug("REST request to get provider from uaa.");
        return callUaaAuthFeignClient.callProvider();
    }

}
b, 编译运行uaa,microservice1

如果一切正常,会看到Jhipster-Registry的Web UI中2个微服务已经注册成功。

c, 浏览器访问microservice1的测试API

http://localhost:8081/test/servicecall

可以看到uaa返回的结果:

说明microservice1从uaa获取token之后,成功访问了uaa的一个受限访问的API。

3.2 没有通过UAA获取安全令牌的访问

a, 注释掉从uaa获取安全令牌的代码

注释掉ServiceFeignClientInterceptor中的代码:

@Override
    public void apply(RequestTemplate template) {
        //OAuth2AccessToken oauthToken = uaaTokenEndpointServiceClient.sendClentCredentialsGrant();
        //if (oauthToken != null) {
            //template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, oauthToken.getValue()));
        //}

    }
b, 重新编译运行microservice1
c, 浏览器访问microservice1的测试API

http://localhost:8081/test/servicecall

可以看到返回错误信息:

查看microservice1的日志,报401错误:

org.springframework.web.client.HttpClientErrorException: 401 Unauthorized

说明microservice没有从uaa获取token,所以无法访问uaa的受限访问的API。

参考

完整源码

原文地址:https://www.cnblogs.com/yorkwu/p/9851946.html

时间: 12-06

JHipster技术栈定制 - 基于UAA的微服务之间安全调用的相关文章

用友iuap云运维平台支持基于K8s的微服务架构

什么是微服务架构? 微服务(MicroServices)架构是当前互联网业界的一个技术热点,业内各公司也都纷纷开展微服务化体系建设.微服务架构的本质,是用一些功能比较明确.业务比较精练的服务去解决更大.更实际的问题.该架构强调的一些准则:单一职责.协议轻量.进程隔离.数据分离.独立部署.按需伸缩. 什么是Kubernetes? Kubernetes是Google开源的容器集群管理系统,其提供应用部署.维护. 扩展机制等功能,利用Kubernetes能方便地管理跨机器运行容器化的应用,其主要功能:

云端基于Docker的微服务与持续交付实践

云端基于Docker的微服务与持续交付实践笔记,是基于易立老师在阿里巴巴首届在线技术峰会上<云端基于Docker的微服务与持续交付实践>总结而出的. 本次主要讲了什么? Docker Swarm Docker Swarm mode 微服务支持(Docker集群架构体系) Docker的发展趋势和前沿成果 在Docker技术方面还是很佩服大牛的,所以赶紧写下笔记,追随大神的脚步. 阿里云资深专家易立,技术就不说了,他比其他直播间硬生生多讲了半个多点,于情于理还是万分感谢本次分享的(可惜devOp

单体应用与微服务优缺点辨析

前久由于需要做一个异构系统集成的架构设计,所以深入研究了下微服务架构,今天由于家里断网(只能用手机热点)所以分享一篇OneNote里面摘录的文章. 微服务架构(MSA)是一种架构概念,旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦.你可以将其看作是在架构层次而非在具体代码上应用SOLID原则的设计原则.个人我认为微服务更多的是一种架构风格,也可以看作是一种粒度更细的SOA.在InfoQ上有很多介绍微服务架构的文章,今天要分享的是一篇对比单体应用和微服务的文章,所谓单体应用和微服务可以

基于微服务API级权限的技术架构

一般而言,企业内部一套成熟的权限系统,都是基于角色(Role)的 访问控制方法(RBAC – Role Based Access Control),即权限 (Permission)与角色相关联,用户(User)通过成为适当角色的成员而得到这 些角色的权限,权限包含资源(或者与操作组合方式相结合),最终实现权限控制 的目的. 背景 权限系统是根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源. 一般而言,企业内部一套成熟的权限系统,都是基于角色(Role)的访问控制方法(

小程聊微服务-基于dubbo的mock测试系统

一.说在前面 基于微服务或者SOA的自动化测试系统每个公司都有自己的特有的,我今天就主要介绍一下,我们研发的一套mock测试系统. 二.目前面临的问题 1.测试人员面临的测试问题 我公司目前用的是基于Dubbo的微服务改造,服务之间的调用链路冗长,每个服务又是单独的团队在维护,每个团队又在不断的演进和维护各个服务,那么对测试人员将是非常大的挑战. 测试人员每次进行功能测试的时候,测试用例每次都需要重新写一遍,无法将测试用例的数据沉淀,尤其是做自动化测试的时候,测试人员准备测试数据就需要很长时间,

《2016ThoughtWorks技术雷达峰会----微服务架构》

微服务架构   王键,ThoughtWorks, 首席咨询师 首先微服务架构的定义,thoughtWorks在2012年3月的技术雷达中这样定义: “微服务架构是一种架构,它提倡将单一应用程序划分为一组小的服务,每个服务运行在其独立的进程中,服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API).每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境.类生产环境等.” 从这个定义中可以知道,如何甄别一个一个架构是不是微服务架构,可以从2点来判断. 一个是服务

基于docker部署的微服务架构(四): 配置中心

原文:http://www.jianshu.com/p/b17d65934b58%20 前言 在微服务架构中,由于服务数量众多,如果使用传统的配置文件管理方式,配置文件分散在各个项目中,不易于集中管理和维护.在 spring cloud 中使用 config-server 集中管理配置文件,可以使用 git.svn.本地资源目录 来管理配置文件,在集成了 spring cloud bus 之后还可以通过一条 post 请求,让所有连接到消息总线的服务,重新从config-server 拉取配置文

Re:从 0 开始的微服务架构--(四)如何保障微服务架构下的数据一致性--转

原文地址:http://mp.weixin.qq.com/s/eXvoJew3bjFKzLLJpS0Otg 随着微服务架构的推广,越来越多的公司采用微服务架构来构建自己的业务平台.就像前边的文章说的,微服务架构为业务开发带来了诸多好处的同时,例如单一职责.独立开发部署.功能复用和系统容错等等,也带来一些问题. 例如上手难度变大,运维变得更复杂,模块之间的依赖关系更复杂,数据一致性难以保证,等等.但是办法总是比问题多,本篇文章就来介绍一下我们是如何保障微服务架构的数据一致性的. 微服务架构的数据一

怎么用API网关构建微服务

选择将应用程序构建为微服务时,需要确定应用程序客户端如何与微服务交互.在单体应用程序中,只有一组端点.而在微服务架构中,每个微服务都会暴露一组通常是细粒度的端点.在本文中,我们将讨论一下这对客户端与应用程序之间的通信有什么影响,并提出一种使用API网关的方法. 当选择将应用程序构建为一组微服务时,需要确定应用程序客户端如何与微服务交互.在单体应用程序中,只有一组(通常是重复的.负载均衡的)端点.然而,在微服务架构中,每个微服务都会暴露一组通常是细粒度的端点.在本文中,我们将讨论一下这对客户端与应

深入解析DC/OS 1.8 – 高可靠的微服务及大数据管理平台

深入解析DC/OS 1.8 – 高可靠的微服务及大数据管理平台 大家好,欢迎大家参加这次DC/OS的技术分享. 先做个自我介绍,刘超,Linker Networks首席架构师,Open DC/OS社区贡献者,长期专注于OpenStack, Docker, Mesos等开源软件的企业级应用与产品化. 从事容器方面工作的朋友可能已经听说过DC/OS,往往大家误解DC/OS就是marathon + mesos,其实DC/OS包含很多的组件,DC/OS 1.8九月份发布了,此次分享给大家做一个介绍. 一