热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于Feign初探RangerApi

RangerApi之User管理在大数据平台之权限管理组件-AapcheRanger一文中我们了解了Ranger以及安装部署过程以及Admin可视化界面的使用。除了可以在可视化的R

Ranger Api之User管理

在大数据平台之权限管理组件 - Aapche Ranger一文中我们了解了Ranger以及安装部署过程以及Admin可视化界面的使用。

除了可以在可视化的Ranger Admin界面上进行权限、用户等管理外,Ranger还支持通过REST API来完成这些操作。因为我们如果要开发自己的大数据平台,可能并不会使用Ranger Admin的可视化界面,而是希望在自己的大数据平台界面去操作Ranger。有了Ranger Api的支持,我们就可以轻易实现这一点。

关于Ranger Api的官方文档如下:



  • https://ranger.apache.org/apidocs/index.html

本小节简单演示下User Api的使用,User Api用于管理用户,对用户进行增删改查。首先创建一个空Maven项目。由于要通过http请求Api,所以需要用到http请求工具。这里用到的是feign,完整的pom文件内容如下:


4.0.0
org.example
ranger-client
1.0-SNAPSHOT


junit
junit
4.12


org.projectlombok
lombok
1.18.4


com.netflix.feign
feign-core
8.18.0


com.netflix.feign
feign-jackson
8.18.0


com.netflix.feign
feign-okhttp
8.18.0


com.fasterxml.jackson.core
jackson-databind
2.9.8


com.google.guava
guava
21.0


org.slf4j
slf4j-api
1.7.25





org.apache.maven.plugins
maven-compiler-plugin
3.8.0

1.8
1.8




首先定义一个配置类,配置用于访问ranger api的用户名密码:

package com.example.ranger.config;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class RangerAuthConfig {
private String username = "admin";
private String password = "admin";
}

再定义一个配置类,用于提供http客户端配置:

package com.example.ranger.config;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import feign.Logger;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class RangerClientConfig {
private int cOnnectionTimeoutMills= 5 * 1000;
private int readTimeoutMills = 30 * 1000;
private Logger.Level level = Logger.Level.BASIC;
private String url = "http://192.168.243.161:6080";
private RangerAuthConfig authCOnfig= new RangerAuthConfig();
}

声明一个请求拦截器,用于在发起请求之前添加一些请求头:

package com.example.ranger.interceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
public class RangerHeadersInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("Accept", "application/json");
template.header("X-XSRF_HEADER", "\"\"");
template.header("Content-Type", "application/json");
}
}

通常在实际的开发中,我们会定义一个业务异常,用于对异常信息进行自定义封装:

package com.example.ranger.exception;
import java.io.Serializable;
public class RangerClientException extends RuntimeException implements Serializable {
private static final long serialVersiOnUID= -4441189815976639860L;
private Throwable cause;
private int status;
private String message;
public RangerClientException(int status, String message) {
this.status = status;
this.message = message;
}
@Override
public String getMessage() {
return String.format("%s http status = %s", message, status);
}
@Override
public String toString() {
return String.format("%s http status = %s", message, status);
}
}

自定义一个异常信息解析器,当请求发生异常时,feign会调用该方法返回我们自定义的异常类:

package com.example.ranger.decoder;
import com.example.ranger.exception.RangerClientException;
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import java.io.IOException;
public class RangerErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
return new RangerClientException(
response.status(), errorMessage(methodKey, response)
);
}
private String errorMessage(String methodKey, Response response) {
String msg = String.format("status %s reading %s", response.status(), methodKey);
if (response.body() != null) {
try {
msg += "content:\n" + Util.toString(response.body().asReader());
} catch (IOException e) {
e.printStackTrace();
}
}
return msg;
}
}

完成上面feign相关的前置准备后,我们就可以开始编写请求ranger api的代码了。首先,定义用户接口的请求和响应实体:

package com.example.ranger.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 用户信息实体类
* https://ranger.apache.org/apidocs/json_VXUser.html
*
* @author 01
* @date 2020-11-12
**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
private int id;
private String name;
private String createDate;
private String updateDate;
private String owner;
private String updateBy;
private String firstName;
private String lastName;
private String emailAddress;
private String password;
private String description;
private int status;
private int isVisible;
private int userSource;
private List userRoleList;
}

定义用户api相关的接口,这是Feign这种声明式http客户端的做法:

package com.example.ranger.api;
import com.example.ranger.model.User;
import feign.Param;
import feign.RequestLine;
/**
* 用户相关api
* https://ranger.apache.org/apidocs/resource_XUserREST.html
*
* @author 01
* @date 2020-11-12
**/
public interface UserFeignClient {
/**
* 创建用户接口
* https://ranger.apache.org/apidocs/resource_XUserREST.html#resource_XUserREST_secureCreateXUser_POST
*
* @param user user
* @return 用户信息
*/
@RequestLine("POST /service/xusers/secure/users")
User createUser(User user);
/**
* 删除用户
* https://ranger.apache.org/apidocs/resource_XUserREST.html#resource_XUserREST_deleteSingleUserByUserId_DELETE
*
* @param id 用户id
* @param forceDelete 是否强制删除
*/
@RequestLine("DELETE /service/xusers/secure/users/id/{id}?forceDelete={forceDelete}")
void deleteUser(@Param("id") Integer id,
@Param("forceDelete") boolean forceDelete);
/**
* 获取用户信息
* https://ranger.apache.org/apidocs/resource_XUserREST.html#resource_XUserREST_getXUserByUserName_GET
*
* @param name 用户名称
* @return 用户信息
*/
@RequestLine("GET /service/xusers/users/userName/{name} ")
User getUserByName(@Param("name") String name);
}

然后我们在此之外再包一层,通常我们会在这一层做一些额外的处理,例如参数校验、结果校验之类的:

package com.example.ranger.api;
import com.example.ranger.exception.RangerClientException;
import com.example.ranger.model.User;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class UserApi {
private final UserFeignClient userClient;
public User createUser(User user) throws RangerClientException {
return userClient.createUser(user);
}
public void deleteUser(Integer id, boolean forceDelete) {
userClient.deleteUser(id, forceDelete);
}
public User getUserByName(String name) throws RangerClientException {
return userClient.getUserByName(name);
}
}

最后定义一个客户端工具类,提供一个统一的操作入口,以便于外部使用:

package com.example.ranger;
import com.example.ranger.api.PolicyApi;
import com.example.ranger.api.PolicyFeignClient;
import com.example.ranger.api.UserApi;
import com.example.ranger.api.UserFeignClient;
import com.example.ranger.config.RangerClientConfig;
import com.example.ranger.decoder.RangerErrorDecoder;
import com.example.ranger.interceptor.RangerHeadersInterceptor;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import feign.Feign;
import feign.Logger;
import feign.Request;
import feign.auth.BasicAuthRequestInterceptor;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.okhttp.OkHttpClient;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
public class RangerClient {
@Getter
private UserApi userApi;
@Getter
private PolicyApi policyApi;
private final RangerClientConfig rangerClientConfig;
public RangerClient(RangerClientConfig rangerClientConfig) {
this.rangerClientCOnfig= rangerClientConfig;
}
private static final ObjectMapper MAPPER = new ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.configure(SerializationFeature.INDENT_OUTPUT, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
private static final JacksonEncoder ENCODER = new JacksonEncoder(MAPPER);
private static final JacksonDecoder DECODER = new JacksonDecoder(MAPPER);
/**
* 标识client是否已启动
*/
private final AtomicBoolean started = new AtomicBoolean(false);
/**
* 配置client的构建信息
*
* @return {@link Feign.Builder}
*/
private Feign.Builder feignBuilder() {
return Feign.builder()
.logger(new Logger.JavaLogger())
.logLevel(rangerClientConfig.getLevel())
.options(new Request.Options(
rangerClientConfig.getConnectionTimeoutMills(),
rangerClientConfig.getReadTimeoutMills()
)).encoder(ENCODER).decoder(DECODER)
.client(new OkHttpClient())
.errorDecoder(new RangerErrorDecoder())
.requestInterceptor(new RangerHeadersInterceptor())
.requestInterceptor(new BasicAuthRequestInterceptor(
rangerClientConfig.getAuthConfig().getUsername(),
rangerClientConfig.getAuthConfig().getPassword()
));
}
/**
* 启动client
*/
public void start() {
if (started.get()) {
log.info("ranger client is already started");
return;
}
userApi = new UserApi(feignBuilder().target(
UserFeignClient.class, rangerClientConfig.getUrl()
));
policyApi = new PolicyApi(feignBuilder().target(
PolicyFeignClient.class, rangerClientConfig.getUrl()
));
started.set(true);
}
/**
* 停止client
*/
public void stop() {
if (started.get()) {
started.set(false);
} else {
log.info("ranger client is not started");
}
}
}

完成以上的功能代码编写后,我们来写一些单元测试,去验证一下功能是否都正确实现了:

package com.example.ranger.api;
import com.example.ranger.RangerClient;
import com.example.ranger.config.RangerClientConfig;
import com.example.ranger.model.User;
import org.junit.Before;
import org.junit.Test;
import java.util.Collections;
import static org.junit.Assert.assertNotNull;
public class UserApiTest {
private static RangerClient rangerClient;
@Before
public void initRangerClient() {
rangerClient = new RangerClient(new RangerClientConfig());
rangerClient.start();
}
@Test
public void testCreateUser() {
User user = User.builder().name("test")
.firstName("first").lastName("last").password("user@123")
.isVisible(1).status(1).userSource(0)
.userRoleList(Collections.singletonList("ROLE_USER"))
.build();
User result = rangerClient.getUserApi().createUser(user);
assertNotNull(result);
System.out.println(result);
}
@Test
public void testDeleteUser() {
User result = rangerClient.getUserApi().getUserByName("test");
assertNotNull(result);
rangerClient.getUserApi().deleteUser(result.getId(), true);
}
@Test
public void testGetUserByName() {
User result = rangerClient.getUserApi().getUserByName("test");
assertNotNull(result);
System.out.println(result);
}
}

运行testCreateUser这个单元测试,然后到ranger admin上查看是否有新增相应的用户:
基于Feign初探Ranger Api

然后再运行testDeleteUser这个单元测试,看看该用户是否能被正常删除:
基于Feign初探Ranger Api




Ranger Api之Policy管理

本小节将介绍使用Policy Api对Ranger上的权限策略进行管理。首先定义接口的请求/响应实体类,由于Policy稍微复杂点,需要定义的类也比较多:

/**
* 策略所作用的资源,即hdfs目录、hive的库/表/列等
* https://ranger.apache.org/apidocs/json_RangerPolicyResource.html
*
* @author 01
* @date 2020-11-12
**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class PolicyResource {
private List values = Lists.newArrayList();
private Boolean isExcludes;
private Boolean isRecursive;
}
/**
* https://ranger.apache.org/apidocs/json_RangerPolicyItemCondition.html
*
* @author 01
* @date 2020-11-12
**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class PolicyItemCondition {
private String type;
private List value = Lists.newArrayList();
}
/**
* 策略条件项中的权限信息,即在该项中拥有哪些权限,对应“Permissions”
* https://ranger.apache.org/apidocs/json_RangerPolicyItemAccess.html
*
* @author 01
* @date 2020-11-12
**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class PolicyItemAccess {
private String type;
private Boolean isAllowed;
}
/**
* 策略中的条件项,对应“Allow Conditions”或“Deny Conditions”中的每一栏信息
* https://ranger.apache.org/apidocs/json_RangerPolicyItem.html
*
* @author 01
* @date 2020-11-12
**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class PolicyItem {
private List

accesses = Lists.newArrayList();
private Set users = Sets.newHashSet();
private List groups = Lists.newArrayList();
private List

cOnditions= Lists.newArrayList();
private Boolean delegateAdmin;
}
/**
* 策略实体
* https://ranger.apache.org/apidocs/json_RangerPolicy.html
*
* @author 01
* @date 2020-11-12
**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Policy {
private Map resources;
private List

policyItems = Lists.newArrayList();
private List

denyPolicyItems = Lists.newArrayList();
private List

allowExceptiOns= Lists.newArrayList();
private List

denyExceptiOns= Lists.newArrayList();
private List dataMaskPolicyItems = Lists.newArrayList();
private List rowFilterPolicyItems = Lists.newArrayList();
private int id;
private String guid;
private boolean isEnabled;
private int version;
private String service;
private String name;
private int policyType;
private String description;
private boolean isAuditEnabled;
}

定义权限策略相关api的接口:

package com.example.ranger.api;
import com.example.ranger.model.Policy;
import feign.Param;
import feign.RequestLine;
import java.util.List;
/**
* 权限策略相关api
* https://ranger.apache.org/apidocs/resource_PublicAPIsv2.html
* https://ranger.apache.org/apidocs/resource_ServiceREST.html
*
* @author 01
* @date 2020-11-12
**/
public interface PolicyFeignClient {
/**
* 创建策略
* https://ranger.apache.org/apidocs/resource_PublicAPIsv2.html#resource_PublicAPIsv2_createPolicy_POST
*
* @param policy 策略信息
* @return 策略信息
*/
@RequestLine("POST /service/public/v2/api/policy")
Policy createPolicy(Policy policy);
/**
* 删除策略
* https://ranger.apache.org/apidocs/resource_ServiceREST.html#resource_ServiceREST_deletePolicy_DELETE
*
* @param id 策略id
*/
@RequestLine("DELETE /service/plugins/policies/{id}")
void deletePolicy(@Param("id") Integer id);
/**
* 通过服务和策略名称获取策略信息
* https://ranger.apache.org/apidocs/resource_PublicAPIsv2.html#resource_PublicAPIsv2_getPolicyByName_GET
*
* @param serviceName 服务名称
* @param policyName 策略名称
* @return 策略信息
*/
@RequestLine("GET /service/public/v2/api/service/{serviceName}/policy/{policyName}")
Policy getPolicyByName(@Param("serviceName") String serviceName,
@Param("policyName") String policyName);
/**
* 获取指定服务下的策略信息列表
*
* @param serviceName 服务名称
* @return 该服务下的策略信息列表
*/
@RequestLine("GET /service/public/v2/api/service/{serviceName}/policy")
List

getAllPoliciesByService(@Param("serviceName") String serviceName);
}

同样,在接口之上再包一层:

package com.example.ranger.api;
import com.example.ranger.model.Policy;
import lombok.AllArgsConstructor;
import java.util.List;
@AllArgsConstructor
public class PolicyApi {
private final PolicyFeignClient policyFeignClient;
public Policy getPolicyByName(String serviceName, String policyName) {
return policyFeignClient.getPolicyByName(serviceName, policyName);
}
public List

getAllPoliciesByService(String serviceName) {
return policyFeignClient.getAllPoliciesByService(serviceName);
}
public Policy createPolicy(Policy policy) {
return policyFeignClient.createPolicy(policy);
}
public void deletePolicy(Integer id) {
policyFeignClient.deletePolicy(id);
}
}

修改RangerClient,增加PolicyApi相关代码:

@Slf4j
public class RangerClient {
@Getter
private PolicyApi policyApi;
...
/**
* 启动client
*/
public void start() {
if (started.get()) {
log.info("ranger client is already started");
return;
}
userApi = new UserApi(feignBuilder().target(
UserFeignClient.class, rangerClientConfig.getUrl()
));
policyApi = new PolicyApi(feignBuilder().target(
PolicyFeignClient.class, rangerClientConfig.getUrl()
));
started.set(true);
}
...
}

编写单元测试:

package com.example.ranger.api;
import com.example.ranger.RangerClient;
import com.example.ranger.config.RangerClientConfig;
import com.example.ranger.model.Policy;
import com.example.ranger.model.PolicyItem;
import com.example.ranger.model.PolicyItemAccess;
import com.example.ranger.model.PolicyResource;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
import static org.junit.Assert.assertNotNull;
public class PolicyApiTest {
private static RangerClient rangerClient;
@Before
public void initRangerClient() {
rangerClient = new RangerClient(new RangerClientConfig());
rangerClient.start();
}
@Test
public void testCreatePolicy() {
PolicyResource policyResource = PolicyResource.builder()
.values(Collections.singletonList("/testdir2"))
.isRecursive(true)
.build();
Map policyResourceMap = new HashMap<>();
policyResourceMap.put("path", policyResource);
Set users = new HashSet<>();
users.add("hive");
List

policyItemAccessList = new ArrayList<>();
policyItemAccessList.add(PolicyItemAccess.builder().type("read").build());
policyItemAccessList.add(PolicyItemAccess.builder().type("write").build());
policyItemAccessList.add(PolicyItemAccess.builder().type("execute").build());
PolicyItem policyItem = PolicyItem.builder()
.delegateAdmin(true).users(users)
.accesses(policyItemAccessList)
.build();
Policy policy = Policy.builder()
.service("dev_hdfs")
.name("test_ranger_api")
.isEnabled(true).policyType(0)
.resources(policyResourceMap)
.policyItems(Collections.singletonList(policyItem))
.build();
Policy result = rangerClient.getPolicyApi().createPolicy(policy);
assertNotNull(result);
System.out.println(result.getName());
}
@Test
public void testGetPolicyByName() {
Policy result = rangerClient.getPolicyApi()
.getPolicyByName("dev_hdfs", "test_ranger_api");
assertNotNull(result);
System.out.println(result.getName());
}
@Test
public void testGetAllPoliciesByService() {
List

result = rangerClient.getPolicyApi()
.getAllPoliciesByService("dev_hdfs");
assertNotNull(result);
System.out.println(result.size());
}
@Test
public void testDeletePolicy() {
Policy result = rangerClient.getPolicyApi()
.getPolicyByName("dev_hdfs", "test_ranger_api");
assertNotNull(result);
rangerClient.getPolicyApi().deletePolicy(result.getId());
System.out.println(result.getName());
}
}

执行testCreatePolicy单元测试,到ranger admin上验证是否创建了相应的策略:
基于Feign初探Ranger Api

查看策略内容是否与代码中定义的一致:
基于Feign初探Ranger Api



本文的代码仓库:



推荐阅读
  • springMVC JRS303验证 ... [详细]
  • SpringMVC RestTemplate的几种请求调用(转)
    SpringMVCRestTemplate的几种请求调用(转),Go语言社区,Golang程序员人脉社 ... [详细]
  • 烤鸭|本文_Spring之Bean的生命周期详解
    烤鸭|本文_Spring之Bean的生命周期详解 ... [详细]
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • Python + Pytest 接口自动化测试中 Token 关联登录的实现方法
    本文将深入探讨 Python 和 Pytest 在接口自动化测试中如何实现 Token 关联登录,内容详尽、逻辑清晰,旨在帮助读者掌握这一关键技能。 ... [详细]
  • ElasticSearch 集群监控与优化
    本文详细介绍了如何有效地监控 ElasticSearch 集群,涵盖了关键性能指标、集群健康状况、统计信息以及内存和垃圾回收的监控方法。 ... [详细]
  • Spring Boot 中静态资源映射详解
    本文深入探讨了 Spring Boot 如何简化 Web 应用中的静态资源管理,包括默认的静态资源映射规则、WebJars 的使用以及静态首页的处理方法。通过本文,您将了解如何高效地管理和引用静态资源。 ... [详细]
  • 本文详细介绍了Java中实现异步调用的多种方式,包括线程创建、Future接口、CompletableFuture类以及Spring框架的@Async注解。通过代码示例和深入解析,帮助读者理解并掌握这些技术。 ... [详细]
  • 搭建Jenkins、Ant与TestNG集成环境
    本文详细介绍了如何在Ubuntu 16.04系统上配置Jenkins、Ant和TestNG的集成开发环境,涵盖从安装到配置的具体步骤,并提供了创建Windows Slave节点及项目构建的指南。 ... [详细]
  • 优化Jenkins首次启动速度
    本文详细描述了在启动Jenkins后遇到的长时间加载问题,并提供了一种通过修改更新中心配置文件来显著提升启动速度的有效解决方案。 ... [详细]
  • 近期我们开发了一款包含天气预报功能的万年历应用,为了满足这一需求,团队花费数日时间精心打造并测试了一个稳定可靠的天气API接口,现正式对外开放。 ... [详细]
  • 深入浅出TensorFlow数据读写机制
    本文详细介绍TensorFlow中的数据读写操作,包括TFRecord文件的创建与读取,以及数据集(dataset)的相关概念和使用方法。 ... [详细]
  • Servlet过滤器入门:实现与配置
    本文介绍如何在Java Web应用中实现和配置Servlet过滤器,通过实现`javax.servlet.Filter`接口来创建过滤器,并详细说明其在web.xml文件中的配置方法。 ... [详细]
  • Win10 UWP 开发技巧:利用 XamlTreeDump 获取 XAML 元素树
    本文介绍如何在 Win10 UWP 开发中使用 XamlTreeDump 库来获取和转换 XAML 元素树为 JSON 字符串,这对于 UI 单元测试非常有用。 ... [详细]
  • Eclipse 中 JSP 开发环境配置指南
    本文详细介绍了如何在 Eclipse 集成开发环境中配置 JSP 运行环境,包括必要的软件下载、Tomcat 服务器的配置以及常见问题的解决方法。 ... [详细]
author-avatar
且羞且笑且动心细
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有