net core SSO 单点登录和控制器中获取Token和UserId

2024-03-29 10:36

本文主要是介绍net core SSO 单点登录和控制器中获取Token和UserId,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

net core SSO 单点登录和控制器中获取Token和UserId

在写WebApi时常常是要获取登录用户的oken和UserId的,本文就这个需求来分享一下我在实际项目中的处理代码。

代码

控制器中注入

[ApiController]
//[Authorize]
[ServiceFilter(typeof(LDAPPLoginFilter))]
[Route("/file/api/[controller]/[action]")]
public class BaseController : ControllerBase
{public ITokenHelper _tokenHelper;public IHttpContextAccessor _httpContext;/// <summary>/// 当前用户ID/// </summary>public string CurrentUserId{get{var userId = "00185770cfb24ccca22e14f8b9111111";if (_httpContext != null && _tokenHelper != null){var tokenobj = _httpContext.HttpContext.Request.Headers["Authorization"].ToString();//读取配置文件中 的 秘钥var secretKey = ConfigurationManager.JwtTokenConfig["Secret"];string token = tokenobj.Split(" ")[1].ToString();//剔除Bearerstring mobile = "";//用户手机号//验证jwt,同时取出来jwt里边的用户IDTokenType tokenType = _tokenHelper.ValiTokenState(token, secretKey, a => a["iss"] == "test.cn" && a["aud"] == "test", action =>{userId = action["id"];mobile = action["phone_number"];});}return userId;}}}public FileServerController(ITokenHelper tokenHelper, IHttpContextAccessor httpContextAccessor)
{ _tokenHelper = tokenHelper;
_httpContext = httpContextAccessor;}

调用

登录过滤器 

/// <summary>/// 用户登录过滤器 /// 需要登录时 Check请求头中的token字段/// </summary>public class LDAPPLoginFilter : Attribute, IActionFilter{private readonly ITokenHelper _tokenHelper;/// <summary>/// 通过依赖注入得到数据访问层实例/// </summary>/// <param name="tokenHelper"></param>public LDAPPLoginFilter(ITokenHelper tokenHelper){_tokenHelper = tokenHelper;}public void OnActionExecuted(ActionExecutedContext context){}/// <summary>/// 操作过滤器/// </summary>/// <param name="context">请求上下文</param>/// <param name="next">下一个过滤器或者终结点本身</param>/// <returns></returns>//async public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)//{//    var descriptor = context.ActionDescriptor;//    // 当未标记为 AllowAnonymous 时再执行  //    if (!descriptor.EndpointMetadata.Any(p => p is IAllowAnonymous))//    {//        context.HttpContext.Request.Headers.TryGetValue("token", out var tokens);//        var token = tokens.FirstOrDefault();//        if (string.IsNullOrWhiteSpace(token))//        {//            //如果没有token 直接返回//            context.Result = new UnauthorizedResult();//直接返回401 统一请求返回的话,这里修改成统一需要登录的请求实体//        }//        else//        {//            var redisKey = $"_APP_Token_{token}";//            //存在token//            var userInfo = RedisClient.GetValue<CommonUserModel>(redisKey);//            if (userInfo == null)//            {//                context.Result = new UnauthorizedResult();//直接返回401 统一请求返回的话,这里修改成统一需要登录的请求实体//                return;//            }//            // RedisClient.SetValue(redisKey, userInfo, 180 * 24 * 60);//180天内登录一次就重新置成180天  暂时不启用,重复写入性能影响大,一个月写入一次。 写入缓存时需要设计 cachetime//            await next();//        }//    }//}/// <summary>/// 请求接口时进行拦截处理/// </summary>/// <param name="context"></param>public void OnActionExecuting(ActionExecutingContext context){//LawcaseEvidenceFilePreviewif (context.ActionDescriptor.EndpointMetadata.Any(it => it.GetType() == typeof(NoLDAPPLoginFilter))){return;}//Action 名称过滤if (context.ActionDescriptor.DisplayName.Contains("ActionName")){return;}//var ret = new Models.Commons.ResultInfoModel();var ret = new AjaxResult();try{//获取请求头中的Tokenvar tokenobj = context.HttpContext.Request.Headers["Authorization"].ToString();if (string.IsNullOrEmpty(tokenobj)){ret.state = (int)ResultCodeEnum.ApiUnauthorized;ret.message = "接口未授权";context.Result = new JsonResult(ret);return;}//读取配置文件中 的 秘钥var secretKey = ConfigurationManager.JwtTokenConfig["Secret"];string token = tokenobj.Split(" ")[1].ToString();//剔除Bearer string userId = string.Empty;string mobile = string.Empty;//用户手机号//var token = getToken(context);//验证jwt,同时取出来jwt里边的用户IDTokenType tokenType = _tokenHelper.ValiTokenState(token, secretKey, a => a["iss"] == "test.cn" && a["aud"] == "test", action =>{userId = action["id"];mobile = action["phone_number"];});if (tokenType == TokenType.FormError){ret.state = (int)ResultCodeEnum.ApiUnauthorized;ret.message = "登录失效,请重新登录!";//token非法context.Result = new JsonResult(ret);return;}if (tokenType == TokenType.Fail){ret.state = (int)ResultCodeEnum.ApiUnauthorized;ret.message = "用户信息验证失败!";//token验证失败context.Result = new JsonResult(ret);return;}if (tokenType == TokenType.Expired){ret.state = (int)ResultCodeEnum.ApiUnauthorized;ret.message = "登录失效,请重新登录!";context.Result = new JsonResult(ret);return;}if (string.IsNullOrEmpty(userId)){//获取用户编号失败时,阻止用户继续访问接口ret.state = (int)ResultCodeEnum.Error;ret.message = "用户信息丢失";context.Result = new JsonResult(ret);return;}//自定义代码逻辑,  取出token中的 用户编号 进行 用户合法性验证即可//。。。。。。。}catch (Exception ex){ret.state = (int)ResultCodeEnum.Error;ret.message = "请求来源非法" + ex.Message.ToString();context.Result = new JsonResult(ret);return;}}}
/// <summary>/// /// </summary>public interface ITokenHelper{/// <summary>/// Token验证/// </summary>/// <param name="encodeJwt">token</param>/// <param name="secretKey">secretKey</param>/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值</param>/// <returns></returns>bool ValiToken(string encodeJwt, string secretKey, Func<Dictionary<string, string>, bool> validatePayLoad = null);/// <summary>/// 带返回状态的Token验证/// </summary>/// <param name="encodeJwt">token</param>/// <param name="secretKey">secretKey</param>/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值</param>/// <param name="action"></param>/// <returns></returns>TokenType ValiTokenState(string encodeJwt, string secretKey, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action);}
/// <summary>/// /// </summary>public class TokenHelper : ITokenHelper{/// <summary>/// 验证身份 验证签名的有效性/// </summary>/// <param name="encodeJwt"></param>/// <param name="secretKey">配置文件中取出来的签名秘钥</param>/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param>public bool ValiToken(string encodeJwt, string secretKey, Func<Dictionary<string, string>, bool> validatePayLoad = null){var success = true;var jwtArr = encodeJwt.Split('.');if (jwtArr.Length < 3)//数据格式都不对直接passreturn false;var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));//配置文件中取出来的签名秘钥var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(secretKey));//验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));if (!success)return success;//签名不正确直接返回//其次验证是否在有效期内(也应该必须)var now = ToUnixEpochDate(DateTime.UtcNow);success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));//不需要自定义验证不传或者传递null即可if (validatePayLoad == null)return true;//再其次 进行自定义的验证success = success && validatePayLoad(payLoad);return success;}/// <summary>/// 时间转换/// </summary>/// <param name="date"></param>/// <returns></returns>private long ToUnixEpochDate(DateTime date){return (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);}/// <summary>/// /// </summary>/// <param name="encodeJwt"></param>/// <param name="secretKey"></param>/// <param name="validatePayLoad"></param>/// <param name="action"></param>/// <returns></returns>public TokenType ValiTokenState(string encodeJwt, string secretKey, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action){//iss: jwt签发者//sub: jwt所面向的用户//aud: 接收jwt的一方//exp: jwt的过期时间,这个过期时间必须要大于签发时间//nbf: 定义在什么时间之前,该jwt都是不可用的//iat: jwt的签发时间//jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击var jwtArr = encodeJwt.Split('.');if (jwtArr.Length < 3)//数据格式都不对直接passreturn TokenType.FormError;//var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(secretKey));//验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)if (!string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))))return TokenType.FormError;var now = ToUnixEpochDate(DateTime.UtcNow);var nbf = long.Parse(payLoad["nbf"].ToString());var exp = long.Parse(payLoad["exp"].ToString());if (!(now >= nbf && now < exp)){action(payLoad);return TokenType.Expired;}//不需要自定义验证不传或者传递null即可if (validatePayLoad == null){action(payLoad);return TokenType.Ok;}//再其次 进行自定义的验证if (!validatePayLoad(payLoad))return TokenType.Fail;//可能需要获取jwt摘要里边的数据,封装一下方便使用action(payLoad);return TokenType.Ok;}}
public class TokenManagement{public string Secret { get; set; }public string Issuer { get; set; }public string Audience { get; set; }public int AccessExpiration { get; set; }public int RefreshExpiration { get; set; }}
public class NoLDAPPLoginFilter : Attribute, IActionFilter{public void OnActionExecuted(ActionExecutedContext context){}public void OnActionExecuting(ActionExecutingContext context){//var ret = new Models.Commons.ResultInfoModel();//ret.Head.ErrorCode = 1000;//ret.Head.Msg = "成功!";//context.Result = new JsonResult(ret);}}
/// <summary>/// 设置该方法不会进行AES加密和解密操作,直接传入参数和响应结果 /// </summary>[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]public class NoAESMiddlewareAttribute : Attribute{}
public class Startup{public Startup(IConfiguration configuration){Configuration = configuration;LD.Code.ConfigurationManager.Configure(Configuration);//注册日志功能LD.Code.LogFactory.ResisterLogger();}public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){//services.AddSwaggerGen(options =>//{//    #region  文档格式化//    options.SwaggerDoc("v1", new OpenApiInfo//    {//        Version = "V1",//        Title = "ASP.NET CORE WepbApi 3.1",//        Description = "基于Asp.Net Core 3.1 实现文件上传下载",//        Contact = new OpenApiContact//        {//            Name = "律盾",//            Email = "lvduntech@lvdun.com"//        },//        License = new OpenApiLicense//        {//            Name = "许可证",//        }//    });//    options.DocumentFilter<HiddenApiFilter>();//    #endregion//});services.AddSwaggerGen(c =>{c.SwaggerDoc("v1", new OpenApiInfo { Title = "XXX服务接口", Version = "v1" });// 获取xml文件名var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";// 获取xml文件路径var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);// 添加控制器层注释,true表示显示控制器注释c.IncludeXmlComments(xmlPath, true);LD.Domain.xml//xmlFile = "LD.Domain.xml";//xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);//c.IncludeXmlComments(xmlPath, true);LD.Code.xml//xmlFile = "LD.Code.xml";//xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);//c.IncludeXmlComments(xmlPath, true);c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());c.DocumentFilter<HiddenApiFilter>();});services.Configure<TokenManagement>(Configuration.GetSection("JwtTokenConfig"));var token = Configuration.GetSection("JwtTokenConfig").Get<TokenManagement>();services.AddAuthentication(x =>{x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(x =>{x.RequireHttpsMetadata = false;x.SaveToken = true;x.TokenValidationParameters = new TokenValidationParameters{ValidateIssuerSigningKey = true,IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),ValidIssuer = token.Issuer,ValidAudience = token.Audience,ValidateIssuer = false,ValidateAudience = false};});LD.Services.RegisterIoc.Register(services);//                                          ActionExecutingContextservices.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();services.AddTransient<ITokenHelper, TokenHelper>();services.AddScoped<NoLDAPPLoginFilter>();services.AddScoped<LDAPPLoginFilter>();services.AddControllers();//跨域var corsstring = Configuration.GetSection("Cors").Value;string[] corsarray = corsstring.Split(',');services.AddCors(options => options.AddPolicy("CorsPolicy",builder =>{builder.AllowAnyMethod().AllowAnyHeader().WithOrigins(corsarray).AllowCredentials();}));}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IWebHostEnvironment env){//if (env.IsDevelopment())//{//    app.UseDeveloperExceptionPage();//}DocExpansion swaggeerDoc;//if (env.IsDevelopment())//{app.UseDeveloperExceptionPage();swaggeerDoc = DocExpansion.List;//添加Swagger有关中间件app.UseSwagger();app.UseSwaggerUI(c =>{c.SwaggerEndpoint("/swagger/v1/swagger.json", "FileServerAPI  v1");c.RoutePrefix = string.Empty;c.DocExpansion(DocExpansion.None);});//}//else//{//    swaggeerDoc = DocExpansion.None;//}app.UseStaticFiles();//app.UseStaticFiles(new StaticFileOptions//{                //设置不限制content-type//    ServeUnknownFileTypes = true//});app.UseHttpsRedirection();app.UseRouting();//1.路由app.UseCors("CorsPolicy");app.UseAuthentication();//2.认证app.UseAuthorization();//3.授权app.UseEndpoints(endpoints =>{endpoints.MapControllers();});//app.UseSwagger();//app.UseSwaggerUI(c =>//{//    c.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1");//});}}
public class Enum{/// <summary>/// 系统数据返回状态/// </summary>public enum ResultCodeEnum{/// <summary>/// 失败/// </summary>[Description("失败")]Error = 0,/// <summary>/// 成功/// </summary>[Description("成功")]Success = 1,/// <summary>/// 接口未授权/// </summary>[Description("接口未授权")]ApiUnauthorized = 401}/// <summary>/// /// </summary>public enum TokenType{/// <summary>/// 验证成功/// </summary>[Description("验证成功")]Ok,/// <summary>/// 验证失败/// </summary>[Description("验证失败")]Fail,/// <summary>/// Token失效/// </summary>[Description("Token失效")]Expired,/// <summary>/// Token非法/// </summary>[Description("Token非法")]FormError}}

END

这篇关于net core SSO 单点登录和控制器中获取Token和UserId的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/858368

相关文章

MySQL 获取字符串长度及注意事项

《MySQL获取字符串长度及注意事项》本文通过实例代码给大家介绍MySQL获取字符串长度及注意事项,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql 获取字符串长度详解 核心长度函数对比⚠️ 六大关键注意事项1. 字符编码决定字节长度2

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

python3如何找到字典的下标index、获取list中指定元素的位置索引

《python3如何找到字典的下标index、获取list中指定元素的位置索引》:本文主要介绍python3如何找到字典的下标index、获取list中指定元素的位置索引问题,具有很好的参考价值,... 目录enumerate()找到字典的下标 index获取list中指定元素的位置索引总结enumerat

CSS3打造的现代交互式登录界面详细实现过程

《CSS3打造的现代交互式登录界面详细实现过程》本文介绍CSS3和jQuery在登录界面设计中的应用,涵盖动画、选择器、自定义字体及盒模型技术,提升界面美观与交互性,同时优化性能和可访问性,感兴趣的朋... 目录1. css3用户登录界面设计概述1.1 用户界面设计的重要性1.2 CSS3的新特性与优势1.

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过

使用Python实现获取屏幕像素颜色值

《使用Python实现获取屏幕像素颜色值》这篇文章主要为大家详细介绍了如何使用Python实现获取屏幕像素颜色值,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、一个小工具,按住F10键,颜色值会跟着显示。完整代码import tkinter as tkimport pyau

Java中的登录技术保姆级详细教程

《Java中的登录技术保姆级详细教程》:本文主要介绍Java中登录技术保姆级详细教程的相关资料,在Java中我们可以使用各种技术和框架来实现这些功能,文中通过代码介绍的非常详细,需要的朋友可以参考... 目录1.登录思路2.登录标记1.会话技术2.会话跟踪1.Cookie技术2.Session技术3.令牌技