在nginx反向代理后面使用keycloak保护nodejs中的路由

时间:2019-07-17 15:33:52

标签: node.js docker nginx keycloak

我启动了一个钥匙斗篷容器:

docker run --name keycloak -p 8080:8080 -e PROXY_ADDRESS_FORWARDING=true -d jboss/keycloak

配置了nginx反向代理:

add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";

server {
    listen 80;
    listen [::]:80;

    server_name 192.168.32.132;

    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name 192.168.32.132;

    ssl_certificate     /etc/ssl/private/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/privkey.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    location /login {
        proxy_pass http://127.0.0.1:9080;

        proxy_set_header    Host                $host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Host    $host;
        proxy_set_header    X-Forwarded-Server  $host;
        proxy_set_header    X-Forwarded-Port    $server_port;
        proxy_set_header    X-Forwarded-Proto   $scheme;

        proxy_buffer_size          128k;
        proxy_buffers              4 256k;
        proxy_busy_buffers_size    256k;

    }

    location /auth {
        proxy_pass http://localhost:8080;

        proxy_set_header    Host                $host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Host    $host;
        proxy_set_header    X-Forwarded-Server  $host;
        proxy_set_header    X-Forwarded-Port    $server_port;
        proxy_set_header    X-Forwarded-Proto   $scheme;

        proxy_buffer_size          128k;
        proxy_buffers              4 256k;
        proxy_busy_buffers_size    256k;
    }

    location /app {
        rewrite ^/app(.*)$ $1 last;
        proxy_pass http://localhost:7080;

        proxy_set_header    Host                $host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Host    $host;
        proxy_set_header    X-Forwarded-Server  $host;
        proxy_set_header    X-Forwarded-Port    $server_port;
        proxy_set_header    X-Forwarded-Proto   $scheme;

        proxy_buffer_size          128k;
        proxy_buffers              4 256k;
        proxy_busy_buffers_size    256k;
        }
    }

然后,我启动了一个nodeJS服务器,需要一些自定义逻辑才能使用户访问应用程序:

// Setup Axios HTTP Request client
AXIOS.defaults.baseURL = CONF.URL;
AXIOS.defaults.headers.common['apikey'] = CONF.apiKey;

// Setup keycloak to use the session memoryStore
var memoryStore = new SESSION.MemoryStore();
var keycloak = new KEYCLOAK({ store: memoryStore });

// Set up a server instance and create a session middleware
const APP = EXPRESS();

APP.use(SESSION({
    secret: CRYPTO.randomBytes(512).toString('hex'),
    resave: false,
    saveUninitialized: true,
    store: memoryStore
}));


// Default route handler
APP.get('/login', keycloak.protect(), function(req, res) {
    console.log('%s accessing protected route', req.get('X-Forwarded-For'));
    let sessionID = '';
    let tokenID = '';

    // Dispatch requests to APP's REST Endpoints
    AXIOS({
        method:'POST',
        url: 'session',
        headers: {'Accept': 'application/json'}
    })
    .then(response => {
        if (response.status == 201) {
            sessionID =  response.data['sessionId'];
            console.log('Received session %s', sessionID);
            return AXIOS({
                method:'POST',
                url: 'session/' + sessionID + '/token',
                headers: {'Accept': 'application/json'},
            });
        }
        else throw new ServerError(response);
    })
    .then(response => {
        if (response.status == 201) {
            tokenID =  response.data['tokenId'];
            console.log('Received token %s', tokenID);
            res.redirect('/app/html/createCustomer?' +  QUERYSTRING.encode({'tokenId': tokenID}));
        }
    })
    .catch( error => {console.log('Error', error.message);}
    });
});

APP.use(keycloak.middleware( { logout: '/logout'}));

// Start servers
HTTP.createServer(APP).listen(CONF.serverPort, CONF.serverInterface, () =>         
console.log('Server listening on %s:%d', CONF.serverInterface, 
CONF.serverPort));

其中CONF.serverPort=9080CONF.serverInterface=127.0.0.1

从keycloak服务器的管理控制台读取keycloak.json

{
    "realm": "app-realm",
    "auth-server-url": "https://192.168.32.132/auth",
    "ssl-required": "external",
    "resource": "app-client",
    "public-client": true,
    "confidential-port": 0
}

在keycloak管理控制台上,我添加了一个用户。客户端的重定向URL设置为通配符。

当我通过路由/ login访问VM的IP 192.168.32.132时,我将重定向到keycloak登录页面,该页面告诉我更改用户的初始密码。

每一次进一步的登录尝试都会导致:ERROR_TOO_MANY_REDIRECTS,无论我如何尝试。

1 个答案:

答案 0 :(得分:0)

当您尝试访问 /auth 路径下的 keycloak 时,它会将您重定向到 /auth,这会命中反向代理并导致循环。

要解决在 / 之后和 proxy_pass 中添加 /auth 在您的 ngnix 配置中的问题:

location /auth/ {
   proxy_pass http://localhost:8080/;
   {...}
}