๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๊ฐœ์ธ ๊ณต๋ถ€/IoT

node-red API์™€ Auth์„ค์ •

by syLim___ 2024. 9. 30.
728x90

โœ… Node-red ์„ค์ •ํŒŒ์ผ(settings.js)

- ๋‚˜๋Š” ๋„์ปค ์œ„์— node-red ์˜ฌ๋ฆฐ ๊ฑฐ๋ผ์„œ ์•„๋ž˜ ๋ช…๋ น์–ด๋กœ settings.js ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

> docker exec -it nodered /bin/bash
> vi /data/settings.js
  • node-red๊ฐ€ ์‹œ์ž‘๋  ๋•Œ ์ตœ์ดˆ๋กœ ํ•œ ๋ฒˆ๋งŒ ์ ์šฉ๋˜๋Š” ์„ค์ •์ด๋‹ค.
  • ์„ค์ •ํŒŒ์ผ์„ ์ˆ˜์ • ํ›„ ์ ์šฉํ•˜๋ ค๋ฉด node-red๋ฅผ ๊ป๋‹ค ์ผœ์•ผ ํ•œ๋‹ค.

 

๐Ÿ“Œ httpAdminRoot ์„ค์ •

  • httpAdminRoot์— ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด, ํ•ด๋‹น ๊ฒฝ๋กœ๊ฐ€ ๋ฃจํŠธ ๊ฒฝ๋กœ๊ฐ€ ๋œ๋‹ค.
  • ๋””ํดํŠธ ๋ฃจํŠธ ๊ฒฝ๋กœ๋Š” `/` ์ด๋ฏ€๋กœ `http://์„œ๋ฒ„์•„์ดํ”ผ:1880` ๋กœ ์ ‘์†ํ•˜๋ฉด ๋œ๋‹ค.

 

๐Ÿ“Œ adminAuth ์„ค์ •

  • ๋…ธ๋“œ ๋ ˆ๋“œ editor๋‚˜ admin API๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์‚ฌ์šฉ์ž ์ธ์ฆ์„ ์–ด๋–ป๊ฒŒ ํ• ์ง€ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„
  • ๋””ํดํŠธ๋กœ๋Š” ์ธ์ฆ ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋˜์–ด ์žˆ๋‹ค.
  • username์™€ ํŒจ์Šค์›Œ๋“œ(bcrypt๋กœ ์•”ํ˜ธํ™”๋œ ํ˜•ํƒœ)๋ฅผ ์ ์–ด์ฃผ๊ณ , permissions ์—๋Š” * ๋˜๋Š” read๋ฅผ ์ ์–ด์ฃผ๋ฉด ๋œ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ธ์ฆ์ด ํ•„์š”ํ•˜๋„๋ก ์„ค์ •ํ•  ๊ฒฝ์šฐ, ๋…ธ๋“œ๋ ˆ๋“œ API๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด
    • username๊ณผ password๋ฅผ ์ด์šฉํ•ด ํ† ํฐ์„ ๋ฐœ๊ธ‰๋ฐ›์•„์„œ,
    • ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ Authorization ํ—ค๋”์— ๋ฐœ๊ธ‰๋ฐ›์€ ํ† ํฐ์„ ๋‹ด์•„์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

 

โœ… Node-red API

๐Ÿ“Œ GET /flows

  • ํ˜„์žฌ ๋ฐฐํฌ๋˜์–ด์žˆ๋Š” flows.json ํŒŒ์ผ์„ ์‘๋‹ต body์— ๋‹ด์•„ ๋ณด๋‚ธ๋‹ค.

๐Ÿ“Œ POST /flows

  • ํ˜„์žฌ flows๋ฅผ ์š”์ฒญ body์— ๋‹ด๊ธด flows.json์œผ๋กœ ์ˆ˜์ •ํ•˜์—ฌ ๋ฐฐํฌํ•œ๋‹ค.

https://nodered.org/docs/api/admin/methods/

 

๐Ÿฃ flows.json

  • json ๋ฐฐ์—ด ํ˜•ํƒœ์ด๊ณ  ๊ฐ json ๊ฐ์ฒด ํ•˜๋‚˜๋Š” ํƒญ ๋˜๋Š” ๋…ธ๋“œ์ด๋‹ค.
  • Node-red์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋…ธ๋“œ๋“ค์ด ํด๋ž˜์Šค ํ˜•ํƒœ๋กœ ์ •์˜๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋Š”์ง€ ์ฐพ์•„๋ดค์ง€๋งŒ,
  • Java์—๋„, JavaScript์—๋„ ์—†์—ˆ์Œ.

→ flows ์ˆ˜์ •ํ•˜๋ ค๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ๋“  flows.json์„ ์ง์ ‘ ์ž‘์„ฑํ•ด์„œ POST /flows ์š”์ฒญ ๋ณด๋‚ด์•ผํ•จ

flows.json ์˜ˆ์‹œ

[
    {
        "id": "9b9dc9d130c425ba",
        "type": "mqtt in",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "topic": "#",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "e4c4de3f1d988aba",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 270,
        "y": 260,
        "wires": [
            [
                "478118ba5279275d"
            ]
        ]
    },
    {
        "id": "7c06fcd191b77fab",
        "type": "mqtt out",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "topic": "",
        "qos": "",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "e4c4de3f1d988aba",
        "x": 550,
        "y": 260,
        "wires": []
    },
    {
        "id": "478118ba5279275d",
        "type": "function",
        "z": "f6f2187d.f17ca8",
        "name": "function 1",
        "func": "msg.payload.time = Math.floor(Date.now()/1000);\\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 420,
        "y": 260,
        "wires": [
            [
                "7c06fcd191b77fab",
                "6c977802da75f838"
            ]
        ]
    },
    {
        "id": "6c977802da75f838",
        "type": "debug",
        "z": "f6f2187d.f17ca8",
        "name": "debug 1",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 580,
        "y": 180,
        "wires": []
    },
    {
        "id": "e4c4de3f1d988aba",
        "type": "mqtt-broker",
        "name": "๋งฅ๋ฏธ๋‹ˆ",
        "broker": "192.168.70.203",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "autoUnsubscribe": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    }
]

 

- ์ฒซ ๋ฒˆ์งธ ์˜ˆ์‹œ: flow ์ถ”๊ฐ€

POST http://localhost:1880/flows

[
    {
        "id": "edc211502dc456d3",
        "type": "mqtt in",
        "z": "e30ff5fe7777e085",
        "name": "",
        "topic": "#",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "4f5d917ba7bff899",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 470,
        "y": 380,
        "wires": [
            [
                "f77710ba99621141"
            ]
        ]
    },
    {
        "id": "f77710ba99621141",
        "type": "function",
        "z": "e30ff5fe7777e085",
        "name": "add time",
        "func": "msg.payload.time = Math.floor(Date.now() / 1000);\\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 620,
        "y": 380,
        "wires": [
            [
                "0c3c71a50fcaef03"
            ]
        ]
    },
    {
        "id": "0c3c71a50fcaef03",
        "type": "mqtt out",
        "z": "e30ff5fe7777e085",
        "name": "",
        "topic": "",
        "qos": "",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "4f5d917ba7bff899",
        "x": 770,
        "y": 380,
        "wires": []
    },
    {
        "id": "4f5d917ba7bff899",
        "type": "mqtt-broker",
        "name": "๋งฅ๋ฏธ๋‹ˆ",
        "broker": "192.168.70.203",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "autoUnsubscribe": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    }
]

์š”์ฒญ ๋ณด๋‚ด๋ฉด “ํ”Œ๋กœ์šฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค” ๋ผ๊ณ  ํ‘œ์‹œ๋˜๋ฉด์„œ ์ž๋™ ๋ฐฐํฌ๋จ

 

- ๋‘ ๋ฒˆ์งธ ์˜ˆ์‹œ: ์—ฌ๋Ÿฌ ํƒญ ์ถ”๊ฐ€

type: tab

id: ํƒญ ์•„์ด๋””

z : ๋…ธ๋“œ๊ฐ€ ์†ํ•œ ํƒญ์˜ ์•„์ด๋””

POST http://localhost:1880/flows

[
    {
        "id": "ababab",
        "type": "tab",
        "label": "Flow ONE",
        "disabled": false,
        "info": ""
    },
    {
        "id": "8968361bdf2072f1",
        "type": "inject",
        "z": "ababab",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 600,
        "y": 440,
        "wires": [
            [
                "c48c9fc37e393400"
            ]
        ]
    },
    {
        "id": "c48c9fc37e393400",
        "type": "debug",
        "z": "ababab",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 770,
        "y": 440,
        "wires": []
    },

    {
        "id": "aaabbbb",
        "type": "tab",
        "label": "Flow TWO",
        "disabled": false,
        "info": ""
    },
    {
        "id": "bbbbbddddd",
        "type": "function",
        "z": "aaabbbb",
        "name": "function",
        "func": "msg.payload.time = Math.floor(Date.now()/1000);\\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 420,
        "y": 260,
        "wires": [
            [
                "7c06fcd191b77fab"
            ]
        ]
    }
]

 

 

โœ… Auth ์„ค์ •ํ•˜๊ธฐ

๐Ÿ“Œ GET /auth/login (Authentication ์ •๋ณด ํ™•์ธํ•˜๊ธฐ)

  • settings.json์— adminAuth ๋ณ„๋„๋กœ ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด, ์‘๋‹ต ๋ฐ”๋””์— ์•„๋ฌด ๊ฒƒ๋„ ๋‹ด๊ธฐ์ง€ ์•Š๊ณ  ์˜ด

 

 

  • settings.json์— adminAuth ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด, ์‘๋‹ต ๋ฐ”๋””์— Authentication scheme์ด ๋‹ด๊ฒจ์„œ ์˜ด

 

 

 

 

  • ์œ„์—์„œ ๋งํ–ˆ์ง€๋งŒ, settings.js์€ node-red๊ฐ€ ์‹œ์ž‘๋  ๋•Œ ์ตœ์ดˆ ํ•œ ๋ฒˆ ์ ์šฉ๋˜๋Š” ์„ค์ •์ด๋‹ค.
  • ๊ทธ๋ž˜์„œ ์ค‘๊ฐ„์— ๊ณ„์ •์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ธ์ฆ ๋ฐฉ์‹์„ ๋ฐ”๊พธ๊ณ  ์‹ถ์œผ๋ฉด settings.js ํŒŒ์ผ์„ ์ˆ˜์ •ํ•œ ๋’ค ๋…ธ๋“œ๋ ˆ๋“œ๋ฅผ ์žฌ์‹œ์ž‘ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • settings.js์„ ์ˆ˜์ •ํ•˜๋Š” API๋Š” ๋”ฐ๋กœ ์—†๊ณ , ์ง์ ‘ ์„œ๋ฒ„์— ๋“ค์–ด๊ฐ€์„œ ์ˆ˜์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • (Node.js๊ฐ™์€ ๊ฒฝ์šฐ node-red๋ฅผ imbedํ•˜๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์—์„œ ๋ฐ”๋กœ settings.js ์„ค์ •์„ ํ•  ์ˆ˜ ์žˆ๊ธด ํ•จ)

 

 

๐Ÿ“Œ settings.js adminAuth์„ค์ •

  • username: ์œ ์ €์ด๋ฆ„
  • password: bcrypt๋กœ ์ธ์ฝ”๋”ฉ ๋œ ํ•ด์‹œ๊ฐ’
  • permissions: * ๋˜๋Š” read ์ค‘ ํ•˜๋‚˜

์„ค์ •ํŒŒ์ผ ์ˆ˜์ • ํ›„ ๋…ธ๋“œ๋ ˆ๋“œ restart ํ•˜๋ฉด ์ ์šฉ๋œ๋‹ค.

 

 

๐Ÿ“Œ POST /auth/token (ํ† ํฐ ๋ฐœ๊ธ‰๋ฐ›๊ธฐ)

body์— ํ•„์ˆ˜ ์ •๋ณด๋“ค์„ ๋‹ด์•„์„œ POST ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ํ† ํฐ์ด ์‘๋‹ต์œผ๋กœ ์˜จ๋‹ค.

  • client_id : node-red-admin ๋˜๋Š” node-red-editor ๋‘˜ ์ค‘ ํ•˜๋‚˜ ์ ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • grant_type : ๋ฌด์กฐ๊ฑด password๋ผ๊ณ  ์ ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • scope: * ๋˜๋Š” read ์ค‘ ํ•˜๋‚˜ ์ ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • username: ์ธ์ฆ์— ์‚ฌ์šฉํ•  ์œ ์ €์ด๋ฆ„
  • password: ์ธ์ฆ์— ํ•„์š”ํ•œ ํŒจ์Šค์›Œ๋“œ(์•”ํ˜ธํ™”๋˜์ง€ ์•Š์€ plain text)

์ž˜๋ชป๋œ ์ •๋ณด๋กœ post ๋ณด๋‚ด๋ฉด 403 ์‘๋‹ต๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ‹€๋ ธ๋‹ค๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ์˜ค๊ณ  ํ† ํฐ์€ ๋ฐœ๊ธ‰๋˜์ง€ ์•Š๋Š”๋‹ค.

 

 

๐Ÿ“Œ ๋ฐœ๊ธ‰๋ฐ›์€ ํ† ํฐ์œผ๋กœ node-red API ์ด์šฉํ•˜๊ธฐ

  • ์ด์ œ API๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด Authorization ํ—ค๋”์— “Bearer [์œ„์—์„œ ๋ฐœ๊ธ‰๋ฐ›์€ ํ† ํฐ]”์„ ๋‹ด์•„์ฃผ์–ด์•ผ ํ•œ๋‹ค.

ํ† ํฐ ์—†์ด GET /flows ์š”์ฒญ ๋ณด๋‚ด๋ฉด, 401 ๋œฌ๋‹ค.

 

 

ํ† ํฐ์„ ๋„ฃ์–ด์ฃผ๋ฉด API ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

728x90