ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddCryptoFile(Path.Combine(AppContext.BaseDirectory, "appsettings.json.crypto"), true);
});
ConfigurationBuilderExtension
public static class ConfigurationBuilderExtension
{
public static IConfigurationBuilder AddCryptoFile(this IConfigurationBuilder configurationBuilder, string path, bool reloadOnChange)
{
configurationBuilder.Add(new CryptoFileConfigurationSource(path, reloadOnChange));
return configurationBuilder;
}
}
CryptoFileConfigurationSource
public class CryptoFileConfigurationSource : IConfigurationSource
{
/// <summary>
/// 路径
/// </summary>
public string Path { get; set; }
/// <summary>
/// 是否启用热加载
/// </summary>
public bool ReloadOnChange { get; set; }
public CryptoFileConfigurationSource(string path, bool reloadOnChange)
{
Path = path;
ReloadOnChange = reloadOnChange;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new CryptoFileConfigurationProvider(this);
}
}
CryptoFileConfigurationProvider
public class CryptoFileConfigurationProvider : ConfigurationProvider
{
private static ConcurrentDictionary<string, string> CONFIG_CACHE = new ConcurrentDictionary<string, string>();
private CryptoFileConfigurationSource _source = null;
private string _random = string.Empty;
public CryptoFileConfigurationProvider(CryptoFileConfigurationSource source)
{
this._source = source;
string fileName = IOHelper.GetFileNameNoPath(_source.Path);
string filePath = _source.Path.Replace(fileName, string.Empty);
IFileProvider fileProvider = new PhysicalFileProvider(filePath);
if (_source.ReloadOnChange)
{
ChangeToken.OnChange(
() => fileProvider.Watch(fileName),
() =>
{
Load();
});
}
else
{
Load();
}
}
public override void Load()
{
if (!IOHelper.IsExistFilePath(_source.Path))
{
throw new ArgumentException($"{_source.Path}路径不存在");
}
string cryptoContent = IOHelper.GetFileContent(_source.Path);
//解密cryptoContent,获取json
string json = AESCryptoHelper.Decrypt(cryptoContent);
byte[] bytes = Encoding.UTF8.GetBytes(json);
MemoryStream ms = new MemoryStream(bytes);
this.Data = JsonConfigurationFileParser.Parse(ms);
}
}
JsonConfigurationFileParser
internal class JsonConfigurationFileParser
{
private JsonConfigurationFileParser() { }
private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _context = new Stack<string>();
private string _currentPath;
private JsonTextReader _reader;
public static IDictionary<string, string> Parse(Stream input)
=> new JsonConfigurationFileParser().ParseStream(input);
private IDictionary<string, string> ParseStream(Stream input)
{
_data.Clear();
_reader = new JsonTextReader(new StreamReader(input));
_reader.DateParseHandling = DateParseHandling.None;
var jsonConfig = JObject.Load(_reader);
VisitJObject(jsonConfig);
return _data;
}
private void VisitJObject(JObject jObject)
{
foreach (var property in jObject.Properties())
{
EnterContext(property.Name);
VisitProperty(property);
ExitContext();
}
}
private void VisitProperty(JProperty property)
{
VisitToken(property.Value);
}
private void VisitToken(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
VisitJObject(token.Value<JObject>());
break;
case JTokenType.Array:
VisitArray(token.Value<JArray>());
break;
case JTokenType.Integer:
case JTokenType.Float:
case JTokenType.String:
case JTokenType.Boolean:
case JTokenType.Bytes:
case JTokenType.Raw:
case JTokenType.Null:
VisitPrimitive(token.Value<JValue>());
break;
default:
throw new Exception("format error");
}
}
private void VisitArray(JArray array)
{
for (int index = 0; index < array.Count; index++)
{
EnterContext(index.ToString());
VisitToken(array[index]);
ExitContext();
}
}
private void VisitPrimitive(JValue data)
{
var key = _currentPath;
if (_data.ContainsKey(key))
{
throw new Exception("format error");
}
_data[key] = data.ToString(CultureInfo.InvariantCulture);
}
private void EnterContext(string context)
{
_context.Push(context);
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
private void ExitContext()
{
_context.Pop();
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
}