Разработка веб-сервера на Go и веб-клиента к API этогому серверу

60 000 руб. за проект
19 марта 2022, 15:34 • 5 откликов • 33 просмотра
Сервер регистрации callback

1. Написать HTTP-сервер, сохраняющий получаемые этим сервером HTTP-запросы в базе данных. Реализовать на языке программирования Go/golang
2. Реализовать пакет на Go/golang для клиентского использования этого API сервера из п. 1

Краткое описание работы сервер:

0. Сервер, принимающий запросы по HTTP
1. Разрешаем серверу прием запросов по определенному URL
2. После чего все HTTP-запросы получаемые по этому разрешенному в п.1 URL сохраняются в базе данных
3. После чего получаем от сервера информацию: был ли уже получен запрос по этому разрешенному callback URL
и какой именно это был запрос (вся информация - тело запроса, URL, дата-время, заголовок запроса и пр.)

Требуется подробно логировать работу сервера (вывод лога в стандартные потоки вывода stderr, stdout)

------------------------------------------------------------------------------------------------------

Использовать логирование https://github.com/uber-go/zap
Использовать базу данных https://github.com/etcd-io/bbolt
Для работы с HTTP использовать https://github.com/gin-gonic/gin

Использовать go mod
Использовать go 1.18

Будет использоваться под Linux, то есть должно нормально под Linux компилироваться и запускаться.
Реализовать с помощью обычной программы на Go, способной работать из-под systemd
(это обычная программа на Go, systemd сам делает все необходимое для превращения программы в демона/сервис Linux)

------------------------------------------------------------------------------------------------------

Управление сервером по простому API HTTP/JSON, вызовы защищены подписью проверяемой ключом ed25519

Функции API

1) Разрешить сохранять в БД запрос HTTP, принятый сервером по конкретному URL
2) Отменить п. 1)
3) Очистить все разрешения на URL выданные в п. 1)
4) Узнать был ли к серверу ранее выполнен запрос по определенному URL (получение информации из БД)

Подробная документация по этому API - в личку

Фактически сервер работает с двумя группами URL

/api/v1/API_Управления_Сервером (эти вызовы защищены проверкой подписи ключом ed25519)

/callback/url_разрешенные_для_сохранения_в_БД (эти вызовы сохраняются в БД если в 1) было разрешение для данного конкретного URL).

Сохранятся должна вся информация, получаемая сервером по URL /callback/* - дата-время, URL, тело запроса, заголовки запроса.

Пример проверки подписи ed25519

Как загрузить публичный ключ ed25519 из файла:

import (
"crypto/ed25519"
"encoding/base64"
"errors"

"go.uber.org/zap"
)

func getPublicKeyFromBinary(lg *zap.Logger, data []byte) (ed25519.PublicKey, error) {

l := lg.Named("getKeyFromBinary").With(zap.String("data", string(data)))

decoded, err := base64.StdEncoding.DecodeString(string(data))
if err != nil {
errMsg := "Can not decode key from base64"
l.Error(errMsg, zap.Error(err), zap.String("data", string(data)))
return nil, errors.New(errMsg)
}

publicKey := ed25519.PublicKey(decoded)
if len(publicKey) != ed25519.PublicKeySize {
errMsg := "Unexpected size of ed25519 public key"
l.Error(errMsg, zap.Int("len", len(ed)))
return nil, errors.New(errMsg)
}

return publicKey, nil
}

Как выполнять проверку подписи с помощью ed25519:

import (
"crypto/ed25519"
"encoding/base64"
"io/ioutil"
"net/http"

"github.com/gin-gonic/gin"
"go.uber.org/zap"
)

func verifySignature(message []byte, signature string) bool {

signatureB, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return false
}

return ed25519.Verify(publicKey, message, signatureB)
}

func GinSignatureVerifyMiddleware(ctx *gin.Context) {

bd, err := ioutil.ReadAll(ctx.Request.Body)
if err != nil {
ctx.AbortWithStatus(http.InternalError)
}

// Невозможно прочитать HTTP body дважды
// Но нам нужно это сделать первый раз для проверки подписи
// И второй раз собственно для анализа тела запроса (параметров API)
// Поэтому после чтения тела HTTP-запроса первый раз
// Необходимо его сохранить со специальным предопределенным
// пакетом Gin ключем

ctx.Set(gin.BodyBytesKey, bd)

h := ctx.GetHeader("Signature")

if !verifySignature(bd, h) {
ctx.AbortWithStatus(http.InternalError)
}
}

Подключить middleware к gin можно так

router:= gin.Default()
api:= router.Group("/api/v1/callback-register", GinSignature)
api.POST("enable-callback-url", handlerName)


Отзывы
Avatar r50 a6ce93fe35b158fd29ba0e8681c918c22117160e9586a56eee4ffbc20df9bda1
Заказчик
 
2 года назад
R50 9fcaaa81e45308adcf629fe9582dc203
Фрилансер
 
2 года назад