package com.ruoyi.framework.web.service;
|
|
import javax.annotation.Resource;
|
|
import com.alibaba.fastjson2.JSONObject;
|
import com.ruoyi.common.utils.HttpUtil;
|
import com.ruoyi.common.utils.RSAPublicKeyExample;
|
import com.smartor.service.impl.ServiceSLTDHealthcareRecordServiceImpl;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.ObjectUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.core.Authentication;
|
import org.springframework.stereotype.Component;
|
import com.ruoyi.common.constant.CacheConstants;
|
import com.ruoyi.common.constant.Constants;
|
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.exception.ServiceException;
|
import com.ruoyi.common.exception.user.CaptchaException;
|
import com.ruoyi.common.exception.user.CaptchaExpireException;
|
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
|
import com.ruoyi.common.utils.DateUtils;
|
import com.ruoyi.common.utils.MessageUtils;
|
import com.ruoyi.common.utils.ServletUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.ip.IpUtils;
|
import com.ruoyi.framework.manager.AsyncManager;
|
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
|
import com.ruoyi.system.service.ISysConfigService;
|
import com.ruoyi.system.service.ISysUserService;
|
|
import java.util.HashMap;
|
import java.util.Map;
|
|
/**
|
* 登录校验方法
|
*
|
* @author ruoyi
|
*/
|
@Slf4j
|
@Component
|
public class SysLoginService {
|
@Autowired
|
private TokenService tokenService;
|
|
@Resource
|
private AuthenticationManager authenticationManager;
|
|
@Autowired
|
private RedisCache redisCache;
|
|
@Autowired
|
private ISysUserService userService;
|
|
@Autowired
|
private ISysConfigService configService;
|
|
@Autowired
|
private RSAPublicKeyExample rsaPublicKeyExample;
|
|
@Value("${pri_key}")
|
private String priKey;
|
|
@Value("${isEncryp}")
|
private Integer isEncryp;
|
|
@Value("${sltd_pub_path}")
|
private String sltdPubPath;
|
|
@Value("${spring.profiles.active}")
|
private String active;
|
|
/**
|
* 登录验证
|
*
|
* @param username 用户名
|
* @param password 密码
|
* @param code 验证码
|
* @param uuid 唯一标识
|
* @return 结果
|
*/
|
public String login(String username, String password, String code, String uuid, String orgid, String campusid) {
|
boolean captchaEnabled = configService.selectCaptchaEnabled();
|
// 验证码开关
|
if (captchaEnabled) {
|
validateCaptcha(username, code, uuid);
|
}
|
// 用户验证
|
Authentication authentication = null;
|
try {
|
if (StringUtils.isEmpty(campusid)) campusid = "1";
|
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username + "&" + orgid + "&" + campusid, password);
|
AuthenticationContextHolder.setContext(authenticationToken);
|
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
authentication = authenticationManager.authenticate(authenticationToken);
|
} 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());
|
}
|
} finally {
|
AuthenticationContextHolder.clearContext();
|
}
|
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);
|
}
|
|
|
/**
|
* SSO 单点登录业务处理
|
* <p>
|
* 1. 如果是 sltd 环境,先通过 SSO token 换取员工账号
|
* 2. RSA 解密 userName(若开启加密)
|
* 3. 根据 userName + orgid + deptId + campusid 生成登录 token
|
*
|
* @param userName 用户名(可能为空,如果 sltd 模式则从 token 中获取)
|
* @param orgid 组织机构ID
|
* @param deptId 部门ID
|
* @param campusid 校区 ID
|
* @param token SLTD SSO token(仅 sltd 环境下使用)
|
* @return 登录成功后的 JWT token,失败返回 null
|
*/
|
public String ssoLogin(String userName, String orgid, String deptId, String campusid, String token) {
|
// sltd 环境:通过 SSO token 获取员工账号
|
if ("sltd".equals(active)) {
|
userName = resolveUserNameBySltdToken(token);
|
if (userName == null) {
|
return null;
|
}
|
}
|
|
log.info("【SSO登录】userName={}", userName);
|
|
if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(orgid)) {
|
log.error("【SSO登录】用户名或组织机构不能为空");
|
return null;
|
}
|
|
// RSA 解密用户名
|
if (isEncryp != null && isEncryp == 1) {
|
userName = rsaPublicKeyExample.decryptedData(userName, priKey);
|
}
|
|
if (StringUtils.isEmpty(deptId)) deptId = "null";
|
if (StringUtils.isEmpty(campusid)) campusid = "null";
|
return loginByUserName(userName + "&" + orgid + "&" + deptId + "&" + campusid);
|
}
|
|
/**
|
* 调用省立同德接口,通过 SSO token 获取员工账号
|
*
|
* @param token SLTD SSO token
|
* @return 员工账号,验证失败返回 null
|
*/
|
private String resolveUserNameBySltdToken(String token) {
|
Map<String, String> headers = new HashMap<>();
|
headers.put("app-key", ServiceSLTDHealthcareRecordServiceImpl.APP_KEY);
|
Map<String, String> requestParams = new HashMap<>();
|
requestParams.put("token", token);
|
String reqData = HttpUtil.postFormRequest(sltdPubPath + "/checkSsoTokenId", requestParams, headers, null);
|
log.info("【SLTD token 验证】响应结果:{}", reqData);
|
|
if (StringUtils.isEmpty(reqData)) {
|
log.error("【SLTD token 验证】响应为空,验证失败");
|
return null;
|
}
|
|
Map<String, Object> map = JSONObject.parseObject(reqData, Map.class);
|
if (ObjectUtils.isEmpty(map) || (Integer) map.get("code") != 200) {
|
log.error("【SLTD token 验证】响应码异常,验证失败");
|
return null;
|
}
|
|
Map<String, Object> data = (Map<String, Object>) map.get("data");
|
return (String) data.get("accountNo");
|
}
|
|
public String loginByUserName(String userName) {
|
SysUser sysUser = userService.selectUserByUserNameAndDeptId(userName);
|
log.info("---------sysUser的值为:{}", sysUser);
|
if (ObjectUtils.isNotEmpty(sysUser)) {
|
// 构建登录用户对象
|
LoginUser loginUser = new LoginUser();
|
loginUser.setUser(sysUser);
|
loginUser.setUserId(sysUser.getUserId());
|
// 创建 token
|
String token = tokenService.createToken(loginUser);
|
// 生成token
|
return token;
|
}
|
return null;
|
}
|
|
/**
|
* 校验验证码
|
*
|
* @param username 用户名
|
* @param code 验证码
|
* @param uuid 唯一标识
|
* @return 结果
|
*/
|
public void validateCaptcha(String username, String code, String uuid) {
|
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
|
String captcha = redisCache.getCacheObject(verifyKey);
|
redisCache.deleteObject(verifyKey);
|
if (captcha == null) {
|
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
|
throw new CaptchaExpireException();
|
}
|
if (!code.equalsIgnoreCase(captcha)) {
|
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
|
throw new CaptchaException();
|
}
|
}
|
|
/**
|
* 记录登录信息
|
*
|
* @param userId 用户ID
|
*/
|
public void recordLoginInfo(Long userId) {
|
SysUser sysUser = new SysUser();
|
sysUser.setUserId(userId);
|
sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
|
sysUser.setLoginDate(DateUtils.getNowDate());
|
userService.updateUserProfile(sysUser);
|
}
|
}
|