• Core + Vue 后台管理基础框架2——认证


    1、前言

      这块儿当时在IdentityServer4和JWT之间犹豫了一下,后来考虑到现状,出于3个原因,暂时放弃了IdentityServer4选择了JWT:

    (1)目前这个前端框架更适配JWT;

    (2)前后端分离的项目,如果上IdentityServer4,还要折腾点儿工作,比如前端配置、多余的回调等;

    (3)跨度太大,团队、系统、历史数据接入都是问题,解决是可以解决,但时间有限,留待后续吧;

      当然,只是暂时放弃,理想中的最佳实践还是IdentityServer4做统一鉴权的。

    2、JWT认证实现

    (1)Common项目下定义JWTConfig配置对象

     (2)系统配置文件中增加JWT参数配置

     此处配置与(1)中的配置对象是对应的。

    (3)JWT处理程序及相关服务注册

     1 services.Configure<JWTConfig>(Configuration.GetSection("JWT"));
     2             var jwtConfig = Configuration.GetSection("JWT").Get<JWTConfig>();
     3             services.AddAuthentication(options =>
     4                 {
     5                     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
     6                     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
     7                 })
     8                 .AddJwtBearer(options =>
     9                 {
    10                     options.TokenValidationParameters = new TokenValidationParameters
    11                     {
    12                         ValidateIssuer = true,
    13                         ValidIssuer = jwtConfig.Issuer,
    14                         ValidateIssuerSigningKey = true,
    15                         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SymmetricSecurityKey)),
    16                         ValidateAudience = false,
    17                         ValidateLifetime = true,
    18                         ClockSkew = TimeSpan.FromMinutes(5)
    19                     };
    20                     options.Events = new JwtBearerEvents
    21                     {
    22                         OnTokenValidated = context =>
    23                         {
    24                             var userContext = context.HttpContext.RequestServices.GetService<UserContext>();
    25                             var claims = context.Principal.Claims;
    26                             userContext.ID = long.Parse(claims.First(x => x.Type == JwtRegisteredClaimNames.Sub).Value);
    27                             userContext.Account = claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value;
    28                             userContext.Name = claims.First(x => x.Type == ClaimTypes.Name).Value;
    29                             userContext.Email = claims.First(x => x.Type == JwtRegisteredClaimNames.Email).Value;
    30                             userContext.RoleId = claims.First(x => x.Type == ClaimTypes.Role).Value;
    31 
    32                             return Task.CompletedTask;
    33                         }
    34                     };
    35                 });
    36             JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

       上述代码中注意红色那部分Token验证成功的事件注册,其目的是认证成功之后,从JWT中取出必要信息构建当前用户上下文,这个上下文信息非常重要,但凡涉及到需要获取当前用户相关信息的部分,都要依赖它,后续文章中对应部分还会提及。

    (4)登录并写入Token

     1 /// <summary>
     2         /// 登录
     3         /// </summary>
     4         /// <param name="userDto"></param>
     5         /// <returns></returns>
     6         [AllowAnonymous]
     7         [HttpPost("login")]
     8         public async Task<IActionResult> Login([FromBody]SysUserDto userDto)
     9         {
    10             var validateResult = await _accountService.ValidateCredentials(userDto.Account, userDto.Password);
    11             if (!validateResult.Item1)
    12             {
    13                 return new NotFoundObjectResult("用户名或密码错误");
    14             }
    15 
    16             var user = validateResult.Item2;
    17             var claims = new Claim[]
    18             {
    19                 new Claim(ClaimTypes.NameIdentifier, user.Account),
    20                 new Claim(ClaimTypes.Name, user.Name),
    21                 new Claim(JwtRegisteredClaimNames.Email, user.Email),
    22                 new Claim(JwtRegisteredClaimNames.Sub, user.ID.ToString()),
    23                 new Claim(ClaimTypes.Role, user.RoleId)
    24             };
    25 
    26             var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.SymmetricSecurityKey));
    27 
    28             var token = new JwtSecurityToken(
    29                 issuer: _jwtConfig.Issuer,
    30                 audience: null,
    31                 claims: claims,
    32                 notBefore: DateTime.Now,
    33                 expires: DateTime.Now.AddHours(2),
    34                 signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
    35             );
    36 
    37             var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
    38 
    39             return new JsonResult(new { token = jwtToken });
    40         }

     (5)前端Token状态保存

      一般来讲,在后端登录成功返回前端之后,前端需要保存此token以保持状态,否则一刷新全完蛋,那我们来看看前端怎么实现。由于前端项目引入了Vuex来保持状态,那api调用、状态操作自然就放在store中,我们来看看登录对应的store。打开前端源码,找到user这个store:

       我们看到,登录完毕,调用SET_TOKEN这个mutation提交token状态变更保存Token,这个mutation及其背后的state如下如下:

     同时,在登录action中,登录成功之后,我们还发现了一行代码:

       此setToken引自前端工具类,auth.js,代码如下:

     1 import Cookies from 'js-cookie'
     2 
     3 const TokenKey = 'ngcc_mis_token'
     4 
     5 export function getToken() {
     6   return Cookies.get(TokenKey)
     7 }
     8 
     9 export function setToken(token) {
    10   return Cookies.set(TokenKey, token)
    11 }
    12 
    13 export function removeToken() {
    14   return Cookies.remove(TokenKey)
    15 }

      至此我们明白了前端的机制,把token写入Vuex状态的同时,再写入cookie。那这里问一句,只写入Vuex状态,行不行呢?不行,因为浏览器一刷新,所有前端对象就会销毁,包括Vuex对象,这样会导致前端丢失会话。

    3、总结

      以上就是系统认证的实现,大家摸清楚各种认证方案、优缺点、特点,多深入源码、机制,遇到问题自然会手到擒来。

    SET_TOKEN
  • 相关阅读:
    java stackoverflowerror与outofmemoryerror区别
    JVM参数笔记
    记录一次JVM配置优化的案例
    JVM相关内容简介(转)
    Spring boot中的定时任务(计划任务)
    netty学习:UDP服务器与Spring整合(2)
    netty学习:UDP服务器与Spring整合
    maven 纯注解一步一步搭建Spring Mvc项目(入门)
    将class类对象转化成json的数据格式
    Spring五个事务隔离级别和七个事务传播行为
  • 原文地址:https://www.cnblogs.com/guokun/p/12483215.html
Copyright © 2020-2023  润新知