mysql 间接引用_微服务之间安全调用 by 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.oauth2.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.OAuth2TokenEndpointServiceClient

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

/**

* Send a client grant to the token endpoint.

*

* @return

*/

OAuth2AccessToken sendClentCredentialsGrant();

e, d的适配器类,增加对应的实现方法。

com.company.appstack.security.oauth2.OAuth2TokenEndpointServiceClientAdapter

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

/**

* 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 formParams = new LinkedMultiValueMap<>();

formParams.set("grant_type", "client_credentials");

addAuthentication(reqHeaders, formParams);

HttpEntity> entity = new HttpEntity<>(formParams, reqHeaders);

log.debug("contacting OAuth2 token endpoint to authenticate internal service.");

ResponseEntity 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 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.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个微服务已经注册成功。

6e2b72033451be3ee530d5cd5145238f.png

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

http://localhost:8081/test/servicecall

可以看到uaa返回的结果:

bffa1dfe87e29415a032697c2db50325.png

说明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

可以看到返回错误信息:

8a129d235cad43bc21f28da25456a609.png

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

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

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

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值