前言
现在是:2022年6月2日15:43:51
上篇文章讲述了springboot中实现用户统一认证的具体内容,主要从后端角度出发的,其实大部分功能还是前端与后端交互的比较多,上篇文章知识罗列了几个回调接口,我一般写这种API接口都是这样的思路。将调用的地址统一放在一个工具类(或者接口)中,然后具体的业务放在service
层来实现,这样也方便后面别的类中共用。
涉及技能点
- 前端:Vue(avue)
- 后端:springboot (bladex框架)
- 数据库:mysql 5.7及以上
实现思路
- 上游平台通过回调接口,将用户和组织机构同步至子系统
- 上游平台通过
url
在地址栏中挂sessionid
的方式访问子系统的登录页面 - 子系统检地址栏中是否有
sessionid
,如果有,则拿着sessionid
去上游系统获取用户信息,然后在子系统中拿着用户信息自动登录 - 如果地址栏中没有
sessionid
,则需要带着子系统的登录地址,重定向至上游平台(上游平台怎么处理的,我就不知道了,我猜测,如果用户未在上游平台登录,则不带sessionid
来的子系统,如果登录了则会带着过来。所以重定向到上游平台时,应该是让用户重新进行登录的) - 当用户点击退出时,清除子系统的用户登录状态的同时还需要清除上游系统,且重定向至上游平台的登录页面
代码实现
- 在
permission.js
中判断地址栏中是否带着xxl_sso_sessionid
参数,拿到参数的值,去做处理。
代码如下:
router.beforeEach((to, from, next) => { //单点登录 //拿到地址栏中的参数 //获取参数 const threeSessionId = getQueryString("xxl_sso_sessionid"); if (threeSessionId) { //拿到用户名 threeCheckLogin(threeSessionId).then(res => { const code = res.data.code; if(code===200){ const username = res.data.data; //异步进行登录 store .dispatch("LoginPrammeSystem", { username }) .then(() => { sessionStorage.setItem("tag",0); //将ssoSessionKey保存起来(退出的时候要用,退出的时候记得要清空掉) localStorage.setItem("threeSessionId",threeSessionId); location.href = location.origin; }); } }); return; }else{ //没有带着标识过来 //第一步:在业务系统登录请求中判断Request中是否包含key值为xxl_sso_sessionid的值, // 如果获取不到,跳转到统一认证平台登录页面,需要带上跳转地址 // (http://统一认证平台IP/login?redirect_url=业务系统登录地址) // 第二步:在统一认证平台登录页面点击“登录”按钮,登录成功后则跳转到http: // 业务系统登录地址?xxl_sso_sessionid=5_9b52f8fe1be24693a7492af854d53759 const tag = sessionStorage.getItem("tag"); if(tag!=='0'){ location.href = tyrzptLoginUrl+"/login?redirect_url="+xuappLoginUrl; return; } } const meta = to.meta || {}; const isMenu = meta.menu === undefined ? to.query.menu : meta.menu; store.commit("SET_IS_MENU", isMenu === undefined); if (getToken()) { if (store.getters.isLock && to.path !== lockPage) { //如果系统激活锁屏,全部跳转到锁屏页 next({ path: lockPage }); } else if (to.path === "/login") { //如果登录成功访问登录页跳转到主页 next({ path: "/" }); } else { //如果用户信息为空则获取用户信息,获取用户信息失败,跳转到登录页 if (store.getters.token.length === 0) { store.dispatch("FedLogOut").then(() => { // next({ path: "/login" }); //跳转到统一认证平台登录页面 location.href = tyrzptLoginUrl + "/login" }); } else { const value = to.query.src || to.fullPath; const label = to.query.name || to.name; const meta = to.meta || router.$avueRouter.meta || {}; const i18n = to.query.i18n; if (to.query.target) { window.open(value); } else if ( meta.isTab !== false && !validatenull(value) && !validatenull(label) ) { store.commit("ADD_TAG", { label: label, value: value, params: to.params, query: to.query, meta: (() => { if (!i18n) { return meta; } return { i18n: i18n }; })(), group: router.$avueRouter.group || [] }); } next(); } } } else { //判断是否需要认证,没有登录访问去登录页 if (meta.isAuth === false) { next(); } else { //next("/login"); //跳转到统一认证平台登录页面 location.href = tyrzptLoginUrl+"/login" } } });
- 拿到地址栏中
xxl_sso_sessionid
参数值,去上游系统中获取用户信息,接口已经在上篇文章中发出,这边只写调动的地方。
代码如下:
/** * 拿着session_id去获取用户名去 * @returns {AxiosPromise} * @param ssoSessionKey */ export const threeCheckLogin = (ssoSessionKey) => { return request({ url: '/api/api/sso/check_login', method: 'get', params: { ssoSessionKey, } }) }
注意:xxl_sso_sessionid
的值,需要记录住,在退出的时候需要用,我这里直接放在了localStorage
中。
- 调用免密登录的方法,
threeCheckLogin
方法会给我们返回用户名,所以我们带着用户名,调用LoginPrammeSystem
方法去登录。代码如下:
在modules/user.js
中的actions
写:
//统一认证平台过来的用户登录此系统 LoginPrammeSystem({ commit }, userInfo) { return new Promise((resolve, reject) => { LoginPrammeSystem("000000", userInfo.username,"xuapp") .then((res) => { const data = res.data; if (data.error_description) { Message({ message: data.error_description, type: "error", }); } else { commit("SET_TOKEN", data.access_token); commit("SET_REFRESH_TOKEN", data.refresh_token); commit("SET_TENANT_ID", data.tenant_id); commit("SET_USER_INFO", data); commit("DEL_ALL_TAG"); commit("CLEAR_LOCK"); } resolve(data); }) .catch((error) => { reject(error); }); }); },
- 然后在
api/user.js
中写上具体登录的方法:
/*统一认证平台登录系统*/ export const LoginPrammeSystem = (tenantId, username, password) => request({ //去训练方案里面的系统登录 url: "/api/blade-auth/oauth/token", method: "post", headers: { "Tenant-Id": tenantId, }, params: { tenantId, username, password, grant_type: "custom", scope: "all", type: "", }, });
- 在
org.springblade.modules.auth.granter
包下面新建自定义鉴权类CustomTokenGranter
,代码如下:
/* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.modules.auth.granter; import lombok.AllArgsConstructor; import org.springblade.core.log.exception.ServiceException; import org.springblade.core.tool.utils.DigestUtil; import org.springblade.core.tool.utils.Func; import org.springblade.modules.auth.enums.UserEnum; import org.springblade.modules.auth.provider.ITokenGranter; import org.springblade.modules.auth.provider.TokenParameter; import org.springblade.modules.auth.utils.TokenUtil; import org.springblade.modules.system.entity.Tenant; import org.springblade.modules.system.entity.UserInfo; import org.springblade.modules.system.service.ITenantService; import org.springblade.modules.system.service.IUserService; import org.springframework.stereotype.Component; /** * @Description: 别的系统登陆本系统 * @author: 穆雄雄 * @date: 2022年5月24日17:58:31 No such property: code for class: Script1 * @Return: */ @Component @AllArgsConstructor public class CustomTokenGranter implements ITokenGranter { public static final String GRANT_TYPE = "custom"; private final IUserService userService; private final ITenantService tenantService; @Override public UserInfo grant(TokenParameter tokenParameter) { String tenantId = tokenParameter.getArgs().getStr("tenantId"); String username = tokenParameter.getArgs().getStr("username"); String password = tokenParameter.getArgs().getStr("password"); UserInfo userInfo = null; if (Func.isNoneBlank(username)) { // 获取租户信息 Tenant tenant = tenantService.getByTenantId(tenantId); if (TokenUtil.judgeTenant(tenant)) { throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION); } userInfo = userService.userInfo(tenantId, username); } return userInfo; } }
整个前端部分的登录流程大体就是这样的~欢迎大家指正。