使用cURL以用户名和密码登录Gitlab

使用cURL以用户名和密码登录Gitlab,curl,gitlab,csrf,Curl,Gitlab,Csrf,为了针对Docker中的Gitlab实例测试命令行工具,我想使用用户名和密码登录Gitlab,并获取创建的会话来验证我的API请求 因此,我采取以下行动: 使用curl-i卷曲用户登录页面http://localhost:8080/users/sign_in -s 从标题中获取\u gitlab\u会话 从登录表单中获取authenticity\u令牌 使用发送第二个curl请求 但是我得到的不是有效的用户登录,而是 422 - The change you requested was reje

为了针对Docker中的Gitlab实例测试命令行工具,我想使用用户名和密码登录Gitlab,并获取创建的会话来验证我的API请求

因此,我采取以下行动:

  • 使用
    curl-i卷曲用户登录页面http://localhost:8080/users/sign_in -s
  • 从标题中获取
    \u gitlab\u会话
  • 从登录表单中获取
    authenticity\u令牌
  • 使用发送第二个curl请求
  • 但是我得到的不是有效的用户登录,而是

    422 - The change you requested was rejected.
    
    在日志文件里,我明白了

    ==> gitlab-rails/production.log <==
    Started POST "/users/sign_in" for 172.17.0.1 at 2017-12-23 00:22:16 +0000
    Processing by SessionsController#create as HTML
    Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"login"=>"root", "password"=>"[FILTERED]", "remember_me"=>"0"}}
    Can't verify CSRF token authenticity
    Completed 422 Unprocessable Entity in 125ms (ActiveRecord: 7.8ms)
    
    ==> gitlab-rails/production_json.log <==
    {"method":"POST","path":"/users/sign_in","format":"html","controller":"SessionsController",  
    "action":"create","status":422,"error":" 
    ActionController::InvalidAuthenticityToken:ActionController::InvalidAuthenticityToken",
    "duration":126.29,"view":0.0,"db":7.78,"time":"2017-12-23T00:22:16.039Z",
    "params":{"authenticity_token":"[FILTERED]","user":{"login":"root","password":"
    [FILTERED]","remember_me":"0"}},"remote_ip":"172.17.0.1",
    "user_id":1,"username":"root"}
    
    ==> gitlab-rails/production.log <==
    
    ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
      lib/gitlab/middleware/multipart.rb:93:in `call'
      lib/gitlab/request_profiler/middleware.rb:14:in `call'
      lib/gitlab/middleware/go.rb:18:in `call'
      lib/gitlab/etag_caching/middleware.rb:11:in `call'
      lib/gitlab/middleware/read_only.rb:31:in `call'
      lib/gitlab/request_context.rb:18:in `call'
      lib/gitlab/metrics/requests_rack_middleware.rb:27:in `call'
    
    ==>gitlab rails/production.log“[过滤]”,“用户”=>{“登录”=>“根”,“密码”=>“[过滤],“记住我”=>“0”}
    无法验证CSRF令牌的真实性
    在125ms内完成422个无法处理的实体(ActiveRecord:7.8ms)
    
    ==>gitlab rails/production\u json.log gitlab rails/production.log确切地说,
    ${cookie}
    中是什么?在我的测试中,登录页面中有4个cookie,其中3个看起来像令牌,如果成功登录至少还需要1个cookie,而不是
    \u gitlab\u session
    cookie,我也不会感到惊讶。但与其费心寻找所需cookie的正确组合,不如让curl自动处理cookie,例如
    --cookie jar
    --cookie
    (那么所有cookie都应该是正确的)-此外,登录页面上有3个不同的真实性标记,你确定你拿的是正确的吗?也许你的问题是取错了令牌。区分这3个标记非常困难,因此我建议改用脚本语言

    下面是一个使用PHP的测试工作示例(只需替换第10行和第11行的用户名和密码):


    通过其他答案和评论,我终于想出了以下解决方案:

    gitlab\u主机=”http://localhost:8080"
    gitlab_user=“root”
    gitlab_password=“12341234”
    #curl用于登录页面,以获取会话cookie和带有auth令牌的源
    body_header=$(curl-c cookies.txt-i“${gitlab_host}/users/sign_-in“-s)
    #grep的用户登录的身份验证令牌
    #不确定页面上的另一个令牌是否也会起作用-共有3个令牌
    csrf_token=$(echo$body_header | perl-ne'print“$1\n”if/new_user.*?authenticity_token”[[:blank:]value=“(.+?)”/”| sed-n 1p)
    #使用以前请求中的cookie和令牌,使用curl发送登录凭据
    curl-b cookies.txt-c cookies.txt-i“${gitlab\u host}/users/sign\u-in”\
    --数据“用户[登录]=${gitlab\u用户}和用户[密码]=${gitlab\u密码}”\
    --数据urlencode“真实性\u令牌=${csrf\u令牌}”
    #将curl GET请求发送到个人访问令牌页以获取身份验证令牌
    body_header=$(curl-H'user-agent:curl'-b cookies.txt-i“${gitlab_-host}/profile/personal_-access_-tokens”-s)
    csrf_-token=$(echo$body_-header | perl-ne'print“$1\n”if/authenticity_-token”[[:blank:]value=“(.+?)”/”;sed-n 1p)
    #curl POST发送“生成个人访问令牌表单”的请求
    #响应将是重定向,因此我们必须使用“-L`
    body_header=$(curl-L-bcookies.txt“${gitlab_host}/profile/personal_access_tokens”\
    --数据urlencode“真实性\u令牌=${csrf\u令牌}”\
    --数据'personal_access_token[name]=golab生成的和personal_access_token[expires_at]=&personal_access_token[scopes][]=api')
    #从响应HTML中刮取个人访问令牌
    personal_access_token=$(echo$body_header | perl-ne'print“$1\n”if/created personal access token”[[:blank:]value=“(.+?)”/”;sed-n 1p)
    
    根据,您现在可以使用会话cookie对API请求进行身份验证:

    curl——标题“私有令牌:${personal\u access\u Token}”https://gitlab.example.com/api/v4/projects
    
    一些提示:

    • 我首先混淆了
      curl-c文件
      (从标题中读取cookies并将其写入文件)和
      curl-b文件
      (使用文件中的cookies并随请求发送cookies)
    • 不幸的是,我没有找到与
      sed
      一起工作的正则表达式,因此我不得不在这里使用
      perl
    • 在Chrome中使用开发者控制台并将
      POST
      请求复制为curl命令非常有用:

    在gitlab中创建访问令牌或私有令牌,您可以在URL中使用它


    bash我得到了一个使用Python的稳定版本,并且:

    从urllib.parse导入urljoin
    导入请求
    进口bs4
    基本的http://gitlab.example.com/'
    用户名='me'
    密码='让我进来'
    #使用会话,以便在请求之间保留cookie。
    会话=请求。会话()
    #加载登录页面以获取CSRF令牌。
    response=session.get(urljoin(基本uri,'users/sign_-in'))
    响应。针对_状态()提出_
    #从登录页面提取CSRF令牌。
    soup=bs4.BeautifulSoup(response.text'html.parser')
    csrf_param=soup.find('meta',dict(name='csrf-param'))['content']
    csrf_token=soup.find('meta',dict(name='csrf-token'))['content']
    #登录。
    请求\u数据={
    “用户名”:用户名,
    “密码”:密码,
    csrf_参数:csrf_令牌}
    response=session.post(response.url,data=request\u data)
    响应。针对_状态()提出_
    #获取项目列表。
    response=session.get(urljoin(基本uri,'api/v4/projects'))
    打印(response.json())
    
    错误是说您的CSRF(真实性)令牌无效。您能再次检查是否正确提取它吗?再次检查后,您能尝试使用cookie jar吗?而不是手动包含标头?此脚本可能会启发您:(关于的完整讨论)虽然这个答案很可能是正确的,但我不能在我的案例中使用PHP。无论如何:感谢您提供了全面的答案!请参阅下面我的帖子,我最终是如何使用bash/perl解决的。@MichaelLihs perl应该完全能够做到这一点:)(编辑:现在我可以
    ==> gitlab-rails/production.log <==
    Started POST "/users/sign_in" for 172.17.0.1 at 2017-12-23 00:22:16 +0000
    Processing by SessionsController#create as HTML
    Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"login"=>"root", "password"=>"[FILTERED]", "remember_me"=>"0"}}
    Can't verify CSRF token authenticity
    Completed 422 Unprocessable Entity in 125ms (ActiveRecord: 7.8ms)
    
    ==> gitlab-rails/production_json.log <==
    {"method":"POST","path":"/users/sign_in","format":"html","controller":"SessionsController",  
    "action":"create","status":422,"error":" 
    ActionController::InvalidAuthenticityToken:ActionController::InvalidAuthenticityToken",
    "duration":126.29,"view":0.0,"db":7.78,"time":"2017-12-23T00:22:16.039Z",
    "params":{"authenticity_token":"[FILTERED]","user":{"login":"root","password":"
    [FILTERED]","remember_me":"0"}},"remote_ip":"172.17.0.1",
    "user_id":1,"username":"root"}
    
    ==> gitlab-rails/production.log <==
    
    ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
      lib/gitlab/middleware/multipart.rb:93:in `call'
      lib/gitlab/request_profiler/middleware.rb:14:in `call'
      lib/gitlab/middleware/go.rb:18:in `call'
      lib/gitlab/etag_caching/middleware.rb:11:in `call'
      lib/gitlab/middleware/read_only.rb:31:in `call'
      lib/gitlab/request_context.rb:18:in `call'
      lib/gitlab/metrics/requests_rack_middleware.rb:27:in `call'
    
    <?php
    declare(strict_types = 1);
    require_once ('hhb_.inc.php');
    $hc = new hhb_curl ( '', true );
    $html = $hc->exec ( 'https://gitlab.com/users/sign_in' )->getStdOut ();
    $domd = @DOMDocument::loadHTML ( $html );
    $inputs = getDOMDocumentFormInputs ( $domd, true, false ) ['new_user'];
    // var_dump ( $inputs );
    $inputs ['user[login]'] = '???';
    $inputs ['user[password]'] = '???';
    $html = $hc->setopt_array ( array (
            CURLOPT_POST => 1,
            CURLOPT_POSTFIELDS => http_build_query ( $inputs ) 
    ) )->exec ()->getStdOut ();
    echo $html;
    $domd = @DOMDocument::loadHTML ( $html );
    $xp = new DOMXPath ( $domd );
    foreach ( $xp->query ( '//div[contains(@class,"flash-alert")]' ) as $loginError ) {
        var_dump ( "login error: ", trim($loginError->textContent) );
    }
    
    function getDOMDocumentFormInputs(\DOMDocument $domd, bool $getOnlyFirstMatches = false, bool $getElements = true): array {
        // :DOMNodeList?
        if (! $getOnlyFirstMatches && ! $getElements) {
            throw new \InvalidArgumentException ( '!$getElements is currently only implemented for $getOnlyFirstMatches (cus im lazy and nobody has written the code yet)' );
        }
        $forms = $domd->getElementsByTagName ( 'form' );
        $parsedForms = array ();
        $isDescendantOf = function (\DOMNode $decendant, \DOMNode $ele): bool {
            $parent = $decendant;
            while ( NULL !== ($parent = $parent->parentNode) ) {
                if ($parent === $ele) {
                    return true;
                }
            }
            return false;
        };
        // i can't use array_merge on DOMNodeLists :(
        $merged = function () use (&$domd): array {
            $ret = array ();
            foreach ( $domd->getElementsByTagName ( "input" ) as $input ) {
                $ret [] = $input;
            }
            foreach ( $domd->getElementsByTagName ( "textarea" ) as $textarea ) {
                $ret [] = $textarea;
            }
            foreach ( $domd->getElementsByTagName ( "button" ) as $button ) {
                $ret [] = $button;
            }
            return $ret;
        };
        $merged = $merged ();
        foreach ( $forms as $form ) {
            $inputs = function () use (&$domd, &$form, &$isDescendantOf, &$merged): array {
                $ret = array ();
                foreach ( $merged as $input ) {
                    // hhb_var_dump ( $input->getAttribute ( "name" ), $input->getAttribute ( "id" ) );
                    if ($input->hasAttribute ( "disabled" )) {
                        // ignore disabled elements?
                        continue;
                    }
                    $name = $input->getAttribute ( "name" );
                    if ($name === '') {
                        // echo "inputs with no name are ignored when submitted by mainstream browsers (presumably because of specs)... follow suite?", PHP_EOL;
                        continue;
                    }
                    if (! $isDescendantOf ( $input, $form ) && $form->getAttribute ( "id" ) !== '' && $input->getAttribute ( "form" ) !== $form->getAttribute ( "id" )) {
                        // echo "this input does not belong to this form.", PHP_EOL;
                        continue;
                    }
                    if (! array_key_exists ( $name, $ret )) {
                        $ret [$name] = array (
                                $input 
                        );
                    } else {
                        $ret [$name] [] = $input;
                    }
                }
                return $ret;
            };
            $inputs = $inputs (); // sorry about that, Eclipse gets unstable on IIFE syntax.
            $hasName = true;
            $name = $form->getAttribute ( "id" );
            if ($name === '') {
                $name = $form->getAttribute ( "name" );
                if ($name === '') {
                    $hasName = false;
                }
            }
            if (! $hasName) {
                $parsedForms [] = array (
                        $inputs 
                );
            } else {
                if (! array_key_exists ( $name, $parsedForms )) {
                    $parsedForms [$name] = array (
                            $inputs 
                    );
                } else {
                    $parsedForms [$name] [] = $tmp;
                }
            }
        }
        unset ( $form, $tmp, $hasName, $name, $i, $input );
        if ($getOnlyFirstMatches) {
            foreach ( $parsedForms as $key => $val ) {
                $parsedForms [$key] = $val [0];
            }
            unset ( $key, $val );
            foreach ( $parsedForms as $key1 => $val1 ) {
                foreach ( $val1 as $key2 => $val2 ) {
                    $parsedForms [$key1] [$key2] = $val2 [0];
                }
            }
        }
        if ($getElements) {
            return $parsedForms;
        }
        $ret = array ();
        foreach ( $parsedForms as $formName => $arr ) {
            $ret [$formName] = array ();
            foreach ( $arr as $ele ) {
                $ret [$formName] [$ele->getAttribute ( "name" )] = $ele->getAttribute ( "value" );
            }
        }
        return $ret;
    }