VueJS 与 Keycloak 集成
Keycloak是一个开源软件产品,旨在为现代的应用程序和服务,提供包含身份管理和访问管理功能的单点登录工具。从概念的角度上来说,该工具的目的是,只用少量编码甚至不用编码,就能很容易地使应用程序和服务更安全。
这里使用的是vue-keycloak-js,官方文档 vue-keycloak-js
vue-keycloak-js 下载与安装
npm i --save @dsb-norge/vue-keycloak-js
Vue 相关配置
在main.js中引入并使用 keycloak
import keycloak from '@dsb-norge/vue-keycloak-js';
...
Vue.use(keycloak , {
init: {
// Use 'login-required' to always require authentication
// If using 'login-required', there is no need for the router guards in router.js
//
checkLoginIframe:false,
onLoad: 'check-sso'
},
config: {
url: 'http://auth.xxxx.net/auth',
realm: 'portal',
clientId: 'portal-client',
},
onReady: (keycloak) => {
new Vue({
router,
keycloak,
render: h => h(App)
}).$mount('#app')
}
});
因为该插件引用的
keycloak-js版本过低("keycloak-js": "4.8.3"),checkLoginIframe配置在才用check-sso模式时必不可少(如果直接使用最新keycloak-js则不用考虑),否则会导致
Refused to frame 'http://auth.xxxx.net/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'self'".
具体原因请参考: www.keycloak.org/docs/latest…
vue-router相关设置
因为不是采用的login-required模式,而是对某些链接才需要添加相关的控制,我们可以在router中添加相关的配置:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
...
{
path: '/labs',
name: 'lab_status_history',
component: () => import('../components/lab_mgmt/LabSummary'),
meta:{
//设置当前链接是否需要登录
requiresAuth:true
}
}
...
]
const router = new VueRouter({
routes
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (router.app.$keycloak.authenticated) {
console.log("Authenticated ")
next()
} else {
const loginUrl = router.app.$keycloak.createLoginUrl()
window.location.replace(loginUrl)
}
} else {
console.log("Authenticated not required ")
next()
}
})
export default router
当path需要登录时,使用$keycloak.createLoginUrl()来生成相应的login URL, 当登录完成时,会自动跳转到当前页面。
可能的坑
- CORS 错误
Axios 配置
本例中使用axios和后端通信,相关的配置如下
import axios from 'axios';
import Qs from 'qs';
import Vue from 'vue';
export const baseURL = `http://api.xxx.net/`;
const service = axios.create({
baseURL: baseURL
});
// 请求拦截器
service.interceptors.request.use(
function(config) {
config.baseURL = fp_baseURL;
config.withCredentials = true;
config.timeout = 5000;
let headers= config.headers
let token = Vue.prototype.$keycloak.token;
if (token) {
//注入得到的token
headers['Authorization']= 'Bearer '+token;
}
config.headers= headers
return config;
},
function(error) {
return Promise.reject(error);
}
);
...
export default service;
可能的坑
- CORS 错误
Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.- 服务端的
OPTIONS响应未包含 刚才注入的Authorization头
- 服务端的
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.- 对于CORS请求,后端要求'Access-Control-Allow-Origin'头,但是程序发出的请求头里并没有包含
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.- 看上去像是本地发出的request里包含了重复的
Access-Control-Allow-Origin,实际上的原因是,对于Authorization头,*通配符模式并不适用,具体原因
- 看上去像是本地发出的request里包含了重复的
NGINX 设置
# Adaptation engine Product env configuration
upstream portal{
server 10.32.219.60:8085;
keepalive 30;
## tengine config
check interval=5000 rise=2 fall=5 timeout=10000 type=http;
check_keepalive_requests 100;
check_http_send "HEAD /swagger-ui.html HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx;
session_sticky;
}
server {
listen 80;
server_name api.xxx.net;
charset utf-8;
location / {
proxy_pass http://portal;
#配置跨域访问
add_header 'Access-Control-Allow-Headers' 'Access-Control-Allow-Origin,Content-Type,Access-Control-Allow-Credentials,Authorization';
#对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为 相应的host,则请求将成功执行。
proxy_hide_header 'access-control-allow-origin';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
# 服务端响应必须包含Access-Control-Allow-Credentials 头部,否则浏览器不会发送身份凭证信息
add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin,Access-Control-Allow-Credentials';
if ($request_method = 'OPTIONS') {
# 当OPTIONS操作时直接返回,不需要到后端,实际上大多数的程序也没有对OPTIONS操作有特定的设置
# 当然如果你的后端是Spring,你也可以通过拦截器的方式实现这一拦截,http://ckjava.com/2019/03/05/SpringBoot-CORS-practice/
return 204;
}
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
upstream portal_web{
server 10.32.219.60:80;
keepalive 30;
## tengine config
check interval=5000 rise=2 fall=5 timeout=10000 type=http;
check_keepalive_requests 100;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx;
session_sticky;
}
server {
listen 80;
server_name portal.xxx.net;
charset utf-8;
location / {
proxy_pass http://fp_portal_web;
#配置跨域访问
add_header 'Access-Control-Allow-Headers' 'Content-Type';
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin,Access-Control-Allow-Credentials';
if ($request_method = 'OPTIONS') {
return 204;
}
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
upstream fp_portal_auth{
server 10.32.219.58:8090;
keepalive 30;
## tengine config
check interval=5000 rise=2 fall=5 timeout=10000 type=http;
check_keepalive_requests 100;
check_http_send "HEAD /auth HTTP/1.0\r\n\r\n";
check_http_expect_alive http_3xx;
session_sticky;
}
server {
listen 80;
server_name auth.xxx.net;
charset utf-8;
location /auth {
proxy_pass http://portal_auth;
# 这一段可以在nginx.conf里设置,主要是为了将请求端和后端链接起来
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;
#配置跨域访问
add_header 'Access-Control-Allow-Headers' 'Access-Control-Allow-Origin,Content-Type,Authorization';
proxy_hide_header 'access-control-allow-origin';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin,Access-Control-Allow-Credentials';
if ($request_method = 'OPTIONS') {
return 204;
}
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
upstream fp_portal_lab_service{
server 10.32.219.58:8091;
keepalive 30;
## tengine config
check interval=5000 rise=2 fall=5 timeout=10000 type=http;
check_keepalive_requests 100;
check_http_send "HEAD /actuator/health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx;
session_sticky;
}
server {
listen 80;
server_name lab-service.xxx.net;
charset utf-8;
location / {
# 这一段可以在nginx.conf里设置,主要是为了将请求端和后端链接起来
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_pass http://portal_lab_service;
#配置跨域访问
add_header 'Access-Control-Allow-Headers' 'Access-Control-Allow-Origin,Content-Type,Access-Control-Allow-Credentials,Authorization';
# add_header 'Access-Control-Allow-Origin' *;
proxy_hide_header 'access-control-allow-origin';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE';
add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin,Access-Control-Allow-Credentials';
if ($request_method = 'OPTIONS') {
return 204;
}
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
# Adaptation engine Product env configuration
upstream portal{
server 10.32.219.60:8085;
keepalive 30;
## tengine config
check interval=5000 rise=2 fall=5 timeout=10000 type=http;
check_keepalive_requests 100;
check_http_send "HEAD /swagger-ui.html HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx;
session_sticky;
}
server {
listen 80;
server_name api.xxx.net;
charset utf-8;
location / {
proxy_pass http://portal;
#配置跨域访问
add_header 'Access-Control-Allow-Headers' 'Access-Control-Allow-Origin,Content-Type,Access-Control-Allow-Credentials,Authorization';
#对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为 相应的host,则请求将成功执行。
proxy_hide_header 'access-control-allow-origin';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
# 服务端响应必须包含Access-Control-Allow-Credentials 头部,否则浏览器不会发送身份凭证信息
add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin,Access-Control-Allow-Credentials';
if ($request_method = 'OPTIONS') {
# 当OPTIONS操作时直接返回,不需要到后端,实际上大多数的程序也没有对OPTIONS操作有特定的设置
# 当然如果你的后端是Spring,你也可以通过拦截器的方式实现这一拦截,http://ckjava.com/2019/03/05/SpringBoot-CORS-practice/
return 204;
}
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
upstream portal_web{
server 10.32.219.60:80;
keepalive 30;
## tengine config
check interval=5000 rise=2 fall=5 timeout=10000 type=http;
check_keepalive_requests 100;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx;
session_sticky;
}
server {
listen 80;
server_name portal.xxx.net;
charset utf-8;
location / {
proxy_pass http://fp_portal_web;
#配置跨域访问
add_header 'Access-Control-Allow-Headers' 'Content-Type';
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin,Access-Control-Allow-Credentials';
if ($request_method = 'OPTIONS') {
return 204;
}
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
upstream fp_portal_auth{
server 10.32.219.58:8090;
keepalive 30;
## tengine config
check interval=5000 rise=2 fall=5 timeout=10000 type=http;
check_keepalive_requests 100;
check_http_send "HEAD /auth HTTP/1.0\r\n\r\n";
check_http_expect_alive http_3xx;
session_sticky;
}
server {
listen 80;
server_name auth.xxx.net;
charset utf-8;
location /auth {
proxy_pass http://portal_auth;
# 这一段可以在nginx.conf里设置,主要是为了将请求端和后端链接起来
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;
#配置跨域访问
add_header 'Access-Control-Allow-Headers' 'Access-Control-Allow-Origin,Content-Type,Authorization';
proxy_hide_header 'access-control-allow-origin';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin,Access-Control-Allow-Credentials';
if ($request_method = 'OPTIONS') {
return 204;
}
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
upstream fp_portal_lab_service{
server 10.32.219.58:8091;
keepalive 30;
## tengine config
check interval=5000 rise=2 fall=5 timeout=10000 type=http;
check_keepalive_requests 100;
check_http_send "HEAD /actuator/health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx;
session_sticky;
}
server {
listen 80;
server_name lab-service.xxx.net;
charset utf-8;
location / {
# 这一段可以在nginx.conf里设置,主要是为了将请求端和后端链接起来
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_pass http://portal_lab_service;
#配置跨域访问
add_header 'Access-Control-Allow-Headers' 'Access-Control-Allow-Origin,Content-Type,Access-Control-Allow-Credentials,Authorization';
# add_header 'Access-Control-Allow-Origin' *;
proxy_hide_header 'access-control-allow-origin';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE';
add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin,Access-Control-Allow-Credentials';
if ($request_method = 'OPTIONS') {
return 204;
}
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}