refresh token axios

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;

namespace JsonWebTokenTesting
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }

    public class Startup
    {
        public Startup()
        {
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        }

        public void ConfigureServices(IServiceCollection services)
        {

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(options =>
                    {
                        options.TokenValidationParameters = new TokenValidationParameters
                        {
                            ValidateIssuerSigningKey = true,
                            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("O73xQtVrtLnBIEA+Oaq79x1d2BGOFbmHroVuE4ogrOk=")),
                            ValidateIssuer = true,
                            ValidIssuer = "http://localhost:5000",
                            ValidateAudience = true,
                            ValidAudience = "http://localhost:5000",
                            ValidateLifetime = true,
                            ClockSkew = TimeSpan.FromMinutes(0)
                        };

                        options.Events = new JwtBearerEvents
                        {
                            OnAuthenticationFailed = context =>
                            {
                                if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                                {
                                    context.HttpContext.Response.Headers.Remove("Token-Expired");
                                    context.HttpContext.Response.Headers.Add("Token-Expired", "Relative");
                                }
                                return Task.CompletedTask;
                            }
                        };
                    });

            services.AddCors(options =>
                    {
                        options.AddPolicy("default", builder =>
                        {
                            builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials().WithExposedHeaders("Token-Expired"); 
                        });
                    });

            services.AddMvc(options =>
                    {
                        options.Filters.Add(new ExceptionFilter());
                    });
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseDeveloperExceptionPage();
            app.UseAuthentication();
            app.UseCors("default");
            app.UseMvc();
        }
    }

    public class ExceptionFilter : IExceptionFilter
    {
        public void OnException(ExceptionContext context)
        {
            var exception = context.Exception;

            var statusCode = StatusCodes.Status500InternalServerError;

            if (exception is SecurityTokenException)
            {
                statusCode = StatusCodes.Status401Unauthorized;
                context.HttpContext.Response.Headers.Remove("Token-Expired");
                context.HttpContext.Response.Headers.Add("Token-Expired", "Absolute");
            }

            context.Result = new ObjectResult(exception.Message) { StatusCode = statusCode };
        }
    }

    public class TokenCreateInput
    {
        public string Account { get; set; }
        public string Password { get; set; }
    }

    public class TokenRefreshInput
    {
        public string AccessToken { get; set; }
        public string RefreshToken { get; set; }
    }

    [Route("token")]
    public class TokenController : ControllerBase
    {
        private static readonly Dictionary<string, dynamic> _dict = new Dictionary<string, dynamic>();

        [HttpPost("create")]
        public IActionResult Create([FromBody]TokenCreateInput input)
        {
            // todo: check the argument

            var userId = "10086"; // todo: validate the user

            var accessToken = CreateAccessToken(
                new Claim[]
                {
                    new Claim(JwtRegisteredClaimNames.Sub, userId)
                }
            );

            var refreshToken = CreateRefreshToken();

            _dict[userId] = new { RefreshToken = refreshToken, RefreshTokenExpires = DateTime.UtcNow.AddSeconds(60) };

            return Ok(new { accessToken, refreshToken, _dict });
        }

        [HttpPost("refresh")]
        public IActionResult Refresh([FromBody]TokenRefreshInput input)
        {
            // todo: check the argument

            var principal = ValidateAccessToken(input.AccessToken);

            var userId = principal.Claims.First(v => v.Type == JwtRegisteredClaimNames.Sub)?.Value;

            _dict.TryGetValue(userId, out var details);

            if (details.RefreshToken == input.RefreshToken && details.RefreshTokenExpires >= DateTime.UtcNow)
            {
                var accessToken = CreateAccessToken(
                    new Claim[]
                    {
                        new Claim(JwtRegisteredClaimNames.Sub, userId)
                    }
                );

                var refreshToken = CreateRefreshToken();

                _dict[userId] = new { RefreshToken = refreshToken, RefreshTokenExpires = DateTime.UtcNow.AddSeconds(60) };

                return Ok(new { accessToken, refreshToken, _dict });
            }
            else
            {
                throw new SecurityTokenException("Invalid RefreshToken");
            }
        }

        private string CreateAccessToken(Claim[] claims)
        {
            return new JwtSecurityTokenHandler().WriteToken(
                new JwtSecurityToken(
                    issuer: "http://localhost:5000",
                    audience: "http://localhost:5000",
                    claims: claims,
                    notBefore: DateTime.UtcNow,
                    expires: DateTime.UtcNow.AddSeconds(20),
                    signingCredentials: new SigningCredentials(
                        key: new SymmetricSecurityKey(Encoding.UTF8.GetBytes("O73xQtVrtLnBIEA+Oaq79x1d2BGOFbmHroVuE4ogrOk=")),
                        algorithm: SecurityAlgorithms.HmacSha256
                    )
                )
            );
        }

        private string CreateRefreshToken()
        {
            var random = new byte[32];
            using (var generator = RandomNumberGenerator.Create())
            {
                generator.GetBytes(random);
                return Convert.ToBase64String(random);
            }
        }

        private ClaimsPrincipal ValidateAccessToken(string accessToken)
        {
            try
            {
                return new JwtSecurityTokenHandler().ValidateToken(
                   accessToken,
                   new TokenValidationParameters
                   {
                       ValidateAudience = false,
                       ValidateIssuer = false,
                       ValidateIssuerSigningKey = true,
                       IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("O73xQtVrtLnBIEA+Oaq79x1d2BGOFbmHroVuE4ogrOk=")),
                       ValidateLifetime = false
                   },
                   out var validatedToken
                );
            }
            catch (Exception)
            {
                throw new SecurityTokenException("Invalid AccessToken");
            }
        }
    }

    [Authorize]
    [Route("users")]
    public class UserController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok(User.FindFirst(v => v.Type == JwtRegisteredClaimNames.Sub)?.Value);
        }
    }
}
import axios from ‘axios‘

axios.defaults.baseURL = ‘http://localhost:5000‘

const tokenRefreshAsync = () => {
    return new Promise((resolve, reject) => {
        axios.post(‘token/refresh‘, {
            AccessToken: localStorage.getItem(‘accessToken‘),
            RefreshToken: localStorage.getItem(‘refreshToken‘)
        }).then(response => {
            resolve(response.data)
        }).catch(error => {
            reject(error)
        })
    })
}

axios.interceptors.request.use(
    config => {
        const accessToken = localStorage.getItem(‘accessToken‘)
        if (accessToken) {
            config.headers = { ‘Authorization‘: ‘Bearer ‘ + accessToken }
        }
        return config
    },
    error => {
        return Promise.reject(error)
    }
)

axios.interceptors.response.use(
    response => {
        return response
    },
    async error => {
        if (error && error.response) {
            switch (error.response.status) {
                case 401:
                    var tokenExpired = error.response.headers[‘token-expired‘]
                    if (tokenExpired === ‘Relative‘) {
                        let { accessToken, refreshToken } = await tokenRefreshAsync()
                        localStorage.setItem(‘accessToken‘, accessToken)
                        localStorage.setItem(‘refreshToken‘, refreshToken)
                        return axios.request(error.config)
                    } else if (tokenExpired === ‘Absolute‘) {
                        localStorage.removeItem(‘accessToken‘)
                        localStorage.removeItem(‘refreshToken‘)
                        window.alert(‘需要重新登录‘)
                    } else {
                        localStorage.removeItem(‘accessToken‘)
                        localStorage.removeItem(‘refreshToken‘)
                        window.alert(‘需要重新登录‘)
                    }
                    break
            }
        }
        return Promise.reject(error)
    }
)

const http = {
    get: (url, params, config) => {
        return new Promise((resolve, reject) => {
            axios.get(url, { params: params }, config).then(response => {
                resolve(response.data)
            }).catch(error => {
                reject(error)
            })
        })
    },
    post: (url, params, config) => {
        return new Promise((resolve, reject) => {
            axios.post(url, params, config).then(response => {
                resolve(response.data)
            }).catch(error => {
                reject(error)
            })
        })
    }
}

export default http
<template>
  <div>
    <button @click="tokenCreate">token/create</button>
    <br />
    <button @click="users">users</button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        msg: Welcome to Your Vue.js App
      }
    },
    methods: {
      async tokenCreate() {
        let { accessToken, refreshToken } = await this.$http.post(token/create, {
          Account: Alice,
          Password: 12345
        })
        localStorage.setItem(accessToken, accessToken)
        localStorage.setItem(refreshToken, refreshToken)
      },
      async users() {
        console.info(await this.$http.get(users))
      }
    }
  }
</script>

 

refresh token axios

上一篇:JavaScript权威指南第02章 词法结构


下一篇:如何解决无法打开Microsoft App Store(0x80131500)问题?