Zum Inhalt springen

OBS/Kostenpflichtige Module/RESTServer/Beispiel1

Aus OBS Wiki
Version vom 9. September 2026, 13:32 Uhr von Rademacker (Diskussion | Beiträge)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Kostenpflichtige Module

Internet-Shop
UPS
IMS Professional
SMS
Mehrlager-Verwaltung
Mehrsprachen Modul
Multilanguage Modul
EVA Marketing Tool
Termin-Projekte
Edifact-Schnittstelle
Backup Überwachung Email
OBS Geo Daten
DeliSprint / DPD
Filialen
Cashback
Moebelschnittstelle
Dokumenten Manager
DocuWare-Schnittstelle
OFML-Kalkulation
Versicherungsschaden
Gutschriftsanzeigen
Kameraverwaltung
DataInOut
OpenMasterData / IDS
Sammelpositionen



Beispiel 1: Daten-Abruf mit JWT-Authentifizierung

In diesem Beispiel werden Steuerungs-Variablen aus OBS abgerufen und auf einer Webseite ausgegeben. Der Zugang ist mit JWT-Pflicht eingerichtet, der Konsument muss sich daher zuerst anmelden und einen Token bezogen haben.

Einrichtung in OBS

  • Server-Profil: Standard-TLS-Profil Public-API mit Bindung 0.0.0.0:443
  • Zugang: Web-Dashboard
    • API-Key: zufaellig generiert
    • JWT aktiv, JWT-Endpunkt oauth, JWT-Key zufaellig, JWT-Exp 60 (Minuten)
    • CORS-Origins: https://dashboard.kunde.de
  • Endpunkt: steuerung/v1, dem Profil Public-API zugeordnet
  • Berechtigung: Zugang Web-Dashboard fuer Endpunkt steuerung/v1 freigeschaltet

JWT-Authentifizierungs-Skript (Zugang)

Wird ueber die Zugaenge-Liste mit F7 geoeffnet. Pruefen, ob Benutzername/Passwort gegen die OBS-Benutzerverwaltung passen.

procedure Authenticate(oReader: TxRestReader; oWriter: TxRestWriter);
var cUser : string;
    cPass : string;
    cSql  : string;
    qUser : TxFQuery;
    lOk   : Boolean;
begin
    cUser := '';
    cPass := '';
    oReader.Str('username', cUser);
    oReader.Str('password', cPass);

    cSql := 'SELECT u_nr, u_name FROM benutzer' +
            ' WHERE u_login = ' + DB_SQLVal(cUser) +
            ' AND u_passwort_hash = ' + DB_SQLVal(HashPasswort(cPass)) +
            ' AND u_aktiv = ' + DB_SQLVal('1');

    // DB_SOpen liefert true, wenn die ABFRAGE lief - nicht, wenn ein
    // Datensatz gefunden wurde. Ohne die EoF-Pruefung wuerde jede
    // Anmeldung gelingen, sobald das SQL fehlerfrei ist.
    lOk := false;
    if (DB_SOpen(oDB, cSql, qUser)) then begin
        if (not qUser.EoF) then begin
            lOk := true;
            oWriter.Int('status'           , 1);
            oWriter.Str('_OBS_JWT_ID'      , qUser.A2C('u_nr'));
            oWriter.Str('_OBS_JWT_SUBJECT' , qUser.A2C('u_name'));
            oWriter.Str('_OBS_JWT_AUDIENCE', 'dashboard');
        end;
    end;
    DB_Close(qUser);

    if (not lOk) then begin
        oWriter.Int('status', 9);
        oWriter.Str('error' , 'Benutzer oder Passwort ist falsch');
    end;
end;
HINWEIS: Hier steht in _OBS_JWT_ID die Benutzernummer - derselbe Wert also bei

jeder Anmeldung desselben Benutzers. Das ist zulässig: der Server führt seine Sitzungen über eigene Kennungen (sid/rid), nicht über das jti des Skripts. Mehrfache und parallele Anmeldungen eines Benutzers sind damit

unproblematisch.

Endpunkt-Skript steuerung/v1

Liefert eine Liste von Steuerungs-Werten zurueck. Die Auswertung der JWT-Claims sorgt dafuer, dass nur Audience dashboard Daten erhaelt.

//------------------------------------------------------------------------------
// Ein Wert als Listeneintrag. Der Helfer bekommt den WRITER und schreibt an
// der Stelle hinein, an der er aufgerufen wird - er gibt kein Fragment zurueck.
// Der leere Feldname ist der Eintrag einer Liste.
//------------------------------------------------------------------------------

procedure _AddVar(oWriter: TxRestWriter; const cVar: string);
var cTitel : string;
    cData  : string;
    cEinh  : string;
    lString: Boolean;
    lAlign : Boolean;
begin
    if (ST_Variable(oDB, cVar, cTitel, cData, cEinh, lString, lAlign)) then begin
        oWriter.ObjBegin('');
            oWriter.Str('variable', cVar);
            oWriter.Str('titel'   , cTitel);
            oWriter.Str('wert'    , cData);
            oWriter.Str('einheit' , cEinh);
        oWriter.ObjEnd;
    end;
end;

procedure Get(oReader: TxRestReader; oWriter: TxRestWriter);
begin
    // Audience-Pruefung: nur Dashboard-Tokens akzeptieren. Die Ablehnung ist
    // ein 403 und keine 200 mit Fehlertext - oWriter.Error baut dieselbe
    // Huelle, die auch die Server-eigenen Fehler tragen.
    if (oReader.Param('_OBS_JWT_AUDIENCE') <> 'dashboard') then begin
        oWriter.Error(403, 'FORBIDDEN_ROLE', 'Token nicht für diese Anwendung ausgestellt');
        exit;
    end;

    // Die Antwort ist eine nackte Liste, kein Objekt: RootArr sagt das, und
    // zwar VOR dem ersten Eintrag. Ein ArrEnd gibt es dazu nicht - die
    // Wurzelklammer setzt der Server.
    oWriter.RootArr();
    _AddVar(oWriter, 'WERT_1');
    _AddVar(oWriter, 'WERT_2');
    _AddVar(oWriter, 'WERT_3');
    _AddVar(oWriter, 'WERT_4');
end;

JavaScript-Client (Browser)

Im Browser werden zwei Aufrufe gemacht: erst Token holen, dann Daten lesen.

const API_BASE = 'https://api.meinserver.de';
const API_KEY  = '[API-KEY]';

async function login(username, password) {
    const res = await fetch(`${API_BASE}/oauth/`, {
        method:  'POST',
        headers: { 'Content-Type': 'application/json', 'apikey': API_KEY },
        body:    JSON.stringify({ username, password })
    });
    if (!res.ok) throw new Error('Login fehlgeschlagen');
    const data = await res.json();
    return data.token;
}

async function ladeSteuerung(token) {
    const res = await fetch(`${API_BASE}/steuerung/v1`, {
        method:  'GET',
        headers: {
            'apikey':        API_KEY,
            'Authorization': 'Bearer ' + token
        }
    });
    if (!res.ok) throw new Error('Abruf fehlgeschlagen: ' + res.status);
    return res.json();
}

async function zeigeTabelle() {
    const token = await login('mitarbeiter', 'geheim');
    const werte = await ladeSteuerung(token);

    const tbl = document.getElementById('steuerung');
    werte.forEach(w => {
        const tr = document.createElement('tr');
        tr.innerHTML = `<td>${w.titel}</td><td>${w.wert} ${w.einheit}</td>`;
        tbl.appendChild(tr);
    });
}

zeigeTabelle();

Test mit curl

Token holen:

curl -X POST -H "apikey: [API-KEY]" -H "Content-Type: application/json" ^
     -d "{\"username\":\"mitarbeiter\",\"password\":\"geheim\"}" ^
     https://api.meinserver.de/oauth/

Antwort:

{"token":       "eyJhbGciOi...",
 "accessToken": "eyJhbGciOi...",
 "expiresIn":   3600,
 "serverTime":  "2026-08-19T09:12:33+02:00"}

Da das Skript kein _OBS_JWT_REFRESH_ID liefert, enthält die Antwort kein refreshToken - der Client meldet sich nach Ablauf der 60 Minuten neu an.

Daten abrufen:

curl -H "apikey: [API-KEY]" -H "Authorization: Bearer eyJhbGciOi..." ^
     https://api.meinserver.de/steuerung/v1

Was zeigt das Beispiel?

  • Zweistufige Anmeldung: API-Key + JWT.
  • Sichere Trennung von Konsumenten-Gruppen ueber die Audience-Claim.
  • Eigenes Authentifizierungs-Skript pro Zugang.
  • Browser-Zugriff ueber CORS-erlaubtes Origin.
  • Anmeldung ohne Refresh-Token: einfachster Fall, der Token laeuft nach der Zeit aus JWT-Exp ab.