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

时间:2013-12-08 21:31:21

标签: java rest jenkins

所有

我需要能够使用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");
}

}

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

2 个答案:

答案 0 :(得分:8)

在对几个网站进行研究之后,我设法将足够的信息拼凑起来以提出解决方案。我正在使用旧的HTTP身份验证代码,在某些情况下不推荐使用它 - 但它没有其他任何东西可以使用。如果有人有更好的解决方案,我会有兴趣看到它。无论如何,这是:

主类:

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");
    }   
}

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;}
}

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");
    }
}

...最后,前一个类用于抢先登录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);
            }
        }
    }
}

答案 1 :(得分:0)

有关处理Jenkins REST API身份验证的文档,请访问:

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