springboot实现用户统一认证、管理(单点登录)_后端

大家好,我是雄雄,欢迎关注微信公众号:雄雄的小课堂

前言

现在是:2022年5月25日13:44:16

最近和模拟登录杠上了,这不,又来了个需求,还是以这个技术点入手的。
需求大概是这样的:为了统一管理系统的用户,上游平台做了个统一认证平台,所有用户进入子系统只有一个入口,即:上游平台登录入口,新用户也是上游平台进行添加;子系统的用户登录和注册模块都屏蔽掉。

设计技能点

  1. 前端:Vue
  2. 后端:springboot (bladex框架)
  3. 数据库:mysql 5.7及以上

实现思路

  1. 上游平台通过回调接口,将用户和组织机构同步至子系统
  2. 上游平台通过​​url​​​在地址栏中挂​​sessionid​​的方式访问子系统的登录页面
  3. 子系统检地址栏中是否有​​sessionid​​​,如果有,则拿着​​sessionid​​去上游系统获取用户信息,然后在子系统中拿着用户信息自动登录
  4. 如果地址栏中没有​​sessionid​​​,则需要带着子系统的登录地址,重定向至上游平台(上游平台怎么处理的,我就不知道了,我猜测,如果用户未在上游平台登录,则不带​​sessionid​​来的子系统,如果登录了则会带着过来。所以重定向到上游平台时,应该是让用户重新进行登录的)
  5. 当用户点击退出时,清除子系统的用户登录状态的同时还需要清除上游系统,且重定向至上游平台的登录页面

代码实现

回调接口实现了两个功能:

  • 同步组织机构
  • 同步用户信息

为了后期维护方便,前后端所有调用外部的地址,从接口中获取数据等均单独提取出来了,这样也能更好的实现复用。

  1. 统一接口管理​​SsoLoginConstant​
package org.springblade.modules.system.util;

/**
* @Description: TODO
* @author: 穆雄雄
* @date: 2022/5/17 下午 2:40
* 放一些公共的常量
* @Return:
*/
public interface SsoLoginConstant {

/**
* 统一认证平台的地址
*/
public final static String SSO_URL = "http://************";


/**
* 登录鉴权
*/

public final static String CHECKLOGIN_URL =SSO_URL+ "/check_login";

/**
* 查询平台用户信息
*/
public final static String QUERYUSER_URL =SSO_URL+ "/get_user";

/**
* 查询平台组织机构信息
*/
public final static String QUERYDEPARTMENT_URL =SSO_URL+ "/get_department";

/**
* 退出系统
*/
public final static String APILOGOUT_URL =SSO_URL+ "/api_logout";


}
  1. 公用​​Service​​层接口:
package org.springblade.modules.system.service;

import org.springblade.core.tool.api.R;
import org.springframework.web.bind.annotation.RequestBody;

/**
* @author: muxiongxiong
* @date: 2022年05月21日 上午 8:41
* 公众号:雄雄的小课堂
* 博客:https://blog.***.net/qq_34137397
* 个人站:http://www.穆雄雄.com
* 个人站:http://www.muxiongxiong.cn
* @Description: 类的描述:单点登录业务层接口
*/
public interface ISsoLoginService {

/**
* @Description: 登录鉴权
* @author: 穆雄雄
* @date: 2022/5/21 上午 8:54
No such property: code for class: Script1
* @Return:
*/
String checkLogin(String ssoSessionKey);

/**
* @Description: 查询平台用户信息
* @author: 穆雄雄
* @date: 2022/5/21 上午 8:42
* 查询平台用户信息
* @Return:
*/
String getUser(String projectKey);

/**
* @Description: 查询平台组织机构信息
* @author: 穆雄雄
* @date: 2022/5/21 上午 8:50
* 查询平台用户信息
* @Return: java.lang.String
*/
String getDepartment(String projectKey);

/**
* @Description: 上传平台用户信息
* @author: 穆雄雄
* @date: 2022/5/21 上午 9:24
* @Return: java.lang.String
*/
R pullUserInfo(@RequestBody String val);

/**
* @Description: 退出
* @author: 穆雄雄
* @date: 2022年5月25日15:34:58
No such property: code for class: Script1
* @Return:
*/
String apiLogout(String ssoSessionKey);

}
  1. ​Service​​层实现类:
package org.springblade.modules.system.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.commons.lang.StringUtils;
import org.springblade.core.tool.api.R;
import org.springblade.modules.system.entity.Dept;
import org.springblade.modules.system.entity.User;
import org.springblade.modules.system.entity.UserService;
import org.springblade.modules.system.service.IDeptService;
import org.springblade.modules.system.service.ISsoLoginService;
import org.springblade.modules.system.service.IUserService;
import org.springblade.modules.system.util.SsoLoginConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
* @author: muxiongxiong
* @date: 2022年05月21日 上午 8:51
* 公众号:雄雄的小课堂
* 博客:https://blog.***.net/qq_34137397
* 个人站:http://www.穆雄雄.com
* 个人站:http://www.muxiongxiong.cn
* @Description: 类的描述
*/
@Service
public class SsoLoginServiceImpl implements ISsoLoginService {

@Autowired
private IUserService userService;

@Autowired
private IDeptService deptService;


/**
* 登录鉴权
*/
@Override
public String checkLogin(String ssoSessionKey) {
JSONObject jsonObjectResult = new JSONObject();
//请求接口地址
String url = SsoLoginConstant.CHECKLOGIN_URL;
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("ssoSessionKey", ssoSessionKey);
try {
String body = HttpUtil.createPost(url).form(paramMap).execute().body();
if (StringUtils.isBlank(body)) {
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
JSONObject obj = JSONObject.parseObject(body);
if (obj == null) {
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
String code = obj.get("code").toString();
if ("200".equals(code)) {
jsonObjectResult.put("code", 200);
jsonObjectResult.put("msg", "请求成功");
jsonObjectResult.put("data", obj.get("data"));
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}else{
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
} catch (Exception e) {
e.printStackTrace();
}
return jsonObjectResult.toJSONString();
}

/**
* 获取平台用户
*/
@Override
public String getUser(String projectKey) {
JSONObject jsonObjectResult = new JSONObject();
//请求接口地址
String url = SsoLoginConstant.QUERYUSER_URL;
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("projectKey", projectKey);
try {
String body = HttpUtil.createGet(url).form(paramMap).execute().body();
if (StringUtils.isBlank(body)) {
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
JSONObject obj = JSONObject.parseObject(body);
if (obj == null) {
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
String code = obj.get("code").toString();
if ("200".equals(code)) {
jsonObjectResult.put("code", 200);
jsonObjectResult.put("msg", "请求成功");
jsonObjectResult.put("data", obj.get("data"));
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
} catch (Exception e) {
e.printStackTrace();
}
return jsonObjectResult.toJSONString();
}

/**
* 获取组织机构
*/
@Override
public String getDepartment(String projectKey) {
JSONObject jsonObjectResult = new JSONObject();
//请求接口地址
String url = SsoLoginConstant.QUERYDEPARTMENT_URL;
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("projectKey", projectKey);
try {
String body = HttpUtil.createGet(url).form(paramMap).execute().body();
if (StringUtils.isBlank(body)) {
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
JSONObject obj = JSONObject.parseObject(body);
if (obj == null) {
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
String code = obj.get("code").toString();
if ("200".equals(code)) {
jsonObjectResult.put("code", 200);
jsonObjectResult.put("msg", "请求成功");
jsonObjectResult.put("data", obj.get("data"));
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
} catch (Exception e) {
e.printStackTrace();
}
return jsonObjectResult.toJSONString();
}

/**
* 上传平台用户信息
* @param val
* @return
*/
@Override
public R pullUserInfo(String val) {
//转换成集合类型
JSONArray userListArray = JSONArray.parseArray(val);
boolean flag = false;
for (Object o : userListArray) {
JSONObject jsonObject = (JSONObject) o;
User user = new User();
//add表示添加
//update表示更新
//delete表示删除
String operate = jsonObject.getString("operate");
//固定标识
String type = jsonObject.getString("type");
JSONObject dataObject = jsonObject.getJSONObject("data");
if (type.equals("sso_user")) {
Long id = dataObject.getLong("id");
//用户账号
String account = dataObject.getString("account");
//用户名称
String name = dataObject.getString("name");
//所属部门
String departmentId = dataObject.getString("departmentId");
//手机号
String mobile = dataObject.getString("mobile");
//用户角色,1表示管理者,2表示使用者
String isManager = dataObject.getString("isManager");
//应用编号
String project = dataObject.getString("project");

//添加用户
user.setId(id);
user.setPhone(mobile);
user.setTenantId("000000");
user.setCode("");
if(isManager.equals("1")){
//管理员
user.setRoleId("1529303109787967490");
}else if(isManager.equals("2")){
//一般用户
user.setRoleId("1529302965017370625");
}else{
//会员(这个地方不会执行到,只要isManager不等于null)
user.setRoleId("1355058724514836481");
}
user.setUserType(Integer.parseInt(isManager));
user.setAccount(account);
//密码是123456
user.setPassword("10470c3b4b1fed12c3baac014be15fac67c6e815");
user.setName(name);
user.setRealName(name);

user.setDeptId(departmentId);
user.setStatus(1);
//证明是那边过来的用户
user.setRemark(type);
switch (operate) {
case "add":
flag = userService.save(user);
break;
case "update":
flag = userService.updateUser(user);
break;
case "delete":
flag = userService.updateById(user);
break;
default:
break;
}
} else if (type.equals("sso_department")) {
Dept dept = new Dept();
Long id = dataObject.getLong("id");
//用户账号
String title = dataObject.getString("title");
//父级企业ID
String parentId = dataObject.getString("parentId");
//企业等级
String level = dataObject.getString("level");
//排序
String sort = dataObject.getString("sort");
//用户角色,1表示管理者,2表示使用者
String isManager = dataObject.getString("isManager");
//业务管路员ID
String manager = dataObject.getString("manager");
//业务管路员ID
String project = dataObject.getString("project");
dept.setId(id);
dept.setDeptName(title);
dept.setTenantId("000000");
dept.setParentId(Long.parseLong(parentId));
dept.setAncestors("0," + parentId);
dept.setDeptCategory(3);
dept.setFullName(title);
dept.setSort(Integer.parseInt(sort));
dept.setRemark(type);
dept.setIsDeleted(0);
switch (operate) {
case "add":
flag = deptService.save(dept);
break;
case "update":
flag = deptService.updateById(dept);
break;
case "delete":
flag = deptService.removeDept(id.toString());
break;
default:
break;
}
}

}
return R.status(flag);
}

/**
* 退出
* @param ssoSessionKey
* @return
*/
@Override
public String apiLogout(String ssoSessionKey) {
JSONObject jsonObjectResult = new JSONObject();
//请求接口地址
String url = SsoLoginConstant.APILOGOUT_URL;
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("ssoSessionKey", ssoSessionKey);
try {
String body = HttpUtil.createPost(url).form(paramMap).execute().body();
if (StringUtils.isBlank(body)) {
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
JSONObject obj = JSONObject.parseObject(body);
if (obj == null) {
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
String code = obj.get("code").toString();
if ("200".equals(code)) {
jsonObjectResult.put("code", 200);
jsonObjectResult.put("msg", "请求成功");
jsonObjectResult.put("data", obj.get("data"));
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}else{
jsonObjectResult.put("code", 500);
jsonObjectResult.put("msg", "请求失败");
jsonObjectResult.put("data", "");
jsonObjectResult.put("status", false);
return jsonObjectResult.toJSONString();
}
} catch (Exception e) {
e.printStackTrace();
}
return jsonObjectResult.toJSONString();
}


}

先实现功能,在做优化~

  1. 控制器中的实现方法:
package org.springblade.modules.system.controller;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springblade.common.constant.TrainingSchemeConstant;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.system.entity.User;
import org.springblade.modules.system.entity.UserService;
import org.springblade.modules.system.service.ISsoLoginService;
import org.springblade.modules.system.service.impl.KnowledgeServiceImpl;
import org.springblade.modules.system.service.impl.SsoLoginServiceImpl;
import org.springblade.modules.system.service.impl.UserServiceImpl;
import org.springblade.modules.system.util.SsoLoginConstant;
import org.springblade.modules.system.util.TokenUtil;
import org.springblade.modules.system.vo.KnowledgeVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

@RestController
@AllArgsConstructor
@RequestMapping("api/sso")
@Api(value = "", tags = "接口")
public class ApiSsoController {

private final SsoLoginServiceImpl ssoLoginService;

/**
* 用户信息
*/
private final UserServiceImpl userService;

//private final RabbitTemplate rabbitTemplate;


/**
* 统一认证平台向第三方平台的接口发起请求。
* 新增、修改、删除平台用户信息,将用户数据上传到我方
*
* @param
*/
@PostMapping(value = "/pulluserinfo")
public String pulluserinfo(@RequestBody String val) {
JSONObject object = new JSONObject();
R r = ssoLoginService.pullUserInfo(val);
if (r.getCode() == 200) {
object.put("msg", "操作成功");
object.put("success", true);
object.put("code", 200);
} else {
object.put("msg", "操作失败");
object.put("success", false);
object.put("code", 500);
}
return object.toJSONString();
}

/**
* 登录鉴权
*
*/
@GetMapping("/check_login")
public R checkLogin(String ssoSessionKey) {
//拿到接口返回值
String result = ssoLoginService.checkLogin(ssoSessionKey);
JSONObject jsonObject = JSON.parseObject(result);
Integer code = jsonObject.getInteger("code");
if (code == 200) {
//操作成功
JSONObject jsonObjectData = jsonObject.getJSONObject("data");
//拿到用户名和密码
if (jsonObjectData != null) {
//将用户名传给前台,前台拿着去登陆去
String account = jsonObjectData.getString("username");
return R.data(account);
} else {
return R.fail("未找到该用户");
}
} else {
//操作失败
return R.fail("未找到该用户");
}
}


/**
* 查询平台用户信息
*
*/
@GetMapping("/get_user")
public String getUser(String projectKey) {

return ssoLoginService.getUser(projectKey);
}

/**
* 查询平台组织机构
*
*/
@GetMapping("/get_department")
public String getDepartment(String projectKey) {
return ssoLoginService.getDepartment(projectKey);
}

/**
* 退出时调用
* 注销统一认证平台的用户信息
*/
@GetMapping("/api_logout")
public R apiLogout(String ssoSessionKey) {
//拿到接口返回值
String result = ssoLoginService.apiLogout(ssoSessionKey);
JSONObject jsonObject = JSON.parseObject(result);
Integer code = jsonObject.getInteger("code");
if (code == 200) {
return R.success("操作成功");
} else {
//操作失败
return R.fail("接口请求出错");
}
}

}

整个后台的代码基本上就这些,基本上没啥难度,就是光操作的调用接口就可以了,主要麻烦点的是在前端,明天分享一下前端的实现。