本文主要是介绍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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!