整理代码,补充注释,消除编译警告

This commit is contained in:
智能大石头 2025-07-16 00:37:30 +08:00
parent 7097e3d7ee
commit ec70e12618
14 changed files with 158 additions and 145 deletions

View File

@ -69,7 +69,7 @@ public static class PagerHelper
if (url.Length == 0) return action;
if (action != null && !action.Contains('?')) action += '?';
return action + url.Put(true);
return action + url.Return(true);
}
/// <summary>过滤特殊字符,避免注入</summary>

View File

@ -1,170 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using NewLife.Cube.Entity;
using NewLife.Http;
using NewLife.Serialization;
namespace NewLife.Web.OAuth
namespace NewLife.Web.OAuth;
/// <summary>IdentityServer4身份验证提供者</summary>
/// <remarks>
/// </remarks>
public class Id4Client : OAuthClient
{
/// <summary>IdentityServer4身份验证提供者</summary>
private static NewLife.Caching.MemoryCache _cache = new Caching.MemoryCache { Expire = 60 * 60 };
#region
/// <summary>租户。默认common</summary>
/// <remarks>
/// 请求路径中的 {tenant} 值可用于控制哪些用户可以登录应用程序。 可以使用的值包括 common、organizations、consumers 和租户标识符。
/// </remarks>
public class Id4Client : OAuthClient
public String Tenant { get; set; } = "common";
#endregion
/// <summary>实例化</summary>
public Id4Client()
{
private static NewLife.Caching.MemoryCache _cache = new Caching.MemoryCache { Expire = 60 * 60 };
#region
/// <summary>租户。默认common</summary>
/// <remarks>
/// 请求路径中的 {tenant} 值可用于控制哪些用户可以登录应用程序。 可以使用的值包括 common、organizations、consumers 和租户标识符。
/// </remarks>
public String Tenant { get; set; } = "common";
#endregion
AuthUrl = "authorize?client_id={key}&response_type={response_type}&scope={scope}&redirect_uri={redirect}&state={state}&code_challenge={code_challenge}&code_challenge_method=S256";
//AccessUrl = "token?grant_type=authorization_code&client_id={key}&client_secret={secret}&code={code}&redirect_uri={redirect}";
//LogoutUrl = "logout?post_logout_redirect_uri={redirect}";
//UserUrl = "https://graph.microsoft.com/oidc/userinfo?access_token={token}&openid={openid}&lang=zh_CN";
/// <summary>实例化</summary>
public Id4Client()
OpenIDUrl = "userinfo";
Scope = "openid profile email";
}
/// <summary>发起请求,获取内容</summary>
/// <param name="action"></param>
/// <param name="url"></param>
/// <returns></returns>
protected override String GetHtml(String action, String url)
{
if (action == nameof(GetAccessToken))
{
var p = url.IndexOf('?');
var dic = url[(p + 1)..].SplitAsDictionary("=", "&").ToDictionary(e => e.Key, e => HttpUtility.UrlDecode(e.Value));
url = url[..p];
//WriteLog(dic.ToJson(true));
var state = HttpContext.Current.Request.Query["state"].FirstOrDefault();
if (!state.IsNullOrEmpty() && _cache.ContainsKey(state))
dic.Add("code_verifier", _cache.Get<String>(state));
AuthUrl = "authorize?client_id={key}&response_type={response_type}&scope={scope}&redirect_uri={redirect}&state={state}&code_challenge={code_challenge}&code_challenge_method=S256";
//AccessUrl = "token?grant_type=authorization_code&client_id={key}&client_secret={secret}&code={code}&redirect_uri={redirect}";
//LogoutUrl = "logout?post_logout_redirect_uri={redirect}";
//UserUrl = "https://graph.microsoft.com/oidc/userinfo?access_token={token}&openid={openid}&lang=zh_CN";
var client = GetClient();
var html = client.PostFormAsync(url, dic).Result;
if (html.IsNullOrEmpty()) return null;
OpenIDUrl = "userinfo";
html = html.Trim();
if (Log != null && Log.Enable) WriteLog(html);
Scope = "openid profile email";
return html;
}
/// <summary>发起请求,获取内容</summary>
/// <param name="action"></param>
/// <param name="url"></param>
/// <returns></returns>
protected override String GetHtml(String action, String url)
else if (action == nameof(GetUserInfo))
{
if (action == nameof(GetAccessToken))
{
var p = url.IndexOf('?');
var dic = url[(p + 1)..].SplitAsDictionary("=", "&").ToDictionary(e => e.Key, e => HttpUtility.UrlDecode(e.Value));
url = url[..p];
//WriteLog(dic.ToJson(true));
var state = HttpContext.Current.Request.Query["state"].FirstOrDefault();
if (!state.IsNullOrEmpty() && _cache.ContainsKey(state))
dic.Add("code_verifier", _cache.Get<string>(state));
var client = GetClient();
var html = client.PostFormAsync(url, dic).Result;
if (html.IsNullOrEmpty()) return null;
html = html.Trim();
if (Log != null && Log.Enable) WriteLog(html);
return html;
}
else if (action == nameof(GetUserInfo))
{
var client = GetClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
var html = client.GetStringAsync(url).Result;
if (html.IsNullOrEmpty()) return null;
html = html.Trim();
if (Log != null && Log.Enable) WriteLog(html);
return html;
}
return base.GetHtml(action, url);
}
public override String GetOpenID()
{
var url= Server.EnsureEnd("/") + OpenIDUrl.TrimStart('/');
var client = GetClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
var html = client.GetStringAsync(url).Result;
if (html.IsNullOrEmpty()) return null;
if (!html.IsNullOrEmpty())
html = html.Trim();
if (Log != null && Log.Enable) WriteLog(html);
return html;
}
return base.GetHtml(action, url);
}
/// <summary>获取OpenID</summary>
public override String GetOpenID()
{
var url = Server.EnsureEnd("/") + OpenIDUrl.TrimStart('/');
var client = GetClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
var html = client.GetStringAsync(url).Result;
if (!html.IsNullOrEmpty())
{
var dic = GetNameValues(html);
if (dic.ContainsKey("sub")) OpenID = dic["sub"].Trim();
if (dic.ContainsKey("name")) UserName = dic["name"].Trim();
}
return null;
}
/// <summary>获取Url替换变量</summary>
/// <param name="name"></param>
/// <param name="url"></param>
/// <returns></returns>
protected override String GetUrl(String name, String url)
{
var baseUrl = base.GetUrl(name, url);
var p = baseUrl.IndexOf('?');
var dic = baseUrl[(p + 1)..].SplitAsDictionary("=", "&").ToDictionary(e => e.Key, e => HttpUtility.UrlDecode(e.Value));
if (dic.ContainsKey("state"))
{
var state = dic["state"];
_ = _cache.GetOrAdd(state, k =>
{
var dic = GetNameValues(html);
if (dic.ContainsKey("sub")) OpenID = dic["sub"].Trim();
if (dic.ContainsKey("name")) UserName = dic["name"].Trim();
}
return null;
}
/// <summary>获取Url替换变量</summary>
/// <param name="name"></param>
/// <param name="url"></param>
/// <returns></returns>
protected override String GetUrl(String name, String url)
{
var baseUrl = base.GetUrl(name, url);
var p = baseUrl.IndexOf('?');
var dic = baseUrl[(p + 1)..].SplitAsDictionary("=", "&").ToDictionary(e => e.Key, e => HttpUtility.UrlDecode(e.Value));
if (dic.ContainsKey("state"))
{
var state = dic["state"];
_ = _cache.GetOrAdd(state, k =>
{
var codeVerifier = GenerateRandomDataBase64url(32);
var codeChallenge = Base64UrlEncodeNoPadding(Sha256Ascii(codeVerifier));
baseUrl = baseUrl.Replace("{code_challenge}", codeChallenge);
return codeVerifier;
});
}
return baseUrl;
var codeVerifier = GenerateRandomDataBase64url(32);
var codeChallenge = Base64UrlEncodeNoPadding(Sha256Ascii(codeVerifier));
baseUrl = baseUrl.Replace("{code_challenge}", codeChallenge);
return codeVerifier;
});
}
/// <summary>
/// Returns URI-safe data with a given input length.
/// </summary>
/// <param name="length">Input length (nb. output will be longer)</param>
private static string GenerateRandomDataBase64url(int length)
{
byte[] bytes = RandomNumberGenerator.GetBytes(length);
return Base64UrlEncodeNoPadding(bytes);
}
/// <summary>
/// Returns the SHA256 hash of the input string, which is assumed to be ASCII.
/// </summary>
private static byte[] Sha256Ascii(string text)
{
byte[] bytes = Encoding.ASCII.GetBytes(text);
return SHA256.HashData(bytes);
}
/// <summary>
/// Base64url no-padding encodes the given input buffer.
/// </summary>
private static string Base64UrlEncodeNoPadding(byte[] buffer)
{
string base64 = Convert.ToBase64String(buffer);
// Converts base64 to base64url.
base64 = base64.Replace("+", "-");
base64 = base64.Replace("/", "_");
// Strips padding.
base64 = base64.Replace("=", "");
return base64;
}
return baseUrl;
}
/// <summary>
/// Returns URI-safe data with a given input length.
/// </summary>
/// <param name="length">Input length (nb. output will be longer)</param>
private static String GenerateRandomDataBase64url(Int32 length)
{
var bytes = RandomNumberGenerator.GetBytes(length);
return Base64UrlEncodeNoPadding(bytes);
}
/// <summary>
/// Returns the SHA256 hash of the input string, which is assumed to be ASCII.
/// </summary>
private static Byte[] Sha256Ascii(String text)
{
var bytes = Encoding.ASCII.GetBytes(text);
return SHA256.HashData(bytes);
}
/// <summary>
/// Base64url no-padding encodes the given input buffer.
/// </summary>
private static String Base64UrlEncodeNoPadding(Byte[] buffer)
{
var base64 = Convert.ToBase64String(buffer);
// Converts base64 to base64url.
base64 = base64.Replace("+", "-");
base64 = base64.Replace("/", "_");
// Strips padding.
base64 = base64.Replace("=", "");
return base64;
}
}

View File

@ -136,6 +136,7 @@ public class UserController : EntityController<User, UserModel>
/// <param name="passwordService"></param>
/// <param name="cacheProvider"></param>
/// <param name="userService"></param>
/// <param name="tracer"></param>
public UserController(PasswordService passwordService, ICacheProvider cacheProvider, UserService userService, ITracer tracer)
{
_passwordService = passwordService;
@ -867,6 +868,7 @@ public class UserController : EntityController<User, UserModel>
return View(model);
}
/// <summary>插入实体</summary>
protected override Int32 OnInsert(User entity)
{
var ef = base.OnInsert(entity);

View File

@ -3,20 +3,28 @@ using XCode.DataAccessLayer;
namespace NewLife.Cube.Areas.Admin.Models;
/// <summary>数据库实体模型</summary>
public class DbEntitiesModel
{
/// <summary>名称</summary>
public String Name { get; set; }
/// <summary>实体集合</summary>
public IList<DbEntityModel> Entities { get; set; }
}
/// <summary>数据库实体模型</summary>
public class DbEntityModel
{
/// <summary>名称</summary>
public String Name { get; set; }
/// <summary>实体工厂</summary>
public IEntityFactory Factory { get; set; }
/// <summary>数据表</summary>
public IDataTable Table { get; set; }
/// <summary>数据行数</summary>
public Int64 Count { get; set; }
}

View File

@ -2,9 +2,12 @@
namespace NewLife.Cube.Areas.Admin.Models;
/// <summary>数据库表模型</summary>
public class DbTablesModel
{
/// <summary>名称</summary>
public String Name { get; set; }
/// <summary>数据表集合</summary>
public IList<IDataTable> Tables { get; set; }
}

View File

@ -209,7 +209,7 @@ public class UserInfo
sb.AppendFormat("{0}#{1}", item.Key, item.Value);
}
Permission = sb.Put(true);
Permission = sb.Return(true);
}
/// <summary>

View File

@ -1,7 +1,11 @@
namespace NewLife.Cube.Charts.Models;
/// <summary>地理坐标点</summary>
public class GeoPoint
{
/// <summary>经度</summary>
public Double Longitude { get; set; }
/// <summary>纬度</summary>
public Double Latitude { get; set; }
}

View File

@ -158,6 +158,7 @@ public class SeriesGraph : Series
/// <summary>图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等。</summary>
public Object Label { get; set; }
/// <summary>边的标签</summary>
public Object EdgeLabel { get; set; }
/// <summary></summary>

View File

@ -8,6 +8,7 @@ namespace NewLife.Cube.Modules;
[DisplayName("魔方适配器")]
public class DefaultAdapter : IAdapter
{
/// <summary>编码配置对象</summary>
public Object Encode(Dictionary<String, Object> dic, Dictionary<ViewKinds, FieldCollection> fieldCollections) => dic;
/// <summary>序列化配置对象</summary>

View File

@ -5,6 +5,7 @@ namespace NewLife.Cube.Modules;
/// <summary>魔方适配器插件。用户给前端提供合适的页面配置</summary>
public interface IAdapter
{
/// <summary>编码配置对象</summary>
Object Encode(Dictionary<String, Object> dic, Dictionary<ViewKinds, FieldCollection> fieldCollections);
/// <summary>序列化配置对象</summary>

View File

@ -26,6 +26,7 @@ public class AccessService
/// <param name="ua"></param>
/// <param name="ip"></param>
/// <param name="user"></param>
/// <param name="session"></param>
/// <returns></returns>
public AccessRule Valid(String url, UserAgentParser ua, String ip, IUser user, IDictionary<String, Object> session)
{

View File

@ -64,7 +64,7 @@ public class DbXmlRepository : DisposeBase, IXmlRepository
/// <summary>自动删除已过期的key</summary>
/// <param name="state"></param>
private void TrimExpired(Object? state)
private void TrimExpired(Object state)
{
var list = Parameter.FindAllByUserID(0, _key);
foreach (var item in list)

View File

@ -291,6 +291,6 @@ public class DataField
/// <param name="obj"></param>
/// <returns></returns>
[return: NotNullIfNotNull(nameof(obj))]
public static implicit operator DataField?(FieldItem obj) => !obj.Equals(null) ? new DataField(obj) : null;
public static implicit operator DataField(FieldItem obj) => !obj.Equals(null) ? new DataField(obj) : null;
#endregion
}

View File

@ -308,7 +308,7 @@ public class ListField : DataField
sb.Append(HttpUtility.HtmlEncode(linkName));
sb.Append("</a>");
var link = sb.Put(true);
var link = sb.Return(true);
return Replace(link, data, EnumModes.String);
}