如何在Dotnet Core中拦截请求令牌Jwt以发送到其他API?

时间:2020-05-23 00:39:23

标签: c# .net .net-core jwt requestfiltering

我正在尝试在Dotnet Core中进行过滤,以验证Login(Java)的其他Api中的令牌JWT 我做到了基本的是,因此,接收到令牌的过滤器将获取令牌Jwt并将其发送以进行验证 在其他Api中。我正在尝试做,但是我没有找到如何做到这一点。我确实用其他方法制作 但是我不知道该怎么做。

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();
        }
    }
}

如何执行验证而不留在Controller中,仅在 带有此类过滤器的startup.cs在Java中?

示例:

@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);
    }
}

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(); 
    }
}

谢谢

2 个答案:

答案 0 :(得分:0)

@RafaelRfs我尝试将其发布为评论,但比受支持的要长,因此我将其发布为答案。

一旦您读到如果不考虑答案,请告诉我,我将删除它。

基本上,您需要一个秘密密钥和一种签名(算法)来生成JWT令牌。

在您的情况下,您在.NET Core上生成了该令牌,这完全没问题,然后您需要将此令牌发送到Java API,这也应该完全没问题。

从技术上讲,如果您在两个站点上使用相同的机密和相同的到期时间(.NET和Java),则在.NET端生成的令牌应在Java端完全可用。

我将严格根据我的观点做一些假设:

  1. 在Java方面,您应该使用中间件来接收该令牌,并且此中间件将为您进行处理(从授权标头中获得令牌)并对其进行解密而无需付出很大的努力。

  2. 我有一组微服务,我在身份微服务上生成了一个JWT,并且该JWT可以在所有其他微服务上正常使用。

  3. 即使使用相同的密钥,相同的算法和相同的到期时间,即使使用.NET App生成该JWT,您也可以在Java App上使用该JWT。

  4. 您应该尝试使用类似的中间件在Java端实现JWT。您的工作方式是编写大量的代码来完成以Java的几种方式完成的工作:https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/

  5. 实现JWT的所有最佳方法,您将不会在控制器上看到任何逻辑来解密令牌,也不必为此编写其他代码。您只是在某个时候设置了一些中间件。如果在这种情况下确实需要编码,则可以遵循此示例(具有Claims。在Microsoft Identity Framework + OAuth上也可以在.NET中找到相同的模型):https://developer.okta.com/blog/2018/10/31/jwts-with-java

其他:

我要恢复的通常是:

  • 安装Swagger,以便正确记录所有我的REST API以便使用。
  • 使用生成器阅读Swagger打开文档,以在端点上正确键入所有请求的类型(在您的情况下,我将在两端使用Swagger,而.NET端将从Java端进行映射。这将消除您已经在.net端编码的所有WebClient请求。
  • 就像您使用中间件一样,任何JWT中间件自然会返回401-未经授权,以防“授权”标头不存在或带有无效或过期的承载令牌。

很抱歉,请长解释。希望有帮助。

答案 1 :(得分:0)

你会说葡萄牙语吗?谢谢你的回答,我认为是正确的 我将尝试在我的应用程序中实现相同的令牌和密钥。我刚在想 有必要在主应用程序中验证令牌, 多亏您的回答,现在我知道我不需要再次发送令牌才能在登录api中进行验证。我会尝试按照您所说的来实现。非常感谢您的答复,它对您有很大帮助!