IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    How to Implement JWT in Core API and Angular – Part 1

    Winson发表于 2023-08-21 08:21:03
    love 0

    This is post 1 of 1 in the series “How to Implement JWT”

    In this series, I will show you how to implement JWT in .Net Core API and communicate with Angular.

    1. Introduction

    JWT means JSON Web Tokens, it usually uses for API authorization with the client. JWT is an open standard that defines a way to transmit information between two parties securely as a JSON object in a compact and verifiable way. It is great for securing REST APIs and authorization. There are some common use cases of JWT include:

    1) Authorization – Once the user is logged in, each subsequent request will include the JWT to allow access to routes, services, and resources.

    2) Information Exchange – JWT is a good way of securely transmitting information between parties.

    3) User information storage – JWT can store user details, preferences, etc in a web/mobile application.

    The benefits of using JWT include compact size, url-safe, ability to be encrypted, decentralized, suitable for mobile apps, and more.

    And in this post, I will show you how to use JWT for .Net Core API and Angular.

    2. The flow for using JWT

    Let’s take a look at the flow for how’s the JWT working:

    1) Client calls the API for a login request
    2) Server(API) side generates JWT after login success
    3) Pass the JWT to client-side
    4) Client-side needs to save the JWT
    5) Client passes the JWT to the API for each request
    6) Server-side needs to verify the JWT from the client’s request, if successful then return the data otherwise return auth failed message.

    3. Generate the JWT

    We can create a JWT service to help to do that! We will use JwtSecurityToken to generate the JWT, this method under System.IdentityModel.Tokens.Jwt namespace, let’s take a look at the signature for this method. There are 4 overwrite methods for this, but we will use the below

    public JwtSecurityToken(string issuer = null, string audience = null, 
                    IEnumerable<Claim> claims = null, DateTime? notBefore = null, DateTime? 
                    expires = null, SigningCredentials signingCredentials = null);
    

    suppose we can let the parameters be null, but for safer, we will use all of them. We can put these parameters into appsettings.json so that can be control

    "JwtSettings": {
        "SecurityKey": "MyVeryOwnSecurityKey",
        "Issuer": "MyVeryOwnIssuer",
        "Audience": "https://localhost:4810",
        "ExpirationTimeInMinutes": 1440
      },
    

    and use them to create the token

    var jwtOptions = new JwtSecurityToken(
                 issuer: _configuration["JwtSettings:Issuer"],
                 audience: _configuration["JwtSettings:Audience"],
                 claims: GetClaims(user),
                 expires: DateTime.Now.AddMinutes(Convert.ToDouble(
                         _configuration["JwtSettings:ExpirationTimeInMinutes"])),
                 signingCredentials: GetSigningCredentials());
    

    the complete codes for the JwtService as below

    using Microsoft.Extensions.Configuration;
    using Microsoft.IdentityModel.Tokens;
    using MyDemo.Core.Data.Entity;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;
    
    namespace MyDemo.Core.Services;
    
    public class JwtService
    {
        private readonly IConfiguration _configuration;
        public JwtService(IConfiguration configuration)
        {
            //inject the configuration for getting the setting values
            _configuration = configuration;
        }
        public JwtSecurityToken GetToken(User user)
        {
            //create the Jwt token from setting values
            var jwtOptions = new JwtSecurityToken(
                            issuer: _configuration["JwtSettings:Issuer"],
                            audience: _configuration["JwtSettings:Audience"],
                            claims: GetClaims(user),
                            expires: DateTime.Now.AddMinutes(Convert.ToDouble(
                                    _configuration["JwtSettings:ExpirationTimeInMinutes"])),
                            signingCredentials: GetSigningCredentials());
            return jwtOptions;
        }
        //generate signing credentials
        private SigningCredentials GetSigningCredentials()
        {
            var key = Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecurityKey"]);
            var secret = new SymmetricSecurityKey(key);
            return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
        }
        //create the claim base current user name and email
        private List<Claim> GetClaims(User user)
        {
            var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Email) };
            return claims;
        }
    }
    

    4. Create the Login function

    We need to create an API login function to return the JWT to the client.

    4.1 Create the DTO

    Before we start, we need to receive the login request object and a result object for return to the client, so we need to create two DTOs for the login function. In this way, I will simply introduce what’s DTO.

    DTO stands for Data Transfer Object. It is a design pattern used to transfer data between different layers of an application, such as between the UI layer and the business logic layer. The main purpose of a DTO is to transfer data, so they don’t contain any behavior except for storage, retrieval, serialization, and deserialization of data.

    We can create a DTO folder under the Data folder and create the LoginRequest.cs in this folder

    //MyDemo.Core/Data/DTO/LoginRequest.cs
    
    namespace MyDemo.Core.Data.DTO;
    
    public class LoginRequest
    {
        public string Username { get; set; }
        public string Password { get; set; } 
    }
    

    and the result object for returning the token

    //MyDemo.Core/Data/DTO/LoginToken.cs
    
    namespace MyDemo.Core.Data.DTO;
    
    public class LoginToken
    {
        /// <summary>
        /// The JWT token if the login attempt is successful, or NULL if not
        /// </summary>
        public string? access_token { get; set; }
        public string? user_id { get; set; }
        public string? token_type { get; set; }
        /// <summary>
        /// expires time in seconds
        /// </summary>
        /// <value></value>
        public int? expires_in { get; set; }
    }
    

    4.2. Create the Auth API

    Create the AuthController to handle the login function, and we need to inject the below instance first

    [ApiController]
    [Route("api/[controller]")]
    public class AuthController : ControllerBase
    {
        private readonly IUserRepository _userRepository;
        private readonly JwtService _jwtService;
        private readonly IConfiguration _configuration;
        private readonly ILogger<AuthController> _logger;
    
        public AuthController(IUserRepository userRepository,
                            JwtService jwtService,
                            IConfiguration configuration,
                            ILogger<AuthController> logger)
        {
            _userRepository = userRepository;
            _jwtService = jwtService;
            _configuration = configuration;
            _logger = logger;
        }
    }
    

    create the login API to receive a LoginRequest

    [HttpPost("Login")]
    public async Task<IActionResult> Login(LoginRequest loginRequest)
    {
        //todo...
    }
    

    check the user name and password for login, if successful then generate the token, otherwise, return the null data.

    var user = await _userRepository.GetItemWithConditionAsync(
        u => u.Name == loginRequest.Username && u.Password == loginRequest.Password);
    if (user == null)
    {
        return Unauthorized(new LoginToken()
        {
            access_token = null,
            user_id = null,
            expires_in = 0,
            token_type = "Bearer"
        });
    }
    //generate a token by user information and appsettings values
    var secToken = _jwtService.GetToken(user);
    //serializes the token into a JWT format
    var jwt = new JwtSecurityTokenHandler().WriteToken(secToken);
    //get the expiration times and pass them to the client
    var expires_sec = Convert.ToInt32(_configuration["JwtSettings:ExpirationTimeInMinutes"]) * 60;
    return Ok(new LoginToken()
    {
        access_token = jwt,
        user_id = user.Id.ToString(),
        expires_in = expires_sec,
        token_type = "Bearer"
    });
    

    by the way, this is only for a demo, so I didn’t encrypt the password, you should encrypt it in a real project.

    and we should use try...catch to handle exceptions in a complete function

    [HttpPost("Login")]
    public async Task<IActionResult> Login(LoginRequest loginRequest)
    {
        try
        {
            var user = await _userRepository.GetItemWithConditionAsync(
                u => u.Name == loginRequest.Username && u.Password == loginRequest.Password);
            if (user == null)
            {
                return Unauthorized(new LoginToken()
                {
                    access_token = null,
                    user_id = null,
                    expires_in = 0,
                    token_type = "Bearer"
                });
            }
            //generate a token by user information and appsettings values
            var secToken = _jwtService.GetToken(user);
            //serializes the token into a JWT format
            var jwt = new JwtSecurityTokenHandler().WriteToken(secToken);
            //get the expiration times and pass them to the client
            var expires_sec = Convert.ToInt32(_configuration["JwtSettings:ExpirationTimeInMinutes"]) * 60;
            return Ok(new LoginToken()
            {
                access_token = jwt,
                user_id = user.Id.ToString(),
                expires_in = expires_sec,
                token_type = "Bearer"
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Login Error");
            return StatusCode(500);
        }
    }
    

    5. Setup Program.cs

    In the end, we also need to add the service to program.cs file. Before that, we need to install the below package to API project from Nuget first

    Microsoft.AspNetCore.Authentication.JwtBearer
    

    and add the below codes

    // Add Authentication services & middlewares
    builder.Services.AddAuthentication(opt =>
    {
        opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            RequireExpirationTime = true,
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["JwtSettings:Issuer"],
            ValidAudience = builder.Configuration["JwtSettings:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.
                    GetBytes(builder.Configuration["JwtSettings:SecurityKey"]))
        };
    });
    
    builder.Services.AddScoped<JwtService>();
    

    Ok, let’s take a look at the result in the swagger UI

    Great! We got the token after login successfully!

    6. Conclusion

    We have learned how to generate and handle the Jwt on the server side (API), the main port that uses the JwtSecurityToken to generate a token and serialize it into a JWT format and return it to the client. And we will discuss how to handle the JWT on the client side in the next article! 🙂

    Loading

    The post How to Implement JWT in Core API and Angular – Part 1 first appeared on Coder Blog.



沪ICP备19023445号-2号
友情链接