diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java index 81628f0..7705f7c 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -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); + } + } diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 083d2c5..4739222 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -7,7 +7,8 @@ ruoyi: # 版权年份 copyrightYear: 2024 # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /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= diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/StringUtil.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/StringUtil.java index a248420..e2a6adf 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/StringUtil.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/StringUtil.java @@ -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; + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/encrypt/EncryptUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/encrypt/EncryptUtil.java index 875b68e..2345d40 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/encrypt/EncryptUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/encrypt/EncryptUtil.java @@ -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); } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index b5f096e..53313f5 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -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() // 静态资源,可匿名访问 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SsoService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SsoService.java new file mode 100644 index 0000000..bd217bd --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SsoService.java @@ -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; + } +}