解析WwwAuthenticate挑战字符串

时间:2018-03-21 21:46:32

标签: c# .net-core

我正在使用.NET Core 2.0在RESTful服务的客户端上工作。远程服务返回这样的挑战:

WwwAuthenticate: Bearer realm="https://somesite/auth",service="some site",scope="some scope"

需要转换为令牌请求,例如:

GET https://somesite/auth?service=some%20site&scope=some%20scope

使用AuthenticationHeaderValue解析标题以获取方案和参数很简单,但这只是realm="https://somesite/auth",service="some site",scope="some scope"字符串。如何轻松可靠地将其解析为单个realmservicescope组件?它不是JSON,所以使用NewtonSoft JsonConvert反序列化它不会起作用。我可以将它改为看起来像XML或JSON的东西,但这看起来非常h​​acky(更不用说不可靠)。

肯定有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

因为我没有看到非黑客的方式。也许这种 hacky 方式可能有所帮助

string input = @"WwwAuthenticate: Bearer realm=""https://somesite/auth"",service=""some site"",scope=""some, scope""";
var dict = Regex.Matches(input, @"[\W]+(\w+)=""(.+?)""").Cast<Match>()
          .ToDictionary(x => x.Groups[1].Value, x => x.Groups[2].Value);

var url = dict["realm"] + "?" + string.Join("&", dict.Where(x => x.Key != "realm").Select(x => x.Key + "=" + WebUtility.UrlEncode(x.Value)));

<强>输出

url => https://somesite/auth?service=some+site&scope=some%2C+scope

BTW:我在&#34;范围内添加了,&#34;

答案 1 :(得分:0)

  

可能的How to parse values from Www-Authenticate 副本

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