为了方便 ,特将这个 ManageProvider.cs 源码单独弄出来,便于上节和以后的使用

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Security.Principal;
    5. using System.Threading;
    6. using Microsoft.AspNetCore.Builder;
    7. using Microsoft.AspNetCore.Http;
    8. using Microsoft.Extensions.DependencyInjection.Extensions;
    9. using Microsoft.Net.Http.Headers;
    10. using NewLife.Cube.Extensions;
    11. using NewLife.Log;
    12. using NewLife.Model;
    13. using XCode;
    14. using XCode.Membership;
    15. using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection;
    16. using JwtBuilder = NewLife.Web.JwtBuilder;
    17. namespace NewLife.Cube
    18. {
    19. /// <inheritdoc />
    20. public class ManageProvider2 : ManageProvider
    21. {
    22. #region 静态实例
    23. internal static IHttpContextAccessor Context;
    24. #endregion
    25. #region 属性
    26. /// <summary>保存于Session的凭证</summary>
    27. public String SessionKey { get; set; } = "Admin";
    28. #endregion
    29. ///// <summary>当前管理提供者</summary>
    30. //public new static IManageProvider Provider => ObjectContainer.Current.ResolveInstance<IManageProvider>();
    31. #region IManageProvider 接口
    32. /// <summary>获取当前用户</summary>
    33. /// <param name="context"></param>
    34. /// <returns></returns>
    35. public override IManageUser GetCurrent(IServiceProvider context = null)
    36. {
    37. var ctx = (context?.GetService<IHttpContextAccessor>() ?? Context)
    38. ?.HttpContext;
    39. if (ctx == null) return null;
    40. try
    41. {
    42. if (ctx.Items["CurrentUser"] is IManageUser user) return user;
    43. var session = ctx.Items["Session"] as IDictionary<String, Object>;
    44. user = session?[SessionKey] as IManageUser;
    45. ctx.Items["CurrentUser"] = user;
    46. return user;
    47. }
    48. catch (InvalidOperationException ex)
    49. {
    50. // 这里捕获一下,防止初始化应用中session还没初始化好报的异常
    51. // 这里有个问题就是这里的ctx会有两个不同的值
    52. XTrace.WriteException(ex);
    53. return null;
    54. }
    55. }
    56. /// <summary>设置当前用户</summary>
    57. /// <param name="user"></param>
    58. /// <param name="context"></param>
    59. public override void SetCurrent(IManageUser user, IServiceProvider context = null)
    60. {
    61. var ctx = (context?.GetService<IHttpContextAccessor>() ?? Context)
    62. ?.HttpContext;
    63. if (ctx == null) return;
    64. ctx.Items["CurrentUser"] = user;
    65. var session = ctx.Items["Session"] as IDictionary<String, Object>;
    66. if (session == null) return;
    67. var key = SessionKey;
    68. // 特殊处理注销
    69. if (user == null)
    70. {
    71. session.Remove(key);
    72. session.Remove("userId");
    73. }
    74. else
    75. {
    76. session[key] = user;
    77. session["userId"] = user.ID;
    78. }
    79. }
    80. /// <summary>登录</summary>
    81. /// <param name="name"></param>
    82. /// <param name="password"></param>
    83. /// <param name="remember">是否记住密码</param>
    84. /// <returns></returns>
    85. public override IManageUser Login(String name, String password, Boolean remember)
    86. {
    87. XCode.Membership.User user;
    88. try
    89. {
    90. // 用户登录,依次支持用户名、邮箱、手机、编码
    91. var account = name.Trim();
    92. user = XCode.Membership.User.FindByName(account);
    93. if (user == null && account.Contains("@")) user = XCode.Membership.User.FindByMail(account);
    94. if (user == null && account.ToLong() > 0) user = XCode.Membership.User.FindByMobile(account);
    95. if (user == null) user = XCode.Membership.User.FindByCode(account);
    96. if (user == null) throw new EntityException("帐号{0}不存在!", account);
    97. if (!user.Enable) throw new EntityException("账号{0}被禁用!", account);
    98. // 数据库为空密码,任何密码均可登录
    99. if (!user.Password.IsNullOrEmpty())
    100. {
    101. var ss = password.Split(':');
    102. if (ss.Length <= 1)
    103. {
    104. if (!password.MD5().EqualIgnoreCase(user.Password)) throw new EntityException("密码不正确!");
    105. }
    106. else
    107. {
    108. var salt = ss[1];
    109. var pass = (user.Password.ToLower() + salt).MD5();
    110. if (!ss[0].EqualIgnoreCase(pass)) throw new EntityException("密码不正确!");
    111. }
    112. }
    113. // 保存登录信息
    114. user.Logins++;
    115. user.LastLogin = DateTime.Now;
    116. user.LastLoginIP = UserHost;
    117. user.Update();
    118. XCode.Membership.User.WriteLog("登录", true, $"用户[{user}]使用[{name}]登录成功");
    119. }
    120. catch (Exception ex)
    121. {
    122. XCode.Membership.User.WriteLog("登录", false, name + "登录失败!" + ex.Message);
    123. throw;
    124. }
    125. Current = user;
    126. // 过期时间
    127. var set = Setting.Current;
    128. var expire = TimeSpan.FromMinutes(0);
    129. if (remember && user != null)
    130. {
    131. expire = TimeSpan.FromDays(365);
    132. }
    133. else
    134. {
    135. if (set.SessionTimeout > 0)
    136. expire = TimeSpan.FromSeconds(set.SessionTimeout);
    137. }
    138. // 保存Cookie
    139. var context = Context?.HttpContext;
    140. this.SaveCookie(user, expire, context);
    141. return user;
    142. }
    143. /// <summary>注销</summary>
    144. public override void Logout()
    145. {
    146. // 注销时销毁所有Session
    147. var context = Context?.HttpContext;
    148. var session = context.Items["Session"] as IDictionary<String, Object>;
    149. session?.Clear();
    150. // 销毁Cookie
    151. this.SaveCookie(null, TimeSpan.Zero, context);
    152. base.Logout();
    153. }
    154. #endregion
    155. }
    156. /// <summary>管理提供者助手</summary>
    157. public static class ManagerProviderHelper
    158. {
    159. /// <summary>设置当前用户</summary>
    160. /// <param name="provider">提供者</param>
    161. /// <param name="context">Http上下文,兼容NetCore</param>
    162. public static void SetPrincipal(this IManageProvider provider, IServiceProvider context = null)
    163. {
    164. var ctx = context.GetService<IHttpContextAccessor>()?.HttpContext;
    165. if (ctx == null) return;
    166. var user = provider.GetCurrent(context);
    167. if (user == null) return;
    168. if (user is not IIdentity id || ctx.User?.Identity == id) return;
    169. // 角色列表
    170. var roles = new List<String>();
    171. if (user is IUser user2) roles.AddRange(user2.Roles.Select(e => e + ""));
    172. var up = new GenericPrincipal(id, roles.ToArray());
    173. ctx.User = up;
    174. Thread.CurrentPrincipal = up;
    175. }
    176. /// <summary>尝试登录。如果Session未登录则借助Cookie</summary>
    177. /// <param name="provider">提供者</param>
    178. /// <param name="context">Http上下文,兼容NetCore</param>
    179. public static IManageUser TryLogin(this IManageProvider provider, HttpContext context)
    180. {
    181. var serviceProvider = context?.RequestServices;
    182. // 判断当前登录用户
    183. var user = provider.GetCurrent(serviceProvider);
    184. if (user == null)
    185. {
    186. // 尝试从Cookie登录
    187. user = provider.LoadCookie(true, context);
    188. if (user != null) provider.SetCurrent(user, serviceProvider);
    189. }
    190. // 设置前端当前用户
    191. if (user != null) provider.SetPrincipal(serviceProvider);
    192. return user;
    193. }
    194. /// <summary>生成令牌</summary>
    195. /// <returns></returns>
    196. public static JwtBuilder GetJwt()
    197. {
    198. var set = Setting.Current;
    199. // 生成令牌
    200. var ss = set.JwtSecret.Split(':');
    201. var jwt = new JwtBuilder
    202. {
    203. Algorithm = ss[0],
    204. Secret = ss[1],
    205. };
    206. return jwt;
    207. }
    208. #region Cookie
    209. /// <summary>从Cookie加载用户信息</summary>
    210. /// <param name="provider">提供者</param>
    211. /// <param name="autologin">是否自动登录</param>
    212. /// <param name="context">Http上下文,兼容NetCore</param>
    213. /// <returns></returns>
    214. public static IManageUser LoadCookie(this IManageProvider provider, Boolean autologin, HttpContext context)
    215. {
    216. var key = "token";
    217. var req = context?.Request;
    218. var token = req?.Cookies[key];
    219. // 尝试从url中获取token
    220. if (token.IsNullOrEmpty() || token.Split(".").Length != 3) token = req?.Query["token"];
    221. if (token.IsNullOrEmpty() || token.Split(".").Length != 3) token = req?.Query["jwtToken"];
    222. // 尝试从头部获取token
    223. if (token.IsNullOrEmpty() || token.Split(".").Length != 3) token = req?.Headers[HeaderNames.Authorization];
    224. if (token.IsNullOrEmpty() || token.Split(".").Length != 3) return null;
    225. token = token.Replace("Bearer ", "", StringComparison.OrdinalIgnoreCase);
    226. var jwt = GetJwt();
    227. if (!jwt.TryDecode(token, out var msg))
    228. {
    229. XTrace.WriteLine("令牌无效:{0}, token={1}", msg, token);
    230. return null;
    231. }
    232. var user = jwt.Subject;
    233. if (user.IsNullOrEmpty()) return null;
    234. // 判断有效期
    235. if (jwt.Expire < DateTime.Now)
    236. {
    237. XTrace.WriteLine("令牌过期:{0} {1}", jwt.Expire, token);
    238. return null;
    239. }
    240. var u = provider.FindByName(user);
    241. if (u == null || !u.Enable) return null;
    242. // 保存登录信息。如果是json请求,不用记录自动登录
    243. if (autologin && u is IAuthUser mu && !req.IsAjaxRequest())
    244. {
    245. mu.SaveLogin(null);
    246. LogProvider.Provider.WriteLog("用户", "自动登录", true, $"{user} Time={jwt.IssuedAt} Expire={jwt.Expire} Token={token}", u.ID, u + "", ip: context.GetUserHost());
    247. }
    248. return u;
    249. }
    250. /// <summary>保存用户信息到Cookie</summary>
    251. /// <param name="provider">提供者</param>
    252. /// <param name="user">用户</param>
    253. /// <param name="expire">过期时间</param>
    254. /// <param name="context">Http上下文,兼容NetCore</param>
    255. public static void SaveCookie(this IManageProvider provider, IManageUser user, TimeSpan expire, HttpContext context)
    256. {
    257. var res = context?.Response;
    258. if (res == null) return;
    259. var option = new CookieOptions();
    260. var token = "";
    261. if (user != null)
    262. {
    263. // 令牌有效期,默认2小时
    264. var exp = DateTime.Now.Add(expire.TotalSeconds > 0 ? expire : TimeSpan.FromHours(2));
    265. var jwt = GetJwt();
    266. jwt.Subject = user.Name;
    267. jwt.Expire = exp;
    268. token = jwt.Encode(null);
    269. if (expire.TotalSeconds > 0) option.Expires = DateTimeOffset.Now.Add(expire);
    270. }
    271. else
    272. {
    273. option.Expires = DateTimeOffset.MinValue;
    274. }
    275. res.Cookies.Append("token", token, option);
    276. context.Items["jwtToken"] = token;
    277. }
    278. #endregion
    279. /// <summary>
    280. /// 添加管理提供者
    281. /// </summary>
    282. /// <param name="service"></param>
    283. public static void AddManageProvider(this IServiceCollection service)
    284. {
    285. service.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    286. service.TryAddSingleton<IManageProvider, ManageProvider2>();
    287. }
    288. /// <summary>
    289. /// 使用管理提供者
    290. /// </summary>
    291. /// <param name="app"></param>
    292. public static void UseManagerProvider(this IApplicationBuilder app)
    293. {
    294. XTrace.WriteLine("初始化ManageProvider");
    295. ManageProvider.Provider = app.ApplicationServices.GetService<IManageProvider>();
    296. ManageProvider2.Context = app.ApplicationServices.GetService<IHttpContextAccessor>();
    297. // 初始化数据库
    298. _ = Role.Meta.Count;
    299. }
    300. }
    301. }