时间:2022-05-24
现在使用若依框架进行开发的公司及个人很多,这个一个优秀的开源框架。目前碰到这样一个需求,甲方目前现有一个平台系统,需要在这个平台系统中调用 我们新开发的系统,希望登录平台后,能够不再登录进入新开发的系统,即所谓的单点登录。这个需求不能用我们常见的CAS 做鉴权,从而实现多系统单点登录。
流程图:
实现思路
1、三方系统(也就是需要跳转我们系统的系统),直接请求我们系统的登录页面,挂着token参数。
2、在我们系统登录界面,判断请求链接中有没有token,没有则正常走登录流程。
3、如果没有token,则重新写一个单点登录的接口,去请求。
4、在后台将拿到的token
,去三方系统中鉴权,通过则继续登录,没有通过则直接返回到登录页面。
实现代码
前端
1.新建thirdPlatLogin
的vue
页面,调用单点登录的方法。【新建无内容页面,不用现有的login页面,主要是避免第三方平台调用时闪现登录框】
在后台将拿到的token,去三方系统中鉴权,通过则继续登录,没有通过则直接返回到登录页面。
thirdPlatlogin.vue 代码如下:
<template>
<div> </div>
</template>
<script>
export default {
name: "Login",
data() {
return {
loginRules: {
},
loading: false,
// 验证码开关
captchaOnOff: true,
// 注册开关
register: false,
redirect: undefined
};
},
watch: {
},
created() {
//平台单独的登录 2022年4月19日11:23:58
this.getLoginByNameAndTokenJ();
},
methods: {
/**
* 三方平台单点登陆系统 2022年4月19日11:22:33
* 只传递token
*/
getLoginByNameAndTokenJ(){
//获取地址栏中的token
var token = this.$route.query.token;
//调用登录的接口
if(token==''||token==undefined||token==null){
//不是那边系统过来的,不走这个地方(阻止created的方法继续向下走)
}else{
//转圈圈,不要看到登陆页面,无感体验
this.loading = true;
var logininfo= {
"token":token
};
//执行另一套登录操作
//不是本系统的用户,去J平台登陆去
this.$store.dispatch("LoginJHaveToken", logininfo).then(() => {
this.$message.success("登录成功");
this.loading = false;
//判断当前角色
getInfo().then((res) => {
//获取角色名称
var rolesName = res.roles[0];
//获取所属场馆
this.deptInfo = res.dept;
sessionStorage.setItem("ssUserName", res.user.nickName);
this.$router.push({path: this.redirect || "/"}).catch(() => {
});
// //如果是场馆管理员
// if (rolesName === 'changguanmanager') {
// this.$router.push({
// path: "/VenueKanban",
// query: {changguan: res, aa: 0},
// replace: true
// }).catch(() => {
// });
// //否则就是其他用户
// } else {
// this.$router.push({path: this.redirect || "/"}).catch(() => {
// });
// }
});
}).catch(err=> {
console.log("有异常信息",err);
//异常信息
this.loading = false;
// if (this.captchaOnOff) {
// this.getCode();
// }
});
}
},
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
</style>
2、在store->modules的user.js
中,实现LoginJHaveToken
方法:3
//平台带着token登录,不需要输入账号密码
//密码都是123456,
//还需要带着token验证一下
LoginJHaveToken({commit},userInfo){
const token = userInfo.token;
const queryParams ={
'token':token
};
return new Promise((resolve, reject) => {
getLoginByJHaveToken(queryParams).then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
}).catch(error => {
reject(error)
})
})
}
3、在api目录的login.js
中,实现getLoginByJHaveToken
方法:
/**
* 平台带着tonken进行登录
*
* @param queryParam
* @returns {*}
*/
export function getLoginByJHaveToken(queryParam) {
console.log(queryParam)
return request({
url: '/toThirdPartGetAuthJHaveToken',
method: 'post',
params: queryParam
})
}
4、在router的index.js中,添加如下路由:
{
path: '/thirdPlatlogin',
component: () => import('@/views/thirdPlatlogin'),
hidden: true
},
5、在src的permission.js中,修改白名单如下:
const whiteList = ['/login', '/auth-redirect', '/bind', '/register','/thirdPlatlogin']
后台:
1.新建第三方平台登录Controller
代码如下:
package com.ruoyi.web.controller.system;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.system.service.ISysPostService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import jodd.http.HttpRequest; //导入方法依赖的package包/类
import io.swagger.annotations.ApiOperation;
import jodd.http.HttpResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* 第三方登录验证
*
* @author ruoyi
*/
@RestController
public class ThirdPartLoginController {
/**
* @Description: 平台带着token来系统里面登陆
* 这边需要做两个步骤:
* 1.检测数据库里面有没有这个用户名,有则不操作,无则添加
* 2.去平台验证一下Token是否有,有的话继续操作后面的登录
* 平台没有这个token,则直接打回去,不让上来
* @author: 穆雄雄
* @date: 2022/4/19 上午 11:38
* @Return: com.ruoyi.common.core.domain.AjaxResult
*/
@Autowired
private ISysUserService userService;
@Autowired
private ISysRoleService roleService;
@Autowired
private ISysPostService postService;
@Autowired
private SysLoginService loginService;
@PostMapping("/toThirdPartGetAuthJHaveToken")
@ApiOperation(value = "平台带着token过来登录")
public AjaxResult toThirdPartGetAuthJHaveToken(String token) {
//调用验证token的方法
JSONObject jsonObject = checkJToken(token);
//以下测试用
// JSONObject jsonObject =new JSONObject();
// jsonObject.put("code","0");
// JSONObject jsonObjectData =new JSONObject();
// jsonObjectData.put("name","zhansan");
// jsonObject.put("data",jsonObjectData);
// jsonObject.put("msg","验证成功");
String code = jsonObject.getString("code");
Integer level = 0;
String loginName = "";
Long organId = null;
//返回结果
AjaxResult ajax = null;
if (code.equals("0")) {
//验证成功
JSONObject dataObject = jsonObject.getJSONObject("data");
//拿到其他的信息
loginName = dataObject.getString("name");
} else {
ajax = AjaxResult.error(jsonObject.getString("msg"));
return ajax;
}
String isUserNameHas = "";
//检测一下用户名存在不存在
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(loginName))) {
isUserNameHas = "用户已存在,不需要执行添加的操作";
} else {
// //用户不存在时,将用户添加到数据库中
// SysUser sysUser = new SysUser();
// //登录名
// sysUser.setUserName(loginName);
// //昵称
// sysUser.setNickName(loginName);
// //密码统一都是123456
// sysUser.setPassword(SecurityUtils.encryptPassword("123456"));
// //创建者,标识J平台过来的用户
// sysUser.setCreateBy("j_have_token");
// //创建日期
// sysUser.setCreateTime(new Date());
// //所属等级
// sysUser.setHierarchy(level);
// //明文
// sysUser.setMingwen("123456");
//账户权限:为了区分是平台的用户还是本系统用户
// //id返回来之后需要加上
// sysUser.setDeptId(organId);
//
// //所属等级如果没有,则角色是全国的
// //1 省 2 市 3 区
// if (level == null) {
// //角色
// Long[] roleids = {104L};
// sysUser.setRoleIds(roleids);
// } else {
// Long[] roleids = {100L};
// sysUser.setRoleIds(roleids);
// }
// int rows = userService.insertUser(sysUser);
// if (rows > 0) {
// isUserNameHas = "添加成功";
// }
}
ajax = AjaxResult.success();
// 生成令牌(不加验证码登录)
String tokenNew = loginService.loginNoCode(loginName, "123456", null);
ajax.put(Constants.TOKEN, tokenNew);
ajax.put("isUserNameHas", isUserNameHas);
ajax.put("msg", "登录成功");
return ajax;
}
/**
* 通过第三方平台接口,鉴定token合法性,并返回userName等登录信息
* 这个方法需要根据实际需要进行修改
* @param token
* @return
*/
public JSONObject checkJToken(String token) {
JSONObject jsonObject = new JSONObject();
//测试环境
String baseUrl = "http://xxxxx/checkTokenRtnInfo?stk=" + token;//根据实际地址进行修改
HttpRequest request = HttpRequest.get(baseUrl)
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.header(HttpHeaders.ACCEPT, "application/json");
HttpResponse response = request.send();
return (JSONObject) JSONObject.parse(response.bodyText());
}
}
2、pom.xml添加如下依赖
<!-- https://mvnrepository.com/artifact/org.jodd/jodd-http -->
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<version>6.2.1</version>
</dependency>
3、绕过验证码登录的方法,重写loginService.loginNoCode
方法
/**
* 不加验证码登录
*
* @param username 用户名
* @param password 密码
* @param uuid 唯一标识
* @return 结果
*/
public String loginNoCode(String username, String password, String uuid)
{
// 用户验证
Authentication authentication = null;
try
{
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
}
catch (Exception e)
{
if (e instanceof BadCredentialsException)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
else
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
通过以上,第三方平台通过访问 http://localhost:81/thirdPlatlogin?token=%2713%27
就可以实现单点登录。