本文通过代码实例演示如何通过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 APIpackage 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";@Autowiredprivate ServiceTokenEndpointClient serviceTokenEndpointClient ;@Overridepublic 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.*/@Overridepublic 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);}@Overrideprotected 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());@Overridepublic String callProvider() {log.error("调用uaa provider接口失败!");return "调用uaa provider接口失败!";}}

2.3 microservice1配置文件

application.yml
# 防止第一次初始化restTemplate时超时
hystrix:share-security-context: truecommand:default:execution:isolation:thread:timeoutInMilliseconds: 10000
application-dev.yml
jhipster:security:client-authorization:access-token-uri: http://uaa/oauth/token   // 从uaa获取token的uritoken-service-id: uaaclient-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);@Autowiredprivate 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中的代码:

@Overridepublic 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

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

  1. mysql 间接引用_微服务之间安全调用 by UAA

    本文通过代码实例演示如何通过UAA实现微服务之间的安全调用. uaa: 身份认证服务,同时也作为被调用的资源服务.服务端口9999. microservice1: 调用uaa的消费者服务,服务端口80 ...

  2. 从0到1学SpringCloud——08 通过fegin实现微服务之间请求调用

    目录 一.前言 1.关于Fegin 2.注意事项 3.POM依赖 二.编码实现 1.启动类 2.创建openfeign接口 3.Controller代码 4.回调工厂 三.文件配置 1.Feign接口 ...

  3. 从微服务应用于技术栈,了解华为云微服务应用

    摘要:一个成熟的微服务解决方案产品需要经历足够大的业务量侵袭,才能变得更加成熟和可靠. 云原生时代,随着容器技术.微服务架构思想.产品研发运营模式不断地推陈出新和迅速发展,应用的设计和开发落地门槛已经 ...

  4. Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用

    文章目录 Spring Cloud[Finchley]专栏 概述 HTTP方式之RestTemplate 方式一 (直接使用restTemplate访问URL,url写死) 方式二 (使用LoadBa ...

  5. feign调用多个服务_Spring Cloud 快速入门系列之feign–微服务之间的调用

    我们将一个大的应用拆成多个小的服务之后,紧接着的一个问题就是,原本都在一个项目里,方法我可以随便调用,但是拆开后,原来的方法就没法直接调用了,这时候要怎么办? Spring Cloud提供了feign ...

  6. Spring Cloud之微服务之间相互调用、如何让一个微服务调用另外一个微服务

    在使用微服务架构中,可能遇到一些业务情况会涉及服务之间相互调用,下面通过一个简单的demo给大家演示下,演示的是oms服务需要调用ump服务. 代码如下: 1.oms服务提供者 主要是这个注解: @E ...

  7. JHipster微服务之间的调用以及权限验证

    JHipster微服务(uaa.app.gateway),下图注册中心中app和app1均为微服务(MicroService)且gateway基于uaa. 服务调用: 如上图注册中心所示,多个微服务进 ...

  8. eureka自我保护时间_spring cloud中微服务之间的调用以及eureka的自我保护机制详解...

    上篇讲了spring cloud注册中心及客户端的注册,所以这篇主要讲一下服务和服务之间是怎样调用的 基于上一篇的搭建我又自己搭建了一个客户端微服务: 所以现在有两个微服务,我们所实现的就是微服务1和 ...

  9. feign调用service_微服务之间的调用(Ribbon与Feign)

    概述 在前面的文章中,我们讲了使用Eureka作为服务注册中心,在服务启动后,各个微服务会将自己注册到Eureka server.那么服务之间是如何调用?又是如何进行负载均衡的呢?本文讲讲服务之间调用 ...

最新文章

  1. 一个隐马尔科夫模型的应用实例:中文分词
  2. Java学习从入门到精通的学习建议
  3. Windows Phone开发:常用控件(上)
  4. 怎么看rabbitmq的浏览器信息_没用过消息队列?一文带你体验RabbitMQ收发消息
  5. springboot 2.0.5配置Druid连接池和web监控后台
  6. JS笔记(20): JS中的同步编程和异步编程
  7. VS2010中使用gtest简单案例
  8. iar stm32_树莓派玩转STM32开发(一)——介绍篇
  9. 用springboot编写RestController之——详解RestController中获取请求的各种数据
  10. 不能为虚拟电脑 ubuntu 打开一个新任务的解决办法
  11. Eclipse开发工具组合键介绍
  12. gnome-shell 扩展
  13. 悄悄安装vcredist_x64.exe,不弹出对话框
  14. linux专业的打谱软件下载,MuseScore 3.1 发布,音乐制谱软件
  15. 缠论中枢python源码_缠论中枢主图指标 源码 通达信 贴图 自动画笔
  16. 字节跳动 录屏功能_非常值得推荐,字节跳动出品的一款协同办公软件。
  17. TIFF图像文件格式详解
  18. 中南大学工商管理考研考情与难度、参考书及上岸前辈备考经验
  19. WIN10工具栏里的Ludashi mini 怎么删除 把鲁大师卸载后 发现还有这个,怎么去掉
  20. BIP与Siebel系统集成

热门文章

  1. 21世纪计算机科学与技术,大学计算机基础——常用办公软件(21世纪计算机科学与技术实践型教程)...
  2. pbmplus-图像文件格式转换包与PBM/PGM/PPM 格式图像文件
  3. 光伏逆变器市场现状及未来发展趋势
  4. java 健身房会员管理系统设计
  5. 在电网上使用的储能系统模拟(simulink)
  6. Vue锚链接(两种方法) scrollIntoView
  7. 利用金箔形成的暗能量制造出的虫洞穿越空间的机器
  8. 《实体解析与信息质量》 - 3.1.2 SERF模型
  9. sketch最强切图工具Sketch Measure
  10. 【linux】循序渐进学运维-基础篇-kill命令