keycloak单点登录(浙政钉2.0扫码、手机号验证码登录)

写在前面:本篇博客只针对前端代码实现,keycloak配置什么的,自己和后端或者运维联调吧。说实在的,因为不熟悉keycloak代码的逻辑,再加上时间紧,所以搞了一些很多骚操作。
登录这些前端代码是写在keycloak项目里的,文件是.ftl,还好政府项目没有对UI有什么很高的要求。扫码登录和手机号验证码,成功之后是跳转到Vue项目里的。
@TOC

浙政钉扫码登录

扫码登录:

在这里插入图片描述
专有钉钉扫码登陆流程
我这里采用的时第二种实现方式,嵌入iframe,但是这样有个弊端,开发时由于跨域,无法改二维码的样式。
在这里插入图片描述

template_login.ftl
<div id="login_container">
	<!--这个时关联form表单的代码 具体代码在sms-captcha-login.ftl-->
                        <#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??>
    	<!--首先循环p-->
		<#list social.providers as p>
		<!--当p.providerId == "dgwork"做判断操作,但是循环了三次,没有找到具体原因,所以就做了一下操作只取第一次循环的数据,之后的全部返回-->
        <#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)
      })
    }
  })
}

猜你喜欢

转载自blog.csdn.net/chenacxz/article/details/113866976