EF+SqlServer/Mysql(三)JWT+Authorize的使用

集成JWT权限认证

为什么要使用JWT,我们先来看看传统的身份校验方式
用户向服务器发送用户名和密码->服务器验证通过后,在当前session对话里保存用户登录信息,比如用户角色,用户名,登录时间等->服务器向用户返回一个session_id,写入用户的cookie->随后的用户每次发送请求都会将session_id传回服务器->服务器搜到session_id,找到前期保存的数据,由此得知用户的身份
这种模式的问题在于,扩展性不好,单机情况下没有问题,如果是服务器集群,要求session数据共享,每台服务器都能够读取session,如果session存储的节点挂了,那么整个服务都会瘫痪,体验相当不好,风险也很高,相比之下,JWT的实现方式是将用户信息存储在客户端,服务端不进行保存,每次请求都把令牌带上来校验用户的登录状态,这样服务器就变成了无状态的,服务器集群也很好扩展。

当用户需要访问带权限验证的API时,应该使用承载模式发送JWT,通常在Authorization标头,格式如下:
Authorization:Bearer

网站的功能简介
登录成功后将用户信息写入Claim对象生成token返回web端,写入cookie,当用户修改密码时,通过ajax调用Api接口在头部添加Authorization:Bearer 发送请求,authorize会验证token是否有效,如果失效则返回401,成功则进行密码修改的操作。

登录API

    /// 
    /// 登录
    /// 
    /// 
    /// 
    [HttpPost]
    [Route("Login")]
    public async Task Login(UserDto user)
    {
        var result = new ResultInclusion();
        if (string.IsNullOrEmpty(user.UserName))
        {
            result.Code = -1;
            result.Msg = "请输入账号";
            return new JsonResult(result);
        }
        if (string.IsNullOrEmpty(user.Password))
        {
            result.Code = -1;
            result.Msg = "请输入密码";
            return new JsonResult(result);
        }
        //dto.LoginIP = ip.ToString();
        ////获取客户端浏览器信息
        //dto.Explorer = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString();
        ////获取客户端浏览器信息
        //dto.ClientOS = HttpContextHelper.GetOsVersion(dto.Explorer);

        var usermodel = await _service.GetAsync(p=>p.UserName==user.UserName);
        if (usermodel != null)
        {
            if (usermodel.Password == GetMD5.Get_MD5(user.Password, "utf-8"))
            {
                var claims = new List()
                {
                    new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                    new Claim(JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddMinutes(30)).ToUnixTimeSeconds()}"),
                    new Claim(ClaimTypes.Name,user.UserName)
                };
                 
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"]));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var token = new JwtSecurityToken(
                    issuer:Configuration["Jwt:Domain"],
                    audience:Configuration["Jwt:Domain"],
                    claims:claims,
                    expires:DateTime.Now.AddMinutes(30),
                    signingCredentials:creds
                    );
              var accesstoken=  new JwtSecurityTokenHandler().WriteToken(token);
                result.Code = 0;
                result.Msg = "登录成功";
                result.Data = new
                {
                    username = user.UserName,
                    token =accesstoken
                };
                return new JsonResult(result);
            }
            else {
                result.Code = -1;
                result.Msg = "密码错误";
                return new JsonResult(result);
            }
        }
        else {
            result.Code = -1;
            result.Msg = "用户名不存在";
            return new JsonResult(result);
        }
    }

appsettings.json配置

  "Jwt": {
"SecretKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI2a2EJ7m872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI543nNDAPfnJsas96mSA7L/mD7RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB",
"Domain": "http://localhost:5001"

}

startup.cs ConfigureServices方法中添加JWT相关配置

            //添加权限认证+jwt认证
        services.AddAuthentication(b =>
        {
            b.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            b.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
            .AddJwtBearer(options => {
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateIssuer = true,//是否验证Issuer
                    ValidateAudience = true,//是否验证Audience
                    ValidateLifetime = true,//是否验证失效时间
                    ClockSkew = TimeSpan.FromMilliseconds(30),
                    ValidateIssuerSigningKey = true,//是否验证SecurityKey
                    ValidAudience = Configuration["Jwt:Domain"],//Audience
                    ValidIssuer = Configuration["Jwt:Domain"],//Issuer,这两项和前面签发jwt的设置一致
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"]))//拿到SecurityKey

                };
            });

Configure方法中添加权限过滤器

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseAuthentication();

        app.UseMvc();
    }

Web端:用户登录成功后返回的token写进cookie

          


                $.ajax({
                type: 'post',
                url: url + "/api/user/Login",
                data:JSON.stringify( { UserName: $("#username").val(), Password: $("#password").val() }),
                contentType:'application/json',
                success: function (data) {
                   //写入cookie
                    $.cookie("access_token", data.data.token, {
                        expires: new Date().getTime() + (30 * 60 * 1000)
                        , path: '/'
                    });
                   console.log(data);
                },
                error: function (xhr) {
                    $('button[type="submit"]').text('登录').removeAttr('disabled');
                },
                beforeSend: function (XHR) {
                    $('button[type="submit"]').text('登录中...').attr('disabled', 'disabled');
                },
                complete: function (XHR, TS) {
                    $('button[type="submit"]').text('登录').removeAttr('disabled');
                }
            });

修改密码

      


      $.ajax({
                type: 'post',
                url: url + "/api/user/UpdatePWD",
                data: JSON.stringify({ OldPassWord: $("#oldpassword").val(), Password: $("#newpassword").val() }),
                contentType: 'application/json',
                beforeSend: function (XHR) {
                    XHR.setRequestHeader("Authorization", "bearer " + $.cookie("access_token"));
                },
               success: function (data) {
                   console.log(data);
                },
                error: function (xhr) {
                    $('button[type="submit"]').text('确定').removeAttr('disabled');
                },
                complete: function (XHR, TS) {
                    $('button[type="submit"]').text('确定').removeAttr('disabled');
                }
            });

修改密码API

    /// 
    /// 修改密码
    /// 
    /// 
    /// 
    [HttpPost]
    [Route("UpdatePWD")]
    [Authorize]
    public async Task UpdatePWD(UserDto user) {
        var result = new ResultInclusion();
        //从claim中获取用户信息
        var schemeProvider = HttpContext.RequestServices.GetService(typeof(IAuthenticationSchemeProvider)) as IAuthenticationSchemeProvider;
        var defaultAuthenticate = await schemeProvider.GetDefaultAuthenticateSchemeAsync();
        if (defaultAuthenticate != null)
        {
            var JWTClaim = await HttpContext.AuthenticateAsync(defaultAuthenticate.Name);
            var ClaimUser = JWTClaim?.Principal;
            if (ClaimUser != null)
            {
                var userPassword = GetMD5.Get_MD5(user.OldPassWord, "utf-8");
                var userinfo = await _service.GetAsync(p => p.UserName == ClaimUser.Identity.Name);
                if (userinfo!=null&&userinfo.Password == userPassword)
                {
                    userinfo.Password = GetMD5.Get_MD5(user.Password, "utf-8");
                    result.Data = await _service.UpdateAsync(userinfo);
                    result.Code = 0;
                    result.Msg = "修改成功";
                }
                else
                {
                    result.Code = -1;
                    result.Msg = "旧密码不正确";
                }
            }
        }
        
        return new JsonResult(result);
    }

若未登录直接修改密码则返回401权限未通过:


image.png

你可能感兴趣的:(EF+SqlServer/Mysql(三)JWT+Authorize的使用)