整理代码,补充注释,消除编译警告
This commit is contained in:
parent
7097e3d7ee
commit
ec70e12618
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@ public class UserInfo
|
|||
sb.AppendFormat("{0}#{1}", item.Key, item.Value);
|
||||
}
|
||||
|
||||
Permission = sb.Put(true);
|
||||
Permission = sb.Return(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -158,6 +158,7 @@ public class SeriesGraph : Series
|
|||
/// <summary>图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等。</summary>
|
||||
public Object Label { get; set; }
|
||||
|
||||
/// <summary>边的标签</summary>
|
||||
public Object EdgeLabel { get; set; }
|
||||
|
||||
/// <summary></summary>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue