测试环境Jenkins版本进行了升级,发现代码中直接使用http调用Jenkins REST API的方法失效了,这边是除了GET以外其他的操作类的接口失效了,都是同样的错误,No valid crumb was included in the request。
搜索了具体的错误信息,发现都是一样的,就是说把Jenkins的CSRF给关闭了,但是我在Jenkins上面没有找到关闭CSRF的入口。就像下面这张图。
然后百度得知,Jenkins高版本关闭了页面上面取消CSRF防护的入口,本来也不推荐关闭。就想到失效的原因应该是之前的Jenkins一直没有打开CSRF防护,所以接口都是可以调用的。
// crumb头实体对象
@Data
private static class CrumbEntity implements Serializable {
private String _class;
private String crumb;
private String crumbRequestField;
}
具体获取crumb头信息的方法。这边能够获取到crumb请求头,然后我也将这个头信息也加到了请求上,还是报错。
/**
* 单独提取的一个公共方法
*
* @param url 请求地址
* @param httpRequest 请求方法对象
* @return
*/
private String customHttpMsg(String url, HttpRequest httpRequest) throws URISyntaxException, IOException {
URI uri = new URI(url);
HttpHost host = new HttpHost(uri.getHost(), uri.getPort());
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(uri.getHost(), uri.getPort()),
new UsernamePasswordCredentials(JENKINS_USERNAME, JENKINS_PASSWORD));
AuthCache authCache = new BasicAuthCache();
BasicScheme basicScheme = new BasicScheme();
authCache.put(host,basicScheme);
try (CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credentialsProvider).build()) {
HttpClientContext httpClientContext = HttpClientContext.create();
httpClientContext.setAuthCache(authCache);
CloseableHttpResponse response = httpClient.execute(host, httpRequest, httpClientContext);
return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}
}
// 获取crumb头
private CrumbEntity getCrumb() throws IOException, URISyntaxException {
String url = JENKINS_URL + "/crumbIssuer/api/json";
HttpGet httpGet = new HttpGet(url);
String res = customHttpMsg(url, httpGet);
return JSON.parseObject(res, CrumbEntity.class);
}
// 测试获取crumb头
@Test
public void testGetCrumb() throws IOException, URISyntaxException {
CrumbEntity crumb = getCrumb();
log.info("crumb is {}", crumb);
}
// 测试方法调用前添加crumb头
// 但是测试无效
@Test
public void testAddCrumb() throws IOException, URISyntaxException {
String url = JENKINS_URL+"/createItem/api/json?name="+JENKINS_PROJECT_NAME;
HttpPost httpPost = new HttpPost(url);
CrumbEntity crumb = getCrumb();
// 添加crumb头
httpPost.addHeader(new BasicHeader(crumb.getCrumbRequestField(), crumb.getCrumb()));
httpPost.setEntity(new StringEntity(JENKINS_PROJECT_XML, ContentType.create("text/xml", "utf-8")));
String res = customHttpMsg(url, httpPost);
log.info("res is {}", res);
}
添加这个token很简单,我这边也简单整理了下。
选择Jenkins当前用户。选择配置,添加用户对应的一个token信息。
这块,生成的token需要自己保存下来,关于token的好处Jenkins也进行了说明,大家可以看看。我自己认为可能是为了安全吧,比如我们对外提供一个token而不是密码,当我们想要收回这个操作权限的时候,我们只需要把token删除掉就好了,而不需要去修改密码。
OK,添加完成token,然后再去调用请求,发现可以通过,这个问题也算解决了。
// 推荐使用token
String JENKINS_TOKEN = "112c7c08043e02449e7fb97d253a657ede";
/**
* 单独提取的一个公共方法
*
* @param url 请求地址
* @param httpRequest 请求方法对象
* @return
*/
private String customHttpMsg(String url, HttpRequest httpRequest) throws URISyntaxException, IOException {
URI uri = new URI(url);
HttpHost host = new HttpHost(uri.getHost(), uri.getPort());
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
// 注意这边 我把密码换成了token
credentialsProvider.setCredentials(new AuthScope(uri.getHost(), uri.getPort()),
new UsernamePasswordCredentials(JENKINS_USERNAME, JENKINS_TOKEN));
AuthCache authCache = new BasicAuthCache();
BasicScheme basicScheme = new BasicScheme();
authCache.put(host,basicScheme);
try (CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credentialsProvider).build()) {
HttpClientContext httpClientContext = HttpClientContext.create();
httpClientContext.setAuthCache(authCache);
CloseableHttpResponse response = httpClient.execute(host, httpRequest, httpClientContext);
return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}
}
// 使用token 调用OK
@Test
public void testAddWithToken() throws IOException, URISyntaxException {
// http://192.168.1.107:8081/jenkins/createItem/api/json?name=test
String url = JENKINS_URL+"/createItem/api/json?name="+JENKINS_PROJECT_NAME;
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(JENKINS_PROJECT_XML, ContentType.create("text/xml", "utf-8")));
String res = customHttpMsg(url, httpPost);
log.info("res is {}", res);
}
项目地址:https://github.com/yzh19961031/blogDemo/tree/master/jenkins_api
所有的代码都在代码仓上面,大家有兴趣或者有疑问可以去看看。