如何使用 Java 发布经过身份验证的 Jenkins 作业

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20459375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 02:05:30  来源:igfitidea点击:

How to POST an authenticated Jenkins job using Java

javarestjenkins

提问by Clayton

All:

全部:

I need to be able to POST a job to Jenkins using the Jenkins REST API but have not been able to get past authentication. I have been attempting to do this for a few days now and have researched answers on both this site and on the Jenkins site. I have been trying to use the Apache HttpClient with no success (even with preemptive validation). I keep getting error code 403 - forbidden. Has anyone been able to do this successfully? Here is the code that I am working with:

我需要能够使用 Jenkins REST API 向 Jenkins 发布工作,但无法通过身份验证。我已经尝试这样做了几天,并在此站点和 Jenkins 站点上研究了答案。我一直在尝试使用 Apache HttpClient 没有成功(即使使用抢先验证)。我不断收到错误代码 403 - 禁止。有没有人能够成功地做到这一点?这是我正在使用的代码:

package stackoverflow.question;

import gsafame.sample;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

public class PreEmptiveAuth {
final Logger log = Logger.getLogger(sample.class.getCanonicalName());
private JobData jd;
private CredentialsProvider credpro;
private AuthScope as;
private UsernamePasswordCredentials upc;
private String url;

public PreEmptiveAuth(JobData jd) {
    this.jd = jd;
    credpro = new BasicCredentialsProvider();
    as = new AuthScope(jd.getServer(), 443);
    upc = new UsernamePasswordCredentials(jd.getUsername(), jd.getPassword());
    credpro.setCredentials(as, upc);
    url = String.format("%s:8080/jenkins/job/%s/buildWithParameters", jd.getServer(), jd.getJob());
}

public void runTagJob() throws ClientProtocolException, IOException {
    log.entering(this.getClass().getCanonicalName(), "runTagJob");
    log.info("Entering runTagJob");
    CloseableHttpClient httpclient = HttpClients.custom()
            .setDefaultCredentialsProvider(credpro).build();
    HttpPost httpPost = new HttpPost(url);
    List <NameValuePair> nvps = new ArrayList <NameValuePair>();
    nvps.add(new BasicNameValuePair("username", jd.getUsername()));
    nvps.add(new BasicNameValuePair("password", jd.getPassword()));
    nvps.add(new BasicNameValuePair("apiToken", jd.getToken()));
    httpPost.setEntity(new UrlEncodedFormEntity(nvps));
    CloseableHttpResponse response2 = httpclient.execute(httpPost);

    try {
        System.out.println(response2.getStatusLine());
        HttpEntity entity2 = response2.getEntity();
        // do something useful with the response body
        // and ensure it is fully consumed
        EntityUtils.consume(entity2);
    } finally {
        response2.close();
    }
    log.info("Exiting runTagJob");
    log.exiting(this.getClass().getCanonicalName(), "runTagJob");
}

public void runPreTagJob() throws ClientProtocolException, IOException {
    log.entering(this.getClass().getCanonicalName(), "runPreTagJob");
    log.info("Entering runPreTagJob");
    HttpHost targetHost = new HttpHost(jd.getServer(), 8080, "http");
    CloseableHttpClient httpclient = HttpClients.custom()
            .setDefaultCredentialsProvider(credpro).build();
    try {

        // Create AuthCache instance
        AuthCache authCache = new BasicAuthCache();
        // Generate BASIC scheme object and add it to the local auth cache
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(targetHost, basicAuth);

        // Add AuthCache to the execution context
        HttpClientContext localContext = HttpClientContext.create();
        localContext.setAuthCache(authCache);

        HttpPost httpPost = new HttpPost(url);
        List <NameValuePair> nvps = new ArrayList <NameValuePair>();
        nvps.add(new BasicNameValuePair("username", jd.getUsername()));
        nvps.add(new BasicNameValuePair("password", jd.getPassword()));
        nvps.add(new BasicNameValuePair("apiToken", jd.getToken()));
        httpPost.setEntity(new UrlEncodedFormEntity(nvps));
        CloseableHttpResponse response2 = httpclient.execute(targetHost, httpPost, localContext);

        try {
            System.out.println(response2.getStatusLine());
            HttpEntity entity2 = response2.getEntity();
            // do something useful with the response body
            // and ensure it is fully consumed
            EntityUtils.consume(entity2);
        } finally {
            response2.close();
        }    
    } finally {
        httpclient.close();
    }
    log.info("Exiting runPreTagJob");
    log.exiting(this.getClass().getCanonicalName(), "runPreTagJob");
}

}

The JobData object that gets passed into this class contains information like username, password, server, and job information. I hope this helps!

传递给此类的 JobData 对象包含用户名、密码、服务器和作业信息等信息。我希望这有帮助!

采纳答案by Clayton

After researching on several sites, I managed to piece enough information together to come up with a solution. I am using older HTTP authentication code that is in some instances deprecated - but it works when nothing else would. If anyone has a better solution, I would be interested in seeing it. Anyway, here it is:

在对多个站点进行研究后,我设法将足够的信息拼凑在一起,以提出解决方案。我正在使用旧的 HTTP 身份验证代码,该代码在某些情况下已被弃用 - 但它可以在没有其他方法的情况下工作。如果有人有更好的解决方案,我会很有兴趣看到它。无论如何,这里是:

Main Class:

主要类:

package stackoverflow.answer;

import java.util.List;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;

public class sample {
    public static void main(String[] args) throws Exception {
        final Logger log = Logger.getLogger(sample.class.getCanonicalName());
        JobData jd = new JobData();

        Scanner input = new Scanner(System.in);
        try {
            System.out.print("What is your user name? ");
            jd.setUsername(input.next());
            System.out.print("What is your password? ");
            jd.setPassword(input.next());
        } catch (Exception e) {
            log.log(Level.SEVERE, "The system encountered an exception while attempting to login");
        } finally {
              input.close();
        }

        jd.setJob("TestREST");
        jd.setServer("http://YOUR-SERVER");
        jd.setPort("YOUR-PORT");
        // set the parameters
        List<NameValuePair> parameters = jd.getParameters();
        parameters.add(new BasicNameValuePair("SONAR-TARGET", "true"));
        parameters.add(new BasicNameValuePair("RELEASE", "1311.1.1"));
        parameters.add(new BasicNameValuePair("REVISION", "HEAD"));
        // run the job
        JenkinsPoster jp = new JenkinsPoster(jd);
        log.info("executing postJenkinsJob");
        jp.postJenkinsJob();
        log.info("executed postJenkinsJob");
    }   
}

The JobData class (holds information about the job you need to run)

JobData 类(保存有关您需要运行的作业的信息)

package stackoverflow.answer;

import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;

public class JobData {
    private String username;
    private String password;
    private String token;
    private String server;
    private String port;
    private String job;
    private List<NameValuePair> parameters;

    public JobData() {
        parameters = new ArrayList<NameValuePair>();
    }

    public String getUsername() {return username;}
    public void setUsername(String username) {this.username = username;}
    public String getPassword() {return password;}
    public void setPassword(String password) {this.password = password;}
    public String getToken() {return token;}
    public void setToken(String token) {this.token = token;}
    public String getServer() {return server;}
    public void setServer(String server) {this.server = server;}
    public String getPort() {return port;}
    public void setPort(String port) {this.port = port;}
    public String getJob() {return job;}
    public void setJob(String job) {this.job = job;}
    public List<NameValuePair> getParameters() {return parameters;}
}

The JenkinsPoster class (contains the business logic to connect to Jenkins and POST the job)

JenkinsPoster 类(包含连接到 Jenkins 和 POST 作业的业务逻辑)

package stackoverflow.answer;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.logging.Logger;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.util.EntityUtils;

public class JenkinsPoster {
    final Logger log = Logger.getLogger(getClass().getCanonicalName());
    private JobData jd;

    public JenkinsPoster(JobData jobData) {
        this.jd = jobData;
    }

    @SuppressWarnings("deprecation")
    public void postJenkinsJob() throws UnsupportedEncodingException {
        log.entering(getClass().getCanonicalName(), "JenkinsPoster");
        // Jenkins url
        String jenkinsUrl = String.format("%s:%s/jenkins", jd.getServer(), jd.getPort());
        log.info("url = " + jenkinsUrl);
        // Create your httpclient
        DefaultHttpClient client = new DefaultHttpClient();
        // Then provide the right credentials
        client.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
        new UsernamePasswordCredentials(jd.getUsername(), jd.getPassword()));
        // Generate BASIC scheme object and stick it to the execution context
        BasicScheme basicAuth = new BasicScheme();
        BasicHttpContext context = new BasicHttpContext();
        context.setAttribute("preemptive-auth", basicAuth);
        // Add as the first (because of the zero) request interceptor
        // It will first intercept the request and preemptively initialize the authentication scheme if there is not
        client.addRequestInterceptor(new PreemptiveAuth(), 0);

        // Post the request to start the build
        List<NameValuePair> parameters = jd.getParameters();
        UrlEncodedFormEntity uefe = null;
        String buildUrl = "";
        if (parameters.isEmpty()) {
            buildUrl = jenkinsUrl + "/job/" + jd.getJob() + "/build";
        }
        else {
            buildUrl = jenkinsUrl + "/job/" + jd.getJob() + "/buildWithParameters";
            uefe = new UrlEncodedFormEntity(parameters);
        }
        HttpPost post = new HttpPost(buildUrl);
        post.setHeader("User-Agent", "Mozilla/5.0");
        if (uefe != null) {
            post.setEntity(uefe);
        }

        try {
            // Execute your request with the given context
            HttpResponse response = client.execute(post, context);
            HttpEntity entity = response.getEntity();
            log.info(EntityUtils.toString(entity));
            EntityUtils.consume(entity);
        }
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally {
            client.close();
        }
        log.exiting(getClass().getCanonicalName(), "JenkinsPoster");
    }
}

... and finally, the PreemptiveAuth class that the previous class uses to preemptively login to Jenkins.

... 最后,上一个类用来抢先​​登录 Jenkins 的 PreemptiveAuth 类。

package stackoverflow.answer;
import java.io.IOException;

import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;

public class PreemptiveAuth implements HttpRequestInterceptor {

    public void process(HttpRequest request, HttpContext context)
            throws HttpException, IOException {
        // Get the AuthState
        AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);

        // If no auth scheme available yet, try to initialize it preemptively
        if (authState.getAuthScheme() == null) {
            AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth");
            CredentialsProvider credsProvider = (CredentialsProvider) context
                    .getAttribute(ClientContext.CREDS_PROVIDER);
            HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
            if (authScheme != null) {
                Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost
                        .getPort()));
                if (creds == null) {
                    throw new HttpException("No credentials for preemptive authentication");
                }
                authState.setAuthScheme(authScheme);
                authState.setCredentials(creds);
            }
        }
    }
}

回答by brandonscript

Documentation on handling Jenkins' REST API authentication can be found here:

可以在此处找到有关处理 Jenkins 的 REST API 身份验证的文档:

https://wiki.jenkins-ci.org/display/JENKINS/Authenticating+scripted+clients

https://wiki.jenkins-ci.org/display/JENKINS/Authenticating+scripted+clients