简体   繁体   中英

How to intercept request Token Jwt in Dotnet Core to Send to other API?

I'm trying to do a filter in Dotnet Core to validate a token JWT in other Api of Login(Java) that i did. The basic is, thus that received the token, the filter gets the token Jwt and send it to validate in other Api. I'm trying to do but i don't findind how to do this. I did make in alternate method but i don't know how to do the correct.

Class SendEmailController.cs:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[controller]")]
public class SendEmailController: ControllerBase
{
    private readonly IEmailSender emailSender;

    public SendEmailController(IEmailSender emailSender){
        this.emailSender = emailSender;
    }

    [HttpPost]
    public async Task<ActionResult>  SendEmail(GetEmailDto emailDto) {
        string header = Request.Headers["Authorization"];
        string token = null;

        if(header != null && header.Contains("Bearer")){
            string [] aux = header.Split(" ");
            token = aux.Length > 1 ? aux[1].Trim() : token;
        }

        if(token != null && await LoginService.ValidateToken(token) != null){
          return  Ok(this.emailSender.SendEmailAsync(emailDto));
        } else {
          return BadRequest("Email not sended >> ");
        }
    }
}

LoginService.cs:

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
public class LoginService{
 public static  async Task<string> ValidateToken(string token)
{
    try{
    string tokenUrl = Environment.GetEnvironmentVariable("TOKEN_VALIDATE_URL");
    HttpClient client = new HttpClient();
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
    client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", token);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    return  await client.GetStringAsync(tokenUrl);
    }
    catch(Exception e)
    {
     Console.WriteLine("ERROR >> "+e.Message);
     return null;
    }   
    }
}

Startup.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace apiEmail
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<DataContext>();
            services.AddCors();
            services.AddControllers();
            services.AddAutoMapper(typeof(Startup));
            services.AddScoped<IEmailService, EmailService>();
            services.AddScoped<IAuthRepository, AuthRepository>();
            services.AddTransient<IEmailSender, AuthMessageSender>();
            services.AddMvc();
            var key = Encoding.ASCII.GetBytes(Environment.GetEnvironmentVariable("SECRET_KEY")+" DOTNET DA DEPRESSAO");
            services.AddAuthentication(x => {
               x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
               x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options => {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = false,
                     IssuerSigningKey = new SymmetricSecurityKey(key),
                     ValidateIssuer = false,
                     ValidateAudience = false
                };
            });
        }

         private static void UpdateDatabase(IApplicationBuilder app)
            {
                using (var serviceScope = app.ApplicationServices
                     .GetRequiredService<IServiceScopeFactory>()
                     .CreateScope())
            {
                using (var context = serviceScope.ServiceProvider.GetService<DataContext>())
              {
                context.Database.Migrate();
              }
        }
    }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext context)
        {
            UpdateDatabase(app);
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //app.UseHttpsRedirection();
            app.UseRouting();
            app.UseCors(
                options => options
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader()
            );

           // app.UseAuthentication();

           var options = new JwtBearerOptions
           {
                Audience = "...",
                Authority = "...",
                Events = new JwtBearerEvents
            {
                OnTokenValidated = context =>
                {
            // Add the access_token as a claim, as we may actually need it
            var accessToken = context.SecurityToken as JwtSecurityToken;
            if (accessToken != null)
            {
               Console.Write("Token >>", accessToken);
            }
            return Task.CompletedTask;
            }
        }
        };    
           app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
             context.Database.EnsureCreated();
             context.Database.Migrate();
        }
    }
}

How to do this validation without stay in the Controller, only in filter defined in startup.cs with filter like this class in Java?

Example:

@Slf4j
@Component
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class JwtRequestFilter extends OncePerRequestFilter {
    private JwtUtil jwtUtil;
    private LoginService login;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");
        String username = null;
        String jwt = null;
        Users usr = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            if (jwtUtil.isTokenExpired(jwt)) {
                throw new IOException("Token expirado ");
            }
            try {
                usr = login.verifyToken(jwt);
                Authentication auth = new UsernamePasswordAuthenticationToken(usr.getEmail(), usr.getSenha());
                Set authorities = new HashSet<>();
                authorities.add(new SimpleGrantedAuthority(usr.getTipo().toString()));
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
                        = new UsernamePasswordAuthenticationToken(
                        auth.getName(), auth.getCredentials(), authorities);
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            } catch (Exception e) {
                log.error("Error >> ", e);
                throw new IOException("Erro ao validar o Token");
            }

        }
        chain.doFilter(request, response);
    }
}

class LoginService.java:

public class LoginService{
public  Users  verifyToken(String token) {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer "+token);
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        return  restTemplate.exchange(Assets.API_TOKEN_DATA, HttpMethod.PUT, entity, Users.class).getBody(); 
    }
}

Thanks

@RafaelRfs I tried to post this as a comment but it is longer than the supported so I posted that as an answer.

Once you read that if you don't consider an answer please let me know and I will delete that.

Basically, you need a secret key and a type of signature (algorithm) to generate a JWT token.

In your case, you generated that token on .NET Core, which is totally fine, and then you need to send this token to a Java API, which is supposed to be totally fine as well.

Technically if you use the same secret on both sites and the same expiration time ( .NET and Java ) a token generated on the .NET side is supposed to be totally usable on the Java side.

I will do some suppositions based strictly in my opinion:

  1. On the java side, you're supposed to use a middleware to receive that token and this middleware you do that processing for you (getting the token from the authorization header) and decrypting it with no big effort necessary.

  2. I have a set of microservices that I have a JWT generated on my identity microservice and this JWT is used on all other microservices with no issues.

  3. Even having a .NET App generating that JWT you may consume that JWT on the Java App if you use the same secret key, the same algorithm and the same expiration time;

  4. You should try to use middleware like that to implement the JWT on the Java side. The way you're doing you are writing a ton of code to do something that is done in several ways for Java: https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/

  5. All the best ways to implement JWT you will not see any logic on the controllers to decrypt the token and you will not have to write additional code for that. You just set up some middleware at some point. If you in this case really need to code that you may follow this example (which have Claims. The same model can be found to .NET as well on Microsoft Identity Framework + OAuth): https://developer.okta.com/blog/2018/10/31/jwts-with-java

Additional:

What I to REST usually is:

  • Install Swagger so all my REST APIs would get documented properly to be consumed.
  • Use a generator to read the Swagger Open Documentation to get all the requested properly typed on the endpoint (in your case, I would have Swagger on both ends and the .NET side would have a mapping from the Java side. This would eliminate all that WebClient requests you have coded on the .net side).
  • As you did with the middleware any JWT middleware would naturally return 401 - Unauthorized in case the "Authorization" header was not present or was with an invalid or expired bearer token.

Sorry for long explanation. Hope that helps.

do you speak portuguese? Thanks by the answer, i think that's correct i will try to implement the same token and secret key in my applications. I was thinking it was necessary to validate the token in the main application, thanks to your answer, now i know i don't need to send the token again to be validated in the login api. I will try to implement it the way you said. Thank you very much for the reply, it was of great help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM