Skip to content

Commit

Permalink
[feat]在Cookie中存储令牌。读取令牌时,如果请求头没有携带令牌,则从Cookie读取。该设置在MVC中默认true,在API中默…
Browse files Browse the repository at this point in the history
…认false。close: #91
  • Loading branch information
nnhy committed Aug 1, 2024
1 parent f284f28 commit a9765c8
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 71 deletions.
26 changes: 13 additions & 13 deletions NewLife.Cube/Common/ApiFilterAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ namespace NewLife.Cube;
/// </remarks>
public sealed class ApiFilterAttribute : ActionFilterAttribute
{
/// <summary>从请求头中获取令牌</summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public static String GetToken(HttpContext httpContext)
{
var request = httpContext.Request;
var token = request.Query["Token"] + "";
if (token.IsNullOrEmpty()) token = (request.Headers["Authorization"] + "").TrimStart("Bearer ");
if (token.IsNullOrEmpty()) token = request.Headers["X-Token"] + "";
if (token.IsNullOrEmpty()) token = request.Cookies["Token"] + "";
///// <summary>从请求头中获取令牌</summary>
///// <param name="httpContext"></param>
///// <returns></returns>
//public static String GetToken(HttpContext httpContext)
//{
// var request = httpContext.Request;
// var token = request.Query["Token"] + "";
// if (token.IsNullOrEmpty()) token = (request.Headers["Authorization"] + "").TrimStart("Bearer ");
// if (token.IsNullOrEmpty()) token = request.Headers["X-Token"] + "";
// if (token.IsNullOrEmpty()) token = request.Cookies["Token"] + "";

return token;
}
// return token;
//}

/// <summary>执行前,验证模型</summary>
/// <param name="context"></param>
Expand All @@ -35,7 +35,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
// throw new ApplicationException(context.ModelState.Values.First(p => p.Errors.Count > 0).Errors[0].ErrorMessage);

// 访问令牌
var token = GetToken(context.HttpContext);
var token = context.HttpContext.LoadToken();
context.HttpContext.Items["Token"] = token;
if (!context.ActionArguments.ContainsKey("token")) context.ActionArguments.Add("token", token);

Expand Down
2 changes: 1 addition & 1 deletion NewLife.Cube/Common/BaseController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ void IActionFilter.OnActionExecuting(ActionExecutingContext context)
{
_args = context.ActionArguments;

var token = Token = ApiFilterAttribute.GetToken(context.HttpContext);
var token = Token = context.HttpContext.LoadToken();

try
{
Expand Down
9 changes: 2 additions & 7 deletions NewLife.Cube/Common/ControllerBaseX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,11 @@ public virtual void OnActionExecuting(ActionExecutingContext context)
}

// 访问令牌
var request = context.HttpContext.Request;
var token = request.Query["Token"] + "";
if (token.IsNullOrEmpty()) token = (request.Headers["Authorization"] + "").TrimStart("Bearer ");
if (token.IsNullOrEmpty()) token = request.Headers["X-Token"] + "";
if (token.IsNullOrEmpty()) token = request.Cookies["Token"] + "";
Token = token;
Token = context.HttpContext.LoadToken();

try
{
if (!token.IsNullOrEmpty())
if (!Token.IsNullOrEmpty())
{
}

Expand Down
5 changes: 5 additions & 0 deletions NewLife.Cube/Setting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ public class CubeSetting : Config<CubeSetting>
[Description("令牌有效期。访问令牌AccessToken的有效期,默认7200秒")]
[Category("用户登录")]
public Int32 TokenExpire { get; set; } = 7200;

/// <summary>在Cookie中存储令牌。读取令牌时,如果请求头没有携带令牌,则从Cookie读取,默认false</summary>
[Description("在Cookie中存储令牌。读取令牌时,如果请求头没有携带令牌,则从Cookie读取,默认false")]
[Category("用户登录")]
public Boolean TokenCookie { get; set; } = false;
#endregion

#region 界面配置
Expand Down
129 changes: 79 additions & 50 deletions NewLife.CubeNC/Membership/ManagerProviderHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Security.Principal;
using System;
using System.Security.Principal;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Net.Http.Headers;
using NewLife.Common;
Expand Down Expand Up @@ -56,8 +57,23 @@ public static IManageUser TryLogin(this IManageProvider provider, HttpContext co
span = DefaultTracer.Instance?.NewSpan(nameof(TryLogin));

// 尝试从Cookie登录
user = provider.LoadCookie(true, context);
if (user != null) provider.SetCurrent(user, serviceProvider);
var token = context.LoadToken();
var (u, jwt) = provider.LoadUser(token);
if ((user = u) != null)
{
provider.SetCurrent(user, serviceProvider);

#if MVC
// 保存登录信息。如果是json请求,不用记录自动登录
var req = context?.Request;
if (user is IAuthUser mu && !req.IsAjaxRequest())
{
mu.SaveLogin(null);

LogProvider.Provider.WriteLog("用户", "自动登录", true, $"{user} IssuedAt={jwt.IssuedAt.ToFullString()} Expire={jwt.Expire.ToFullString()}", user.ID, user + "", ip: context.GetUserHost());
}
#endif
}
}

// 如果Null直接返回
Expand All @@ -77,6 +93,47 @@ public static IManageUser TryLogin(this IManageProvider provider, HttpContext co
}
}

/// <summary>从令牌加载用户</summary>
/// <param name="provider"></param>
/// <param name="token"></param>
/// <returns></returns>
public static (IManageUser, JwtBuilder) LoadUser(this IManageProvider provider, String token)
{
if (token.IsNullOrEmpty()) return (null, null);

using var span = DefaultTracer.Instance?.NewSpan(nameof(LoadUser), token);

//token = token.Replace("Bearer ", "", StringComparison.OrdinalIgnoreCase);
span?.AppendTag(token);

var jwt = GetJwt();
if (!jwt.TryDecode(token, out var msg))
{
span?.AppendTag($"令牌无效:{msg}");
XTrace.WriteLine("令牌无效:{0}, token={1}", msg, token);

return (null, jwt);
}

var user = jwt.Subject;
if (user.IsNullOrEmpty()) return (null, jwt);
span?.AppendTag($"用户:{user}");

// 判断有效期
if (jwt.Expire < DateTime.Now)
{
span?.AppendTag($"令牌过期:{jwt.Expire.ToFullString()}");
XTrace.WriteLine("令牌过期:{0} {1}", jwt.Expire, token);

return (null, jwt);
}

var u = provider.FindByName(user);
if (u == null || !u.Enable) return (null, jwt);

return (u, jwt);
}

/// <summary>设置租户</summary>
/// <param name="context"></param>
/// <param name="userId"></param>
Expand Down Expand Up @@ -167,67 +224,36 @@ public static JwtBuilder GetJwt()
}

#region 用户Cookie
/// <summary>从Cookie加载用户信息</summary>
/// <param name="provider">提供者</param>
/// <param name="autologin">是否自动登录</param>
/// <summary>从上下文加载令牌</summary>
/// <param name="context">Http上下文,兼容NetCore</param>
/// <returns></returns>
public static IManageUser LoadCookie(this IManageProvider provider, Boolean autologin, HttpContext context)
public static String LoadToken(this HttpContext context)
{
using var span = DefaultTracer.Instance?.NewSpan(nameof(LoadCookie));
//using var span = DefaultTracer.Instance?.NewSpan(nameof(LoadToken));

var key = $"token-{SysConfig.Current.Name}";
var req = context?.Request;
var token = req?.Cookies[key];
var token = "";

// 尝试从头部获取token
if (token.IsNullOrEmpty() || token.Split(".").Length != 3)
token = req?.Headers[HeaderNames.Authorization].ToString().TrimStart("Bearer ");
if (token.IsNullOrEmpty() || token.Split(".").Length != 3)
token = req?.Headers["X-Token"].ToString().TrimStart("Bearer ");

// 尝试从url中获取token
if (token.IsNullOrEmpty() || token.Split(".").Length != 3) token = req?.Query["token"];
if (token.IsNullOrEmpty() || token.Split(".").Length != 3) token = req?.Query["jwtToken"];

// 尝试从头部获取token
if (token.IsNullOrEmpty() || token.Split(".").Length != 3) token = req?.Headers[HeaderNames.Authorization];

if (token.IsNullOrEmpty() || token.Split(".").Length != 3) return null;

token = token.Replace("Bearer ", "", StringComparison.OrdinalIgnoreCase);
span?.AppendTag(token);

var jwt = GetJwt();
if (!jwt.TryDecode(token, out var msg))
{
span?.AppendTag($"令牌无效:{msg}");
XTrace.WriteLine("令牌无效:{0}, token={1}", msg, token);

return null;
}

var user = jwt.Subject;
if (user.IsNullOrEmpty()) return null;
span?.AppendTag($"用户:{user}");

// 判断有效期
if (jwt.Expire < DateTime.Now)
// 尝试从Cookie获取token
if (CubeSetting.Current.TokenCookie)
{
span?.AppendTag($"令牌过期:{jwt.Expire.ToFullString()}");
XTrace.WriteLine("令牌过期:{0} {1}", jwt.Expire, token);

return null;
var key = $"token-{SysConfig.Current.Name}";
if (token.IsNullOrEmpty() || token.Split(".").Length != 3) token = req?.Cookies[key];
}

var u = provider.FindByName(user);
if (u == null || !u.Enable) return null;

#if MVC
// 保存登录信息。如果是json请求,不用记录自动登录
if (autologin && u is IAuthUser mu && !req.IsAjaxRequest())
{
mu.SaveLogin(null);

LogProvider.Provider.WriteLog("用户", "自动登录", true, $"{user} IssuedAt={jwt.IssuedAt.ToFullString()} Expire={jwt.Expire.ToFullString()}", u.ID, u + "", ip: context.GetUserHost());
}
#endif
if (token.IsNullOrEmpty() || token.Split(".").Length != 3) return null;

return u;
return token;
}

/// <summary>保存用户信息到Cookie</summary>
Expand All @@ -237,6 +263,9 @@ public static IManageUser LoadCookie(this IManageProvider provider, Boolean auto
/// <param name="context">Http上下文,兼容NetCore</param>
public static void SaveCookie(this IManageProvider provider, IManageUser user, TimeSpan expire, HttpContext context)
{
var set = CubeSetting.Current;
if (!set.TokenCookie) return;

using var span = DefaultTracer.Instance?.NewSpan(nameof(SaveCookie), new { user?.Name, expire });

var res = context?.Response;
Expand Down
5 changes: 5 additions & 0 deletions NewLife.CubeNC/Setting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ public class CubeSetting : Config<CubeSetting>
[Description("令牌有效期。访问令牌AccessToken的有效期,默认7200秒")]
[Category("用户登录")]
public Int32 TokenExpire { get; set; } = 7200;

/// <summary>在Cookie中存储令牌。读取令牌时,如果请求头没有携带令牌,则从Cookie读取,默认true</summary>
[Description("在Cookie中存储令牌。读取令牌时,如果请求头没有携带令牌,则从Cookie读取,默认true")]
[Category("用户登录")]
public Boolean TokenCookie { get; set; } = true;
#endregion

#region 界面配置
Expand Down

0 comments on commit a9765c8

Please sign in to comment.