写在前面:本篇博客只针对前端代码实现,keycloak配置什么的,自己和后端或者运维联调吧。说实在的,因为不熟悉keycloak代码的逻辑,再加上时间紧,所以搞了一些很多骚操作。
登录这些前端代码是写在keycloak项目里的,文件是.ftl,还好政府项目没有对UI有什么很高的要求。扫码登录和手机号验证码,成功之后是跳转到Vue项目里的。
@TOC
扫码登录:
专有钉钉扫码登陆流程
我这里采用的时第二种实现方式,嵌入iframe,但是这样有个弊端,开发时由于跨域,无法改二维码的样式。
template_login.ftl
<div id="login_container">
<#nested "form">
<div class="tabContentCode active" id="frame">
<div class="cover coverbg" id="cover">div>
<iframe name="my-iframe" id="myFrame"
src=""
frameborder="0"
width="310" height="310" scrolling="no">iframe>
div>
div>
<#import "template_login.ftl" as layout>
<@layout.registrationLayout displayInfo=social.displayInfo displayWide=(realm.password && social.providers??); section>
<#if realm.password && social.providers??>
<#list social.providers as p>
<#if p.providerId == "dgwork">
<script defer="defer">
var num = window.localStorage.getItem('first');
console.log("************"+num);
if(!num){
num = 0
window.localStorage.setItem('first', num);
}
if(num==0){
console.log("*****11111*******");
window.addEventListener('message', function(event) {
var origin = event.origin;
//https://openplatform-portal.dg-work.cn/portal/#/helpdoc?docKey=kfzn&slug=engk1k 下面的origin时这个地址里的各环境域名/登录域名,线上环境是login-pro.ding.zj.gov.cn
if (origin == "https://login.dg-work.cn") {
var loginTmpCode = event.data;
//取网址里的参数开始
var search = http_url.substring(1)
if (!search && location.href.lastIndexOf('?') > -1) {
search = location.href.substring(location.href.lastIndexOf('?') + 1)
}
var obj = {}
if (search.length > 0) {
var arr = [], item
arr = search.split('&')
for (var i = arr.length; --i >= 0;) {
item = arr[i].split('=')
obj[item[0]] = item[1]
}
}
//取网址里的参数结束主要取往里的回调地址redirect_uri,code和state是监听返回的也是必须的
var new_uri= decodeURIComponent(obj.redirect_uri) + '?code='+loginTmpCode.code+'&state='+loginTmpCode.state;
try {
//跳到新的回调地址去,也就是页面的跳转
top.location.assign(new_uri);
} catch (e) {
top.location.href=new_uri;
}
}
})
var http_url='';
//htmlUtil时引入 的文件,主要用来对网址解码用的,可以自定义
var login_url=htmlUtil.decode("${p.loginUrl}");
if("${p.providerId}" == 'dgwork'){
//这个玩意也是引入的原生js文件,可以用ajax试试
ajaxHandler.Ajax({
url: login_url,
type: "get",
success: function (result) {
if(result.startsWith("http")){
http_url=result;
console.log(http_url + '---------1')
}
},
error: function (e, status) {
console.log(e.responseText);
}
});
}
//因为不知道二维码啥时候出现,所以做了定时装置。iframe里有个onload事件,自己试了没有成功。
var myVar = setInterval(function(){
var frame = document.getElementById("myFrame")
if(frame){
document.getElementById("myFrame").src = http_url
clearInterval(myVar)
}
}, 500)
num++;
window.localStorage.setItem('first', num);
}
script>
#if>
#list>
#if>
<#if section = "title">
${realm.displayName}
<#elseif section = "header">
${msg("loginTitleHtml",realm.name)}
<#elseif section = "form">
#if>
@layout.registrationLayout>
还有一个要注意的点,我们的网址环境都是https,当跳转或者取到得是http,则会有跨域报错,因此在template_login.ftl加入了这么一行代码,但是关键点来了,开发的时候把代码注释掉,不然会报错。
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
还有一个骚操作就是切换的时候二维码必须更新,因为扫码和手机号都是用的同一个code和state所以,切换到二维码时直接
window.location.reload()就可以了
Vue项目里也很简单
import Vue from 'vue'
import VueKeyCloak from '@dsb-norge/vue-keycloak-js'
export function basicsAuth () {
return new Promise((resolve, reject) => {
let userInfo = {}
let vm = new Vue()
Vue.use(VueKeyCloak , {
init: {
onLoad: 'login-required'
},
config: {
url: window.OIDCPATH,
realm: window.REALM,
clientId: window.CLIENTID,
logoutRedirectUri: window.location.href.split('#')[0]
},
onReady: keycloak => {
// alert(JSON.stringify(keycloak))
keycloak.updateToken()
window.localStorage.setItem('tk', keycloak.token)
vm.$http.defaults.headers['Authorization'] = `Bearer ` + keycloak.token
keycloak.loadUserProfile().success(data => {
userInfo = Object.assign(userInfo, data)
let unionObj = data.attributes.authInfo[0]
let unionId = JSON.parse(unionObj).unionid || JSON.parse(unionObj).accountId
let type = data.attributes.authType[0]
store.commit('SET_USER_DATA', data)
setLocalStorage('sd', data)
store.commit('SET_UNION_ID', unionId)
getUserInfo(unionId, type)
})
},
onInitError: err => {
vm.$message({
type:'error',
message:'账号异常,请联系管理员,即将返回登录页面!',
offset:'80'
})
setTimeout(function() {
window.location.reload();
}, 3000);
}
})
// 根据unionId获取用户信息
function getUserInfo (unionId, type) {
let data = {
thirdUserId: unionId,
thirdUserType: type
}
vm.$get('', data).then(res => {
// vm.$http.defaults.headers['userId'] = res.data.userId
vm.$destroy()
if (res.code === 200) {
userInfo = Object.assign(userInfo, res.data)
resolve(userInfo)
}
}).catch(err => {
console.log(err)
vm.$destroy()
reject(err)
})
}
})
}