You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jenkins Pipeline中withCredentials不同凭证类型的使用故障排查

Jenkins Pipeline访问Grafana的认证问题排查与解决

问题背景

我尝试通过Jenkins Pipeline,使用withCredentials插件结合httpRequest步骤访问Grafana,Jenkins中配置的凭证为Grafana管理员的「用户名密码」类型。


第一阶段错误:凭证类型不匹配

初始代码片段

script.withCredentials([script.string(
                    credentialsId: "${credentialsEnvironment}",
                    variable: 'usernamePassword')
            ]) {

                String auth = "${script.usernamePassword}".bytes.encodeBase64().toString()
                Map params = defaultArgs + httpParams
                def response = []
                try {
                    response = script.httpRequest(
                            params +
                            [
                                url: "${apiEndpoint}/${url}",
                                customHeaders: [[name: 'Authorization', value: "Basic ${auth}"]]
                            ]
                    )

                    if (response && (response.status != 200 && response.status != 201)) {
                        script.echo "Response status: ${response.status}"
                        script.echo "Response message: ${response.content}"
                    }
                    return response
                } catch (ex) {
                    LogUtil.echoStacktraceToScript(script, ex)
                    throw new ObservabilityException("Error: An exception occured when sending request with params: ${params}", ex)
                }
            }

执行异常日志

10:49:56  credentialsEnvironment: sb_1365_kraken_monitoring_scp_west_zone01_z01_grafana_admin
10:49:56  [Pipeline] withCredentials
10:49:56  [Pipeline] // withCredentials
10:49:56  [Pipeline] echo
10:49:56  org.jenkinsci.plugins.credentialsbinding.impl.CredentialNotFoundException: Credentials 'sb_1365_kraken_monitoring_scp_west_zone01_z01_grafana_admin' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected
10:49:56    at org.jenkinsci.plugins.credentialsbinding.MultiBinding.getCredentials(MultiBinding.java:208)
10:49:56    at org.jenkinsci.plugins.credentialsbinding.impl.StringBinding.bindSingle(StringBinding.java:57)
10:49:56    at org.jenkinsci.plugins.credentialsbinding.Binding.bind(Binding.java:149)
10:49:56    at org.jenkinsci.plugins.credentialsbinding.impl.BindingStep$Execution2.doStart(BindingStep.java:132)
10:49:56    at org.jenkinsci.plugins.workflow.steps.GeneralNonBlockingStepExecution.lambda$run$0(GeneralNonBlockingStepExecution.java:77)
10:49:56    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
10:49:56    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
10:49:56    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
10:49:56    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
10:49:56    at java.base/java.lang.Thread.run(Thread.java:829)

原因

代码中使用script.string()绑定凭证,但实际配置的是「用户名密码」类型,两者类型不匹配,导致抛出凭证类型错误。


第二阶段错误:401未授权

修改后的代码片段

script.println "Attempting to use ${credentialsEnvironment}"

            script.withCredentials([script.usernamePassword(
                    credentialsId: "${credentialsEnvironment}",
                    passwordVariable: 'passw', usernameVariable: 'user')
            ]) {

                script.println "user: ${script.user}"
                String auth = "${script.user}".bytes.encodeBase64().toString()
                script.println "auth: ${auth}"

                Map params = defaultArgs + httpParams
                def response = []
                try {
                    response = script.httpRequest(
                            params +
                            [
                                url: "${apiEndpoint}/${url}",
                                customHeaders: [[name: 'Authorization', value: "Basic ${auth}"]]
                            ]
                    )

                    if (response && (response.status != 200 && response.status != 201)) {
                        script.echo "Response status: ${response.status}"
                        script.echo "Response message: ${response.content}"
                    }
                    return response
                } catch (ex) {
                    LogUtil.echoStacktraceToScript(script, ex)
                    throw new ObservabilityException("Error: An exception occured when sending request with params: ${params}", ex)
                }
            }

执行错误日志

credentialsEnvironment: sb_1365_kraken_monitoring_scp_west_zone01_z01_grafana_admin
13:05:52  [Pipeline] echo
13:05:52  Attempting to use sb_1365_kraken_monitoring_scp_west_zone01_z01_grafana_admin
13:05:52  [Pipeline] withCredentials
13:05:52  Masking supported pattern matches of $user or $passw
13:05:52  [Pipeline] {
13:05:52  [Pipeline] echo
13:05:52  user: ****
13:05:52  [Pipeline] echo
13:05:52  auth: YWRtaW50ZXN0
13:05:52  [Pipeline] httpRequest
13:05:52  HttpMethod: GET
13:05:52  URL: https://plg-grafana-sb-1365-kraken-monitoring.apps.scp-west-zone01-z01.net/api/folders
13:05:52  Content-Type: application/json
13:05:52  Accept: application/json
13:05:52  Authorization: *****
13:05:52  Sending request to url: https://plg-grafana-sb-1365-kraken-monitoring.apps.scp-west-zone01-z01.net/api/folders
13:05:52  Response Code: HTTP/1.1 401 Unauthorized
13:05:52  Success: Status code 401 is in the accepted range: 100:500
13:05:52  [Pipeline] echo
13:05:52  Response status: 401
13:05:52  [Pipeline] echo
13:05:52  Response message: {"message":"Invalid Basic Auth Header","traceID":""}

原因

Basic认证的格式要求是用户名:密码的字符串经过Base64编码,但修改后的代码只对用户名单独编码,缺少密码部分,导致认证头部无效,触发401未授权错误。


解决方案

修正认证字符串的生成逻辑,按用户名:密码的格式拼接后再进行Base64编码:

script.println "Attempting to use ${credentialsEnvironment}"

script.withCredentials([script.usernamePassword(
        credentialsId: "${credentialsEnvironment}",
        passwordVariable: 'passw', usernameVariable: 'user')
]) {
    // 按Basic认证要求拼接用户名和密码
    String authString = "${script.user}:${script.passw}"
    // 对拼接后的字符串进行Base64编码
    String auth = authString.bytes.encodeBase64().toString()
    script.println "auth: ${auth}"

    Map params = defaultArgs + httpParams
    def response = []
    try {
        response = script.httpRequest(
                params +
                [
                    url: "${apiEndpoint}/${url}",
                    customHeaders: [[name: 'Authorization', value: "Basic ${auth}"]]
                ]
        )

        if (response && (response.status != 200 && response.status != 201)) {
            script.echo "Response status: ${response.status}"
            script.echo "Response message: ${response.content}"
        }
        return response
    } catch (ex) {
        LogUtil.echoStacktraceToScript(script, ex)
        throw new ObservabilityException("Error: An exception occured when sending request with params: ${params}", ex)
    }
}

关键修正点

  1. 使用script.usernamePassword()正确绑定「用户名密码」类型的凭证,分别获取用户名和密码变量
  2. 生成认证字符串时严格遵循用户名:密码的格式,再进行Base64编码,而非单独编码用户名

内容的提问来源于stack exchange,提问作者Kaliyug Antagonist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 17:35:14