1 可行性分析(配置)

https://github.com/apache/incubator-livy/blob/v0.7.1-incubating-rc1/conf/livy.conf.template

 在最新的发布版本,下列配置表明:Livy除了支持Kerberos认证外,还支持自定义的认证过滤器

# Authentication support for Livy server
# Livy has a built-in SPnego authentication support for HTTP requests  with below configurations.
# livy.server.auth.type = kerberos
# livy.server.auth.kerberos.principal = <spnego principal>
# livy.server.auth.kerberos.keytab = <spnego keytab>
# livy.server.auth.kerberos.name-rules = DEFAULT
#
# If user wants to use custom authentication filter, configurations are:
# livy.server.auth.type = <custom>
# livy.server.auth.<custom>.class = <class of custom auth filter>
# livy.server.auth.<custom>.param.<foo1> = <bar1>
# livy.server.auth.<custom>.param.<foo2> = <bar2>

2 代码执行逻辑分析

2.1 配置加载入口

org.apache.livy.server.LivyServer
    livyConf.get(AUTH_TYPE) match {
**** 中间省略
      case customType =>
        val authClassConf = s"livy.server.auth.$customType.class"
        val authClass = livyConf.get(authClassConf)
        require(authClass != null, s"$customType auth requires $authClassConf to be provided")

        val holder = new FilterHolder()
        holder.setClassName(authClass)

        val prefix = s"livy.server.auth.$customType.param."
        livyConf.asScala.filter { kv =>
          kv.getKey.length > prefix.length && kv.getKey.startsWith(prefix)
        }.foreach { kv =>
          holder.setInitParameter(kv.getKey.substring(prefix.length), kv.getValue)
        }
        server.context.addFilter(holder, "/*", EnumSet.allOf(classOf[DispatcherType]))
        info(s"$customType auth enabled")

2.2 初始化filter和参数传递

filter在livy.server.auth.<custom>.class中进行配置,需要继承 org.apache.hadoop.security.authentication.server.AuthenticationFilter(这里我直接使用的这个类);

参数都存储在holder的Map<String, String> _initParams中;
 

2.3 进行filter,指定authHandle

  public void init(FilterConfig filterConfig) throws ServletException {
    String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX);
    configPrefix = (configPrefix != null) ? configPrefix + "." : "";
    config = getConfiguration(configPrefix, filterConfig);
    String authHandlerName = config.getProperty(AUTH_TYPE, null);
    String authHandlerClassName;
    if (authHandlerName == null) {
      throw new ServletException("Authentication type must be specified: " +
          PseudoAuthenticationHandler.TYPE + "|" + 
          KerberosAuthenticationHandler.TYPE + "|<class>");
    }
    if (authHandlerName.toLowerCase(Locale.ENGLISH).equals(
        PseudoAuthenticationHandler.TYPE)) {
      authHandlerClassName = PseudoAuthenticationHandler.class.getName();
    } else if (authHandlerName.toLowerCase(Locale.ENGLISH).equals(
        KerberosAuthenticationHandler.TYPE)) {
      authHandlerClassName = KerberosAuthenticationHandler.class.getName();
    } else {
      authHandlerClassName = authHandlerName;
    }

    validity = Long.parseLong(config.getProperty(AUTH_TOKEN_VALIDITY, "36000"))
        * 1000; //10 hours
    initializeSecretProvider(filterConfig);

    initializeAuthHandler(authHandlerClassName, filterConfig);

    cookieDomain = config.getProperty(COOKIE_DOMAIN, null);
    cookiePath = config.getProperty(COOKIE_PATH, null);
  }

调用auth函数

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws IOException, ServletException {
    boolean unauthorizedResponse = true;
    int errCode = HttpServletResponse.SC_UNAUTHORIZED;
    AuthenticationException authenticationEx = null;
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    boolean isHttps = "https".equals(httpRequest.getScheme());
    try {
      boolean newToken = false;
      AuthenticationToken token;
      try {
        token = getToken(httpRequest);
      }
      catch (AuthenticationException ex) {
        LOG.warn("AuthenticationToken ignored: " + ex.getMessage());
        // will be sent back in a 401 unless filter authenticates
        authenticationEx = ex;
        token = null;
      }
      if (authHandler.managementOperation(token, httpRequest, httpResponse)) {
        if (token == null) {
          if (LOG.isDebugEnabled()) {
            LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest));
          }
          token = authHandler.authenticate(httpRequest, httpResponse);
          if (token != null && token.getExpires() != 0 &&
              token != AuthenticationToken.ANONYMOUS) {
            token.setExpires(System.currentTimeMillis() + getValidity() * 1000);
          }
          newToken = true;
        }

 

 

3 自定义认证开发(将几个函数实现)

/**
 * @author xxx
 * @date 2023/3/9 5:33 PM
 */
class LivyUserAuth extends AuthenticationHandler {

  val log = LoggerFactory.getLogger(this.getClass)

  private var userPassList:mutable.Map[String, String] = mutable.HashMap()
  private var hdfsLocation:String = null
  private var fileLocation:String = null
  private var periodSecond:Int = -1
  private var decryptType:String = null
  private val CACHE_SIZE:Int = 1000

  private var passwordSupplier:Supplier[PasswordStore] = null

  override def getType: String = "xxx"

  override def init(properties: Properties): Unit = {
  }

  override def destroy(): Unit = {

  }

  /**
   * return true when the request processing should continue. If false is returned, it means the response has been populated by this method
   * @param authenticationToken
   * @param httpServletRequest
   * @param httpServletResponse
   */
  @throws[IOException]
  @throws[AuthenticationException]
  override def managementOperation(authenticationToken: AuthenticationToken,
                                   httpServletRequest: HttpServletRequest,
                                   httpServletResponse: HttpServletResponse): Boolean = {
    return true
  }

  @throws[IOException]
  @throws[AuthenticationException]
  override def authenticate(request: HttpServletRequest,
                            response: HttpServletResponse): AuthenticationToken = {

    var token: AuthenticationToken = null
    var authorization = request.getHeader("Authorization")
    log.info("authorization:" + authorization)
    token
  }
}

 

4 打包部署

只需要项目打包即可,依赖不需要(Livy Jars里面已经有了)

    <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/jars</outputDirectory>
</configuration>
</plugin>
</plugins>
 
livy.conf 新增下面的配置
livy.server.auth.type nxb
livy.server.auth.nxb.class org.apache.hadoop.security.authentication.server.AuthenticationFilter
livy.server.auth.nxb.param.file.location xxxxxxxxxx
livy.server.auth.nxb.param.period.second 1800
livy.server.auth.nxb.param.type com.newsbreak.livy.auth.LivyUserAuth

将打包好jar放在/usr/lib/livy/jars/

重启服务

sudo systemctl restart livy-server.service 

 

最后查看日志

 
posted on 2023-03-30 16:33  我爱吃胡萝卜  阅读(211)  评论(0)    收藏  举报