簡體   English   中英

如何在Dotnet Core中攔截請求令牌Jwt發送到其他API?

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

我正在嘗試在 Dotnet Core 中進行過濾,以驗證我所做的 Login(Java) 的其他 Api 中的令牌 JWT。 基本的是,因此收到令牌,過濾器獲取令牌 Jwt 並將其發送到其他 Api 進行驗證。 我正在嘗試做,但我不知道如何做到這一點。 我確實采用了替代方法,但我不知道如何做正確的。

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

登錄服務.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;
    }   
    }
}

啟動.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 中的類似 class 的過濾器?

例子:

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

謝謝

@RafaelRfs 我嘗試將此作為評論發布,但它比支持的要長,因此我將其發布為答案。

一旦你讀到,如果你不考慮答案,請告訴我,我會刪除它。

基本上,您需要一個密鑰和一種簽名(算法)來生成 JWT 令牌。

在您的情況下,您在 .NET Core 上生成了該令牌,這完全沒問題,然后您需要將此令牌發送到 Java API,這應該也完全沒問題。

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.

我將嚴格按照我的觀點做一些假設:

  1. 在 java 端,您應該使用中間件來接收該令牌,並且該中間件為您執行該處理(從授權標頭獲取令牌)並解密它而無需付出很大的努力。

  2. 我有一組微服務,在我的身份微服務上生成了一個 JWT,這個 JWT 用於所有其他微服務,沒有問題。

  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. 您應該嘗試使用這樣的中間件在 Java 端實現 JWT。 您正在編寫大量代碼來執行 Java 的多種方式: https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/

  5. 實現 JWT 的所有最佳方法,您不會在控制器上看到任何解密令牌的邏輯,並且您不必為此編寫額外的代碼。 您只是在某個時候設置了一些中間件。 如果您在這種情況下確實需要編寫代碼,您可以遵循此示例(有聲明。在 Microsoft Identity Framework + OAuth 上也可以在 .NET 中找到相同的 model): Z5E056C500A1C4B6A7110B50D807BADE//blog. 2018/10/31/jwts-with-java

額外的:

我對 REST 通常是:

  • 安裝Swagger以便我所有的 REST API 將得到正確記錄以供使用。
  • 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請求您在 .net 端編碼)。
  • 正如您對中間件所做的那樣,任何 JWT 中間件自然會返回 401 - 未經授權,以防“授權”header 不存在或帶有無效或過期的承載令牌。

抱歉,解釋太長了。 希望有幫助。

你會說葡萄牙語嗎? 感謝您的回答,我認為這是正確的,我將嘗試在我的應用程序中實現相同的令牌和密鑰。 我認為有必要在主應用程序中驗證令牌,感謝您的回答,現在我知道我不需要再次發送令牌以在登錄 api 中進行驗證。 我將嘗試按照您所說的方式實施它。 非常感謝您的回復,對您有很大幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM