添加单点登录相关

This commit is contained in:
chenyanchang
2026-05-20 16:10:46 +08:00
parent 3bfd80bee9
commit 131f1461ce
6 changed files with 342 additions and 7 deletions

View File

@@ -3,13 +3,13 @@ package com.ruoyi.web.controller.system;
import java.util.List;
import java.util.Set;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.cms.domain.IDCardInfo;
import com.ruoyi.common.core.domain.entity.tymh.wwToken.WwTokenResult;
import com.ruoyi.common.core.domain.entity.tymh.wwToken.WwUserLogin;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.OauthLoginHlwService;
import com.ruoyi.framework.web.service.OauthLoginService;
import com.ruoyi.framework.web.service.*;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -19,8 +19,6 @@ import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.system.service.ISysMenuService;
@@ -44,7 +42,8 @@ public class SysLoginController
private OauthLoginService oauthLoginService;
@Autowired
private OauthLoginHlwService oauthLoginHlwService;
@Autowired
private SsoService ssoService;
/**
* 登录方法
*
@@ -301,4 +300,11 @@ public class SysLoginController
return loginService.companyLoginOrRegister(loginBody);
}
@ApiOperation("单点登录")
@PostMapping("/sso/login")
public AjaxResult ssoCheck(@RequestBody JSONObject param) {
JSONObject result = ssoService.ssoCheck(param);
return AjaxResult.success(result);
}
}

View File

@@ -7,7 +7,8 @@ ruoyi:
# 版权年份
copyrightYear: 2024
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
profile: /home/ruoyi/uploadPath
# profile: /home/ruoyi/uploadPath
profile: /Users/chenyanchang/logs/ruoyi/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
@@ -192,5 +193,15 @@ oauth:
connect-timeout: 10
read-timeout: 30
write-timeout: 30
#浪潮单点登录相关
lc_web_auth:
appId: cloud-out-2fb6330e9c0843e1a1424efda5d604c0
appSecret: x14lueHbtLQL7Pz2G7gE4wcGCV6TDblO5xfeu9V2wGk=
getTokenUrl: http://218.31.252.15:9081/prod-psout-api/auth/token
getUserInfoUrl: http://218.31.252.15:9081/prod-psout-api/system/app/authorize/user/info
lc_cms_auth:
appId: cloud-9793ee8a8c3d47b8871007ffc4128502
appSecret: Yi+NACK70UPg8rFvsnnfBUq1wcLD4nm6ilC4II/4C4k=

View File

@@ -1,8 +1,12 @@
package com.ruoyi.cms.util;
import com.ruoyi.common.utils.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -229,4 +233,94 @@ public class StringUtil {
}
return str.toUpperCase();
}
/**
* 通过身份证获取年龄
*
* @param idNumber
* @return
*/
public static String getAgeByIdNumber(String idNumber) {
if (idNumber == null || idNumber.length() != 18) {
return null;
}
//出生日期yyyyMMdd
String birthDateStr = idNumber.substring(6, 14);
LocalDate birthDate = LocalDate.parse(birthDateStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
// 年龄
return String.valueOf(Period.between(birthDate, LocalDate.now()).getYears());
}
/**
* 转换学历至本地学历
*
* @param val
* @return
*/
public static String convertEducation(String val) {
//模型码值
//初中及以下 0 0 小学
//中专/中技 1 1 初中
//高中 2 2 高中
//大专 3 3 中专
//本科 4 4 大专
//硕士 5 5 本科
//博士 6 6 硕士
//MBA/EMBA 7 7 博士
//留学学士 8
//留学硕士 9
//留学博士 10
String result = null;
if (val == null) {
return null;
}
if ("0".equals(val) || "1".equals(val)) {//小学,初中-->初中及以下
result = "0";
} else if ("2".equals(val)) {
result = "2";
} else if ("3".equals(val)) {
result = "1";
} else if ("4".equals(val)) {
result = "3";
} else if ("5".equals(val)) {
result = "4";
} else if ("6".equals(val)) {
result = "5";
} else if ("7".equals(val)) {
result = "6";
}
return result;
}
/**
* 工作经验转模型经验
* @param personYearsWorking
* @return
*/
public static String convertExp(Integer personYearsWorking) {
// 实习生 1
// 应届毕业生 2
// 1年以下 3
// 1-3年 4
// 3-5年 5
// 5-10年 6
// 10年以上 7
// 经验不限 0
String modelExp = null;
if (ObjectUtils.isEmpty(personYearsWorking)) {
return null;
}
if (personYearsWorking <= 1) {
modelExp = "3";
} else if (personYearsWorking <= 3) {
modelExp = "4";
} else if (personYearsWorking <= 5) {
modelExp = "5";
} else if (personYearsWorking <= 10) {
modelExp = "6";
} else {
modelExp = "7";
}
return modelExp;
}
}

View File

@@ -89,6 +89,15 @@ public class EncryptUtil {
}
}
public static String decryptByAppIdAndSecret(String content, String appId, String appSecret) {
if(StringUtils.isEmpty(content)) {
return "";
}
String generatedIv = generateAppIV(appId);
String generatedKey = generateAppKey(appId, appSecret);
return decrypt(content, generatedIv, generatedKey);
}
private static String getIv(String iv) {
return generateIV(iv);
}

View File

@@ -111,7 +111,7 @@ public class SecurityConfig
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.antMatchers("/login","/loginoss", "/register", "/captchaImage","/app/login","/websocket/**","/ws/**","/speech-recognition","/speech-synthesis",
requests.antMatchers("/sso/login","/login","/loginoss", "/register", "/captchaImage","/app/login","/websocket/**","/ws/**","/speech-recognition","/speech-synthesis",
"/cms/company/listPage","/cms/appUser/noTmlist","/getTjmhToken","/getWwTjmhToken","/getWwTjmHlwToken",
"/cms/notice/noticTotal","/cms/jobApply/zphApply","/cms/jobApply/zphApplyAgree").permitAll()
// 静态资源,可匿名访问

View File

@@ -0,0 +1,215 @@
package com.ruoyi.framework.web.service;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.cms.service.impl.AppUserServiceImpl;
import com.ruoyi.cms.util.StringUtil;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.encrypt.EncryptUtil;
import com.ruoyi.common.utils.ip.IpUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* @Author: chenyanchang
* @Date: 2026/5/19 下午6:03
*/
@Service
public class SsoService {
@Autowired
RedisCache redisCache;
@Autowired
AppUserServiceImpl appUserService;
@Autowired
TokenService tokenService;
@Value("${lc_web_auth.appId}")
String webAppId;
@Value("${lc_web_auth.appSecret}")
String webAppSecret;
@Value("${lc_web_auth.getTokenUrl}")
String WEB_GET_TOKEN_URL;
@Value("${lc_web_auth.getUserInfoUrl}")
String WEB_GET_USER_INFO;
final String APP_USER_TOKEN_KEY = "app:user:token:";
public JSONObject ssoCheck(JSONObject param) {
if (ObjectUtils.isEmpty(param)) {
throw new RuntimeException("请求参数不能为空");
}
String code = param.getString("code");
//String userType = param.getString("userType");
//通过code获取token
JSONObject json = new JSONObject();
json.put("code", code);
String lcToken = getToken(WEB_GET_TOKEN_URL, null, json.toJSONString());
if (StringUtils.isEmpty(lcToken)) {
throw new RuntimeException("获取token失败");
}
//获取用户信息
JSONObject pJson = new JSONObject();
pJson.put("appId", webAppId);
pJson.put("appSecret", webAppSecret);
JSONObject userJson = getUserInfo(WEB_GET_USER_INFO, lcToken, pJson.toJSONString());
if (ObjectUtils.isEmpty(userJson)) {
throw new RuntimeException("获取用户信息失败");
}
//获取身份证号
String personCardNo = null;
JSONObject info = null;
if (userJson.containsKey("info")) {
info = userJson.getJSONObject("info");
if (ObjectUtils.isNotEmpty(info) && info.containsKey("personCardNo")) {
personCardNo = info.getString("personCardNo");
//解密处理
if (StringUtils.isEmpty(personCardNo)) {
throw new RuntimeException("获取用户证件信息失败");
}
personCardNo = EncryptUtil.decryptByAppIdAndSecret(personCardNo, webAppId, webAppSecret);
}
}
//用身份证号查询用户
AppUser appUser = appUserService.selectAppuserByIdcard(personCardNo);
if (appUser == null) {
//用户不存在,则先保存用户
saveAppUser(userJson);
}
//用户存在生成本系统用户的token
LoginUser loginUser = new LoginUser();
SysUser user = new SysUser();
user.setUserName(info.getString("userName"));
loginUser.setUser(user);
String token = tokenService.createToken(loginUser);
//缓存token
String userKey = APP_USER_TOKEN_KEY + userJson.getString("userId");
redisCache.setCacheObject(userKey, token, 2, TimeUnit.HOURS);
JSONObject backJson = new JSONObject();
backJson.put("token", token);
backJson.put("lcToken", lcToken);
return backJson;
}
//1.获取token
private String getToken(String url, String token, String params) {
try {
String result = sendHttpPost(url, token, params);
if (StringUtils.isEmpty(result)) {
throw new RuntimeException("获取token失败");
}
JSONObject json = JSONObject.parseObject(result);
if (json.getInteger("code") == 200) {
return json.getString("token");
} else if (json.getInteger("code") == 401) {
throw new RuntimeException("认证过期");
} else {
throw new RuntimeException("获取token失败");
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
//2.获取用户信息
private JSONObject getUserInfo(String url, String token, String params) {
try {
String result = sendHttpPost(url, token, params);
if (StringUtils.isEmpty(result)) {
throw new RuntimeException("获取用户信息失败");
}
JSONObject json = JSONObject.parseObject(result);
if (json.getInteger("code") == 200) {
return json.getJSONObject("sysUser");
} else if (json.getInteger("code") == 401) {
throw new RuntimeException("认证过期");
} else {
throw new RuntimeException("获取用户信息失败");
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
//保存用户
private void saveAppUser(JSONObject userJson) {
JSONObject info = userJson.getJSONObject("info");
AppUser appUser = new AppUser();
//app角色0企业1求职者2网格员 3内部政府人员 4其他浪潮用
appUser.setIsCompanyUser("1");
appUser.setUserId(userJson.getLong("userId"));
appUser.setName(info.getString("personName"));
appUser.setSex(info.getString("personSex"));
appUser.setBirthDate(info.getString("personBirthday"));
appUser.setEducation(StringUtil.convertEducation(info.getString("personEducation")));
appUser.setPoliticalAffiliation(info.getString("personPolitical"));
appUser.setAddress(info.getString("liveAddress"));
appUser.setWorkExperience(StringUtil.convertExp(info.getInteger("personYearsWorking")));
appUser.setNation(info.getString("personNation"));
appUser.setDomicileAddress(info.getString("householdAddress"));
String date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
appUser.setCreateTime(date);
appUser.setUpdateTime(date);
appUser.setLoginDate(new Date());
appUser.setCreateBy("system");
appUser.setLoginIp(IpUtils.getIpAddr());
//获取身份证,再获取年龄
String personCardNo = info.getString("personCardNo");
//解密处理
if (StringUtils.isNotEmpty(personCardNo)) {
personCardNo = EncryptUtil.decryptByAppIdAndSecret(personCardNo, webAppId, webAppSecret);
appUser.setAge(StringUtil.getAgeByIdNumber(personCardNo));
appUser.setIdCard(personCardNo);
}
String phone = info.getString("personPhone");
//解密电话号码
if (StringUtils.isNotEmpty(phone)) {
phone = EncryptUtil.decryptByAppIdAndSecret(phone, webAppId, webAppSecret);
appUser.setPhone(phone);
}
appUserService.insertAppUser(appUser);
}
//发送请求
private String sendHttpPost(String url, String token, String params) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(url);
if (StringUtils.isNotEmpty(token)) {
httpPost.setHeader("Authorization", "Bearer " + token);
}
httpPost.setEntity(new StringEntity(params, "UTF-8"));
httpPost.setHeader("Content-Type", "application/json");
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
return responseBody;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}