.NET Core中使用Jwt实现登录身份认证

什么是JWT

  JWT(json web token)基于开放标准(RFC 7519),是一种无状态的分布式的身份验证方式,主要用于在网络应用环境间安全地传递声明。它是基于JSON的,所以它也像json一样可以在.Net、JAVA、JavaScript,、PHP等多种语言使用。

  为什么要使用JWT?

  传统的Web应用一般采用Cookies+Session来进行认证。但对于目前越来越多的App、小程序等应用来说,它们对应的服务端一般都是RestFul 类型的无状态的API,再采用这样的的认证方式就不是很方便了。而JWT这种无状态的分布式的身份验证方式恰好符合这样的需求。

JWT的组成:

  它是由三段“乱码”字符串通过两个“.”连接在一起组成。官网https://jwt.io/提供了它的验证方式

它的三个字符串分别对应了Header、Payload和Signature三部分

Header:
{
"alg": "HS256", 
"typ": "JWT"
}

  标识加密方式为HS256,Token类型为JWT, 这段JSON通过Base64Url编码形成上例的第一个字符串

Payload

  Payload是JWT用于信息存储部分,其中包含了许多种的声明(claims)。 可以自定义多个声明添加到Payload中,系统也提供了一些默认的类型

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

  这部分通过Base64Url编码生成第二个字符串。

Signature

  Signature是用于Token的验证。它的值类似这样的表达式:Signature = HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret),也就是说,它是通过将前两个字符串加密后生成的一个新字符串。

  所以只有拥有同样加密密钥的人,才能通过前两个字符串获得同样的字符串,通过这种方式保证了Token的真实性。

认证流程

(图片来源于网络)


  • 认证服务器:用于用户的登录验证和Token的发放。 -应用服务器:业务数据接口。被保护的API。 -客户端:一般为APP、小程序等。

.NET Core 实现jwt登录认证

  1. 安装Microsoft.AspNetCore.Authentication.JwtBearer包


  1. 增加用于验证的实体
public class TokenManagement
    {
        [JsonProperty("secret")]
        public string Secret { get; set; }
        [JsonProperty("issuer")]
        public string Issuer { get; set; }
        [JsonProperty("audience")]
        public string Audience { get; set; }
        [JsonProperty("accessExpiration")]
        public int AccessExpiration { get; set; }
        [JsonProperty("refreshExpiration")]
        public int RefreshExpiration { get; set; }
    }
  1. appsettings.json文件中增加jwt配置
"tokenManagement": {
    "secret": "*#@^%$!(zhike-secret-key)^~",
    "issuer": "zhike.business.Api",
    "audience": "zhike.business.Api",
    "accessExpiration": 30,
    "refreshExpiration": 60
  }
  1. StartUp类中注册Authentication
services.Configure<TokenManagement>(Configuration.GetSection("tokenManagement"));
var token = Configuration.GetSection("tokenManagement").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
    };
});
  1. 注册中间件
app.UseAuthentication();
  1. 新建BookController
[Route("api/[controller]")]
    [ApiController]
    public class BookController : Controller
    {
        // GET: api/<controller>
        [HttpGet]
        [AllowAnonymous]
        public IEnumerable<string> Get()
        {
            return new string[] { "ASP", "C#" };
        }

        // POST api/<controller>
        [HttpPost]
        [Authorize]
        public JsonResult Post()
        {
            return new JsonResult("Create  Book ...");
        }
    }
  1. 增加接口
 public interface IAuthenticateService:IService
    {
        bool IsAuthenticated(LoginRequestDTO request, out string token);
    }
  1. 实现接口
public class AuthenticateService : IAuthenticateService
    {
        private readonly TokenManagement _tokenManagement;

        public AuthenticateService(IOptions<TokenManagement> tokenManagement)
        {
            this._tokenManagement = tokenManagement.Value;
        }
        public bool IsAuthenticated(LoginRequestDTO request, out string token)
        {
            //TODO:验证账户密码逻辑 自行补全
            token = string.Empty;
            var claims = new[]
            {
            new Claim(ClaimTypes.Name,request.UserName),
            new Claim(ClaimTypes.Name,request.Password)
        };

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenManagement.Secret));
            SigningCredentials credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var jwtToken = new JwtSecurityToken(_tokenManagement.Issuer, _tokenManagement.Audience, claims, expires: DateTime.Now.AddMinutes(_tokenManagement.AccessExpiration), signingCredentials: credentials);
            token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
            return true;
        }
    }

9.在Controller中增加Login

[Produces("application/json")]
    [Route("v1/account")]
    public class AccountController : BaseApiController
    {
        private readonly IAuthenticateService _authService;

        public AccountController(
            IAuthenticateService authService)
        {
            _authService = authService;
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        [HttpPost("login")]
        [ProducesResponseType(typeof(ResponseInfo<LoginVM>), 200)]
        [AllowAnonymous]
        public async Task<IActionResult> Login([FromBody]LoginRequestDTO req)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest("Invalid Request");
            }
            string sToken;
            if (_authService.IsAuthenticated(req, out sToken))
            {
                return Ok(sToken);
            }
            return BadRequest("Invalid Request");

        }
    }

测试

通过swaggerpostman可以模拟登录

  启动jwt.demo.passport站点,在浏览器输入:http://localhost:5000/swagger/index.html



  入参:


{
  "userName": "jeffreyHu",
  "password": "123456"
}

  返回结果

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjpbImplZmZyZXlIdSIsIjEyMzQ1NiJdLCJleHAiOjE1OTE1MjU0MTYsImlzcyI6IkhTVHJhZGUuQXBwbGV0TWFuYWdlLkFwaSIsImF1ZCI6IkhTVHJhZGUuQXBwbGV0TWFuYWdlLkFwaSJ9.6r0zaoWCpLlwOU-zgXQJZ2fuFj9dqzi9wSZsAo986e0"

  此时,我们可以拷贝该字符串到jwt官网验证是否解码



  以上基本实现 .NET CORE登录生成jwt-token功能。

 

版权声明: 本文为智客工坊「楠木大叔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

results matching ""

    No results matching ""