如何解析来自Www-Authenticate的值

时间:2018-01-25 22:03:29

标签: c#

我需要解析的值是Www-Authenticate HTTP响应标头的一部分:

realm="https://auth.docker.io/token",service="registry.docker.io",scope="registry:catalog:*"

我想获得一个包含以下值的字典(或类似字体):

{ "realm", "https://auth.docker.io/token" },
{ "service", "registry.docker.io" },
{ "scope", "registry:catalog:*"},

.NET中是否有一些实用程序可以解析它?

这就是我获得价值的方式。在调用Web API服务时,我收到了一个未经授权的响应(这是预期的):

var httpClient = new HttpClient();

//This is a public url
var response = await httpClient.GetAsync("https://registry-1.docker.io/v2/_catalog")

// This is the value that needs to be parsed
string parameter = response.Headers.WwwAuthenticate.FirstOrDefault().Parameter; 

我尝试/看过的事情:

  • 正则表达(痛苦和感觉有点矫枉过正)
  • 手动解析(有条不紊地遍历字符串。这让我感到非常痛苦,因为我不得不寻找逃脱的角色
  • 使用string.Split。这样安全吗?如果引用值中有逗号,该怎么办?

2 个答案:

答案 0 :(得分:1)

使用RFC6750RFC2616中定义的架构,下面提供了一个更为精确的解析器实现。该解析器考虑了字符串可能包含=,和/或转义的"的可能性。

internal class AuthParamParser
{
  private string _buffer;
  private int _i;

  private AuthParamParser(string param)
  {
    _buffer = param;
    _i = 0;
  }

  public static Dictionary<string, string> Parse(string param)
  {
    var state = new AuthParamParser(param);
    var result = new Dictionary<string, string>();
    var token = state.ReadToken();
    while (!string.IsNullOrEmpty(token))
    {
      if (!state.ReadDelim('='))
        return result;
      result.Add(token, state.ReadString());
      if (!state.ReadDelim(','))
        return result;
      token = state.ReadToken();
    }
    return result;
  }

  private string ReadToken()
  {
    var start = _i;
    while (_i < _buffer.Length && ValidTokenChar(_buffer[_i]))
      _i++;
    return _buffer.Substring(start, _i - start);
  }

  private bool ReadDelim(char ch)
  {
    while (_i < _buffer.Length && char.IsWhiteSpace(_buffer[_i]))
      _i++;
    if (_i >= _buffer.Length || _buffer[_i] != ch)
      return false;
    _i++;
    while (_i < _buffer.Length && char.IsWhiteSpace(_buffer[_i]))
      _i++;
    return true;
  }

  private string ReadString()
  {
    if (_i < _buffer.Length && _buffer[_i] == '"')
    {
      var buffer = new StringBuilder();
      _i++;
      while (_i < _buffer.Length)
      {
        if (_buffer[_i] == '\\' && (_i + 1) < _buffer.Length)
        {
          _i++;
          buffer.Append(_buffer[_i]);
          _i++;
        }
        else if (_buffer[_i] == '"')
        {
          _i++;
          return buffer.ToString();
        }
        else
        {
          buffer.Append(_buffer[_i]);
          _i++;
        }
      }
      return buffer.ToString();
    }
    else
    {
      return ReadToken();
    }
  }

  private bool ValidTokenChar(char ch)
  {
    if (ch < 32)
      return false;
    if (ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@'
      || ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"'
      || ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '='
      || ch == '{' || ch == '}' || ch == 127 || ch == ' ' || ch == '\t')
      return false;
    return true;
  }
}

答案 1 :(得分:0)

从Forty3指示我的答案中,我想出了以下内容:

public static class AuthenticateParser
{
    public static IDictionary<string, string> Parse(string value)
    {
        //https://stackoverflow.com/questions/45516717/extracting-and-parsing-the-www-authenticate-header-from-httpresponsemessage-in/45516809#45516809
        string[] commaSplit = value.Split(", ".ToCharArray());

        return commaSplit
            .ToDictionary(GetKey, GetValue);
    }

    private static string GetKey(string pair)
    {
        int equalPos = pair.IndexOf("=");

        if (equalPos < 1)
            throw new FormatException("No '=' found.");

        return  pair.Substring(0, equalPos);
    }

    private static string GetValue(string pair)
    {
        int equalPos = pair.IndexOf("=");

        if (equalPos < 1)
            throw new FormatException("No '=' found.");

        string value = pair.Substring(equalPos + 1).Trim();

        //Trim quotes
        if (value.StartsWith("\"") && value.EndsWith("\""))
        {
            value = value.Substring(1, value.Length - 2);
        }

        return value;
    }
}