内容简介:每日一博 | 使用 RestTemplate 构建远程调用服务
spring boot / cloud (八) 使用RestTemplate来构建远程调用服务
##前言
上周因家里突发急事,请假一周,故博客没有正常更新
###RestTemplate介绍:
RestTemplate是spring框架中自带的rest客户端 工具 类,具有丰富的API,并且在spring cloud中,标记@LoadBalanced注解,可以实现客户端负载均衡的rest调用.
##思路
RestTemplate虽然提供了丰富的API,但是这些API过于底层,如果不稍加控制,让开发人员随意使用,那后续的代码也将会变的五花八门,难以维护.
同时,当系统规模大了之后,将会有更多的服务,并且服务之间的调用关系也将更加复杂,如果不进行管控治理的话,同样,项目同期也将越来越不可控,
最后,服务间调用也需要有明确的权限认证机制,最好是能通过配置的方式来明确,哪些服务可以调用那些服务.从而来把控项目的复杂度.
本文将从以下几点来提供一个解决问题的思路:
-
通过spring boot的@ConfigurationProperties机制来定义远程服务的元数据,从而实现权限认证的配置化
-
使用HandlerInterceptor来进行拦截,实现权限的验证
-
定义通用Rms类,来规范RestTemplate的使用
##实现
###1.实现权限配置
####1.定义Application元数据
public class ApplicationMeta implements Serializable { //ID private static final long serialVersionUID = 1L; //服务ID private String serviceId; //私钥 private String secret; //权限 private String purview; //所有服务的调用权限(优先判定) private Boolean all = false; //禁止服务调用 private Boolean disabled = false; //描述 private String description; }
####2.定义Service元数据
public class ServiceMeta implements Serializable { //ID private static final long serialVersionUID = 1L; //应用名称 private String owner; //地址 private String uri; //服务方法 private String method; //是否HTTPS private Boolean isHttps = false; //描述 private String description;
####3.定义RmsProperties类
@Component @ConfigurationProperties(prefix = "com.egridcloud.rms.properties") public class RmsProperties implements Serializable { //ID private static final long serialVersionUID = 1L; //应用清单(应用名称 : 应用地址) private Map<String, ApplicationMeta> application; //服务路径(服务编号 : 服务元数据) private Map<String, ServiceMeta> service;
####4.在properties文件中进行配置
#定义了一个叫udf-demo(跟spring boot的应用ID一致),设置了私钥,以及可调用的服务 com.egridcloud.rms.properties.application.udf-demo.serviceId=127.0.0.1:8080 com.egridcloud.rms.properties.application.udf-demo.secret=ADSFHKW349546RFSGF com.egridcloud.rms.properties.application.udf-demo.purview=FILE_3 com.egridcloud.rms.properties.application.udf-demo.all=false com.egridcloud.rms.properties.application.udf-demo.disabled=false com.egridcloud.rms.properties.application.udf-demo.description=sample application #定义了一个叫FILE_3的服务,后续使用这个服务编号进行调用即可 com.egridcloud.rms.properties.service.FILE_3.owner=udf-demo com.egridcloud.rms.properties.service.FILE_3.uri=/service/file/download com.egridcloud.rms.properties.service.FILE_3.method=POST com.egridcloud.rms.properties.service.FILE_3.isHttps=false com.egridcloud.rms.properties.service.FILE_3.description=文件下载
###2.实现权限校验
####1.定义RmsAuthHandlerInterceptor拦截器
public class RmsAuthHandlerInterceptor implements HandlerInterceptor { //环境标识 private static final String DEV_PROFILES = "dev"; //配置 @Autowired private RmsProperties rmsProperties; //环境变量 @Autowired private Environment env; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { ....... } }
####2.完善preHandle方法-取出认证信息
String rmsApplicationName = request.getHeader(Constant.HEADER_RMS_APPLICATION_NAME_CODE); if (StringUtils.isBlank(rmsApplicationName)) { rmsApplicationName = request.getParameter(Constant.HEADER_RMS_APPLICATION_NAME_CODE); } //获取认证信息(sign) String rmsSign = request.getHeader(Constant.HEADER_RMS_SIGN_CODE); if (StringUtils.isBlank(rmsSign)) { rmsSign = request.getParameter(Constant.HEADER_RMS_SIGN_CODE); } //获取认证信息(服务代码) String rmsServiceCode = request.getHeader(Constant.HEADER_SERVICE_CODE_CODE); if (StringUtils.isBlank(rmsServiceCode)) { rmsServiceCode = request.getParameter(Constant.HEADER_SERVICE_CODE_CODE); } //获取请求地址 String url = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString(); //获取请求方法 String method = request.getMethod();
####3.完善preHandle方法-校验
//判断环境(开发环境无需校验) if (!DEV_PROFILES.equals(env.getProperty("spring.profiles.active"))) { //判断是否缺少认证信息 if (StringUtils.isBlank(rmsApplicationName) || StringUtils.isBlank(rmsSign) || StringUtils.isBlank(rmsServiceCode)) { throw new AuthException("missing required authentication parameters (rmsApplicationName , rmsSign)"); } //判断systemTag是否有效 if (!this.rmsProperties.getApplication().containsKey(rmsApplicationName)) { throw new AuthException("unrecognized systemTag:" + rmsApplicationName); } //获得应用元数据 ApplicationMeta applicationMeta = rmsProperties.getApplication().get(rmsApplicationName); //获得secret String secret = applicationMeta.getSecret(); //计算sign String sign = Constant.sign(rmsApplicationName, secret); //比较sign if (!rmsSign.equals(sign)) { throw new AuthException("sign Validation failed"); } //判断是否有调用所有服务的权限 if (!applicationMeta.getAll()) { //判断是否禁止调用所有服务权限 if (applicationMeta.getDisabled()) { throw new PermissionException(rmsApplicationName + " is disabled"); } //判断是否有调用该服务的权限 if (applicationMeta.getPurview().indexOf(rmsServiceCode) == -1) { throw new PermissionException("no access to this servoceCode : " + rmsServiceCode); } //判断服务元数据是否存在 if (!rmsProperties.getService().containsKey(rmsServiceCode)) { throw new PermissionException("service code not exist"); } //获得服务元数据 ServiceMeta serviceMeta = rmsProperties.getService().get(rmsServiceCode); //比较url和method的有效性 if (!serviceMeta.getUri().equals(url) || !serviceMeta.getMethod().equals(method)) { throw new PermissionException("url and method verification error"); } } }
####4.定义RmsConfig类
@Configuration @ConfigurationProperties(prefix = "com.egridcloud.rms.config") @Validated public class RmsConfig { //RMS扫描路径 @NotNull private String rmsPathPatterns; ......... }
####5.定义RmsConfig类-注册bean
@Bean @LoadBalanced RestTemplate restTemplate(ClientHttpRequestFactory requestFactory) { return new RestTemplate(requestFactory); } @Bean public RmsAuthHandlerInterceptor rmsAuthHandlerInterceptor() { return new RmsAuthHandlerInterceptor(); } @Bean public WebMvcConfigurer rmsAuthConfigurer() { //NOSONAR return new WebMvcConfigurerAdapter() { @Override public void addInterceptors(InterceptorRegistry registry) { String[] rmsPathPatternsArray = rmsPathPatterns.split(","); registry.addInterceptor(rmsAuthHandlerInterceptor()).addPathPatterns(rmsPathPatternsArray); super.addInterceptors(registry); } }; }
####6.在properties文件中进行配置
#拦截路径 com.egridcloud.rms.config.rmsPathPatterns=/service/**
###3.实现Rms类
####1.定义rms类
@Component public class Rms { //应用名称 @Value("${spring.application.name}") private String springApplicationName; //restTemplate @Autowired private RestTemplate restTemplate; //配置 @Autowired private RmsProperties rmsProperties;
####2.定义rms类-call方法
public <I, O> ResponseEntity<O> call(String serviceCode, I input, String uriParam, ParameterizedTypeReference<O> responseType, Map<String, ?> uriVariables) { //客户端权限验证 verification(serviceCode); //构建请求路径 String path = getRmsUrl(serviceCode); //获得请求方法 String method = getRmsMethod(serviceCode); //拼装路径参数 if (StringUtils.isNotBlank(uriParam)) { path += uriParam; } //构建请求头 HttpHeaders httpHeaders = buildSystemTagHeaders(serviceCode); //构建请求消息体 HttpEntity<I> requestEntity = new HttpEntity<>(input, httpHeaders); //请求并且返回 LOGGER.info("rms url : {} , method : {} ", path, method); return restTemplate.exchange(path, HttpMethod.resolve(method), requestEntity, responseType, uriVariables != null ? uriVariables : new HashMap<String, String>()); }
####3.定义rms类-其他方法
//构建请求头 private HttpHeaders buildSystemTagHeaders(String serviceCode) { String secret = rmsProperties.getApplication().get(springApplicationName).getSecret(); HttpHeaders headers = new HttpHeaders(); headers.add(Constant.HEADER_RMS_APPLICATION_NAME_CODE, springApplicationName); headers.add(Constant.HEADER_RMS_SIGN_CODE, Constant.sign(springApplicationName, secret)); headers.add(Constant.HEADER_SERVICE_CODE_CODE, serviceCode); return headers; } //客户端验证 private void verification(String serviceCode) { ApplicationMeta applicationMeta = rmsProperties.getApplication().get(springApplicationName); if (!applicationMeta.getAll()) { if (applicationMeta.getDisabled()) { throw new PermissionException(springApplicationName + " is disabled"); } if (applicationMeta.getPurview().indexOf(serviceCode) == -1) { throw new PermissionException("no access to this servoceCode : " + serviceCode); } } } //获得请求方法 private String getRmsMethod(String serviceCode) { return rmsProperties.getService().get(serviceCode).getMethod(); } //构造url private String getRmsUrl(String serviceCode) { //获取服务元数据 ServiceMeta serviceMeta = rmsProperties.getService().get(serviceCode); //构建请求路径 StringBuilder url = new StringBuilder(serviceMeta.getIsHttps() ? Constant.HTTPS : Constant.HTTP); url.append(rmsProperties.getApplication().get(serviceMeta.getOwner()).getServiceId()); url.append(serviceMeta.getUri()); return url.toString(); } //计算sign public static String sign(String rmsApplicationName, String secret) { final String split = "_"; StringBuilder sb = new StringBuilder(); sb.append(rmsApplicationName).append(split).append(secret).append(split) .append(new SimpleDateFormat(DATA_FORMAT).format(new Date())); return DigestUtils.md5Hex(sb.toString()); }
###4.客户端调用
//获得文件信息 ResponseEntity<RestResponse<FileInfo>> fileInfo = rms.call("FILE_4", fileParam, null, new ParameterizedTypeReference<RestResponse<FileInfo>>() { }, null);
##结束
这样,规范了远程服务的调用,只关心接口编号和接口的入参和出参,能够增加沟通效率,并且也有了轻量级的服务治理机制,服务间的调用更可控,到最后,配置文件一拉出来一清二楚.
想获得最快更新,请关注公众号
以上所述就是小编给大家介绍的《每日一博 | 使用 RestTemplate 构建远程调用服务》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 轻松构建微服务之远程调用
- 直观讲解-RPC调用和HTTP调用的区别
- 调用链系列一:解读UAVStack中的调用链技术
- 调用链系列二:解读UAVStack中的调用链技术
- 调用链系列三:解读UAVStack中的调用链技术
- dubbo源码解析(二十七)远程调用——injvm本地调用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Introduction to Computation and Programming Using Python
John V. Guttag / The MIT Press / 2013-7 / USD 25.00
This book introduces students with little or no prior programming experience to the art of computational problem solving using Python and various Python libraries, including PyLab. It provides student......一起来看看 《Introduction to Computation and Programming Using Python》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
MD5 加密
MD5 加密工具