Enriquecendo 요청 com Traefik

JWT를 기반으로 토큰을 기반으로 한 자동화 기반 플럭스의 주요 부분은 JWT를 사용하거나 백엔드에 정보를 제공하는 데 필요한 정보로서 프론트엔드 FAZ를 사용하여 현실화할 수 있도록 하는 것입니다. Isso pode ser observado com as request do do frontend enviando o header Authorization nas 요청.



É comum que esse token contenha informações do usuário, como por exemplo o id. Então ao receber a requisição, o backend decodifica esse token para extrair essas informações e assim relacionar com algum usuário do banco de dados. Com o usuário em mãos, executamos a ação desejada. Logo abaixou vou dar um exemplo de um serviço em Go que faz exatamente isso.

package service

import (
    "encoding/json"
    "errors"
    "fmt"
    "net/http"
    "strings"

    "github.com/golang-jwt/jwt"
)

type User struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    http.HandleFunc("/", getEmail)

    fmt.Println("Listening on :8081")
    err := http.ListenAndServe(":8081", nil)
    if err != nil {
        fmt.Println(err)
    }
}

func getEmail(w http.ResponseWriter, r *http.Request) {
    // example from https://pkg.go.dev/github.com/golang-jwt/jwt/[email protected]
    authHeader := r.Header.Get("Authorization")
    tokenStr, err := getToken(authHeader)
    if err != nil {
        fmt.Println(err)
    }

    token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
        }

        return []byte("my_secret_key"), nil
    })

    claims, ok := token.Claims.(jwt.MapClaims)
    if !ok || !token.Valid {
        fmt.Println(err)
    }

    // get the user from the ID
    // user := users.GetUserById(claims["user_id"])
    userID := claims["user_id"].(string)
    user := User{ID: userID, Name: "Matheus Mina", Email: "[email protected]"}
    userJSON, _ := json.Marshal(user)
    w.Write(userJSON)
}

func getToken(tokenStr string) (string, error) {
    authHeaderParts := strings.Fields(tokenStr)
    if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
        return "", errors.New("Not valid")
    }

    return authHeaderParts[1], nil
}


Esse fluxo funciona muito bem para monolitos, mas para micro-serviços não. O problema é que ao ir para um ambiente de micro-serviços, essa logica responsável por abrir o token tem que ser replicada para cada um dos serviços novos. Se por acaso o formato do token mudar, todos os micro-serviços vão ter que se atualizar para seguir o padrão novo de token.

Para evitar esse problema, podemos fazer algo chamado de enriquecimento de requests. 요청 원본을 추가로 구성하고 백엔드에서 정보를 제공할 수 있습니다. Um serviço que faz isso, por exemplo, é o Cloudflare que adiciona alguns headers na sua requisição. Para fazer esse enriquecimento, podemos colocar uma aplicação intermediaria para fazer essa abertura de token e colocar a requisição no header das demais respostas.

Uma maneira bem simples de fazer isso é utilizar os mecanismos de middleware do Traefik . Ele é um proxy reverso e load balancer que nos permite de maneira simples fazer roteamento entre os nossos microserviços. Além disso, ele é open-source e escrito em Go. Traefik에서 미들웨어 아이디어를 활용하여 어떤 방식으로든 ficaria assim:



법적으로 괜찮습니까? Para resumir tudo, o ciclo da requisição vai funcionar assim:
  • 백엔드에 필요한 사용자 요청
  • O Traefik recebe a requisição e segura a requisição original
  • O Traefik faz uma nova requisição ao 미들웨어
  • Traefik은 미들웨어에 대한 응답과 추가 헤더 구성에 대해 원본을 요청했습니다.
  • O Traefik encaminha a request original ao serviço backend
  • O serviço responde o usuário

  • Colocando a mão na massa, vamos configurar nosso serviço no Traefik receber e encaminhar as chamadas para o nosso serviço.

    entryPoints:
      web:
        # Listen on port 8081 for incoming requests
        address: :8081
    
    providers:
      # Enable the file provider to define routers / middlewares / services in file
      file:
        directory: /path/to/dynamic/conf
    
    # dynamic config below
    http:
      routers:
        # Define a connection between requests and services
        user-service:
          rule: "Path(`/users`)"
          service: user-service
    
      services:
        # Define how to reach an existing service on our infrastructure
        user-service:
          loadBalancer:
            servers:
            - url: http://private/user-service
    


    essa configuração, todo request para /users vai ir para o nosso UserService. A idéia aqui é adicionar um middleware no meio, de forma que seja transparente para o usuário que o token está sendo aberto. Para isso vamos criar um outro microserviço cuja responsabilidade seja só abrir esse token e enriquecer essa request.

    package middleware
    
    import (
        "errors"
        "fmt"
        "log"
        "net/http"
        "strings"
    
        "github.com/golang-jwt/jwt"
    )
    
    func main() {
        http.HandleFunc("/", setHeaderExample)
    
        fmt.Println("Listening on :8082")
        err := http.ListenAndServe(":8082", nil)
        if err != nil {
            log.Fatal(err)
        }
    }
    
    func setHeaderExample(w http.ResponseWriter, r *http.Request) {
        // example from https://pkg.go.dev/github.com/golang-jwt/jwt/[email protected]
        authHeader := r.Header.Get("Authorization")
        tokenStr, err := getToken(authHeader)
        if err != nil {
            fmt.Println(err)
        }
    
        token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
            }
    
            return []byte("my_secret_key"), nil
        })
    
        claims, ok := token.Claims.(jwt.MapClaims)
        if !ok || !token.Valid {
            fmt.Println(err)
        }
    
        // Setting the header X-User-Id"
        userID := claims["user_id"].(string)
        w.Header().Add("X-User-Id", userID)
        w.Write([]byte("This response has the X-User-Id header"))
    }
    
    func getToken(tokenStr string) (string, error) {
        authHeaderParts := strings.Fields(tokenStr)
        if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
            return "", errors.New("Not valid")
        }
    
        return authHeaderParts[1], nil
    }
    


    Dessa forma, traefik 미들웨어 메모 추가 필요:

    http:
      services:
        # Define how to reach an existing service on our infrastructure
        user-middleware:
          loadBalancer:
            servers:
            - url: http://private/user-middleware  
    
        middlewares:
        user-middleware:
          forwardAuth:
            address: "http://private/user-middleware"
            authResponseHeaders:
              - "X-User-ID"
    


    UserService 유틸리티 또는 미들웨어에 대한 vamos dizer 선택:

    http:
      routers:
        # Define a connection between requests and services
        user-service:
          rule: "Path(`/users`)"
          service: user-service
                middlewares:
          - user-middleware
    


    완전한 구성 형식:

    entryPoints:
      web:
        # Listen on port 8081 for incoming requests
        address: :8081
    
    providers:
      # Enable the file provider to define routers / middlewares / services in file
      file:
        directory: /path/to/dynamic/conf
    
    # dynamic config below
    http:
      routers:
        # Define a connection between requests and services
        user-service:
          rule: "Path(`/users`)"
          service: user-service
                middlewares:
          - user-middleware
        middlewares:
        user-middleware:
          forwardAuth:
            address: "http://private/user-middleware"
            authResponseHeaders:
              - "X-User-ID"
      services:
        # Define how to reach an existing service on our infrastructure
        user-service:
          loadBalancer:
            servers:
            - url: http://private/user-service
            user-middleware:
          loadBalancer:
            servers:
            - url: http://private/user-middleware  
    


    빨리! Mágica funcionando! 현재 요청 프로 UserService vão ter o 헤더X-User-Id . Para finalizar, é só a gente remover o código que "abre"o token e passar a ler a informação vinda do header. Nosso 핸들러 ficaria assim:

    func getEmailFinal(w http.ResponseWriter, r *http.Request) {
        // get the user from the ID
        // user := users.GetUserById(claims["user_id"])
        userID := r.Header.Get("X-User-Id")
        user := User{ID: userID, Name: "Matheus Mina", Email: "[email protected]"}
        userJSON, _ := json.Marshal(user)
        w.Write(userJSON)
    }
    


    Ao enriquecer a request podemos simplificar o código dos nossos serviços, repassando informações utéis ao backend de forma transparente. Podemos ver que o handler do nosso serviço ficou bem mais limpo, focando somente no que ele de fato deveria fazer.

    Se curtiu o post, você também pode me encontrar no , Github ou .

    좋은 웹페이지 즐겨찾기