Go: RESTful - API-KEY

De Aulas

Afluentes: Sistemas Distribuídos e Mobile

Exemplo usando API KEY

Agora vamos ver um exemplo usando autenticação via api key.

package main

import (
	"bufio"
	"fmt"
	"net/http"
	"os"
)

func main() {
	router := http.NewServeMux()

	router.HandleFunc("GET /auth", func(w http.ResponseWriter, r *http.Request) {
		key := r.Header.Get("X-API-KEY") // Pegamos a informação X-API-KEY do cliente
		if key != getAPIKey() {          // e testamos com a informação do arquivo
			http.Error(w, "Forbidden", http.StatusForbidden)
			return
		}
		fmt.Fprintf(w, "Autentication Ok!")
	})

	err := http.ListenAndServe("localhost:8080", router)
	if err != nil {
		fmt.Println(err.Error())
	}
}

// Pegamos a informação do arquivo apikey.txt que é nossa chave
// propriamente dita. Se não existir o arquivo ou der algum erro,
// retorna uma string vazia.
func getAPIKey() string {
	file, err := os.Open("apikey.txt")
	if err != nil {
		return ""
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	if scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

Observe que no código acima, estamos usando uma função chamada getAPIKey que busca uma informação única de um arquivo chamado apikey.txt. Caso dê algum erro, retorna uma string vazia. Então usamos essa informação para comparar com uma informação recebida da requisição, no cabeçalho, chamada X-API-KEY. Se forem iguais, autentica, senão fecha a aplicação e retorna um código de erro. Podemos usar o curl para testar:

curl -H "X-API-KEY:01234" http://localhost:8080/auth

Veja que adicionamos um cabeçalho agora. Para validar, temos que ter o arquivo apikey.txt com a informação 01234 dentro.

Uma maneira mais simples é colocar uma constante global e pegar a informação dessa constante. Contudo, a maneira apresentada acima permite que você possa alterar a apikey sem a necessidade de reiniciar a api.

API KEY por Usuário

Outra forma interessante é gerenciar as API KEYs por usuário, permitindo que cada usuário tenha sua própria apikey. Pode-se armazenar em um banco de dados ou arquivo json. Em banco de dados é mais recomendável, pois permite que cada usuário gerencie sua própria apikey, alterando ela quando necessário e aumentando o grau de segurança.

Então agora, nossa apikey seria um par de informações, sendo o usuário e a chave:

// Podemos ler as informações de um arquivo JSON e jogar em um MAP
apiKeys := getAPIKeys("apikeys.json")

// Pegamos as informações vindas da requisição
user := r.Header.Get("X-User")
key := r.Header.Get("X-API-KEY")

// Depois validamos
if apiKeys[user] != key {
	http.Error(w, "Forbidden", http.StatusForbidden)
	return
}

Nesse caso, o arquivo apikeys.json ficaria mais ou menos assim:

{
    "user1": "apikey1",
    "user2": "apikey2",
    "user3": "apikey3"
}

E pra testar com curl:

curl -H "X-User: user1" -H "X-API-KEY: apikey1" http://localhost:8080/auth