Commit 4baf314d by kenzo
parents 5d1960cb 8143c904
......@@ -53,6 +53,18 @@
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
......@@ -70,6 +82,36 @@
<version>32.0.1-android</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.36</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.69</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
</dependencies>
<build>
......
package com.cnooc.expert.auth.service;
import com.cnooc.expert.system.entity.vo.LoginVO;
import com.cnooc.expert.system.entity.vo.VerifyCodeVO;
public interface LoginService {
String login(LoginVO loginVO);
String sendPhoneCode(LoginVO vo);
String changePass(LoginVO loginVO);
String verifyCode(VerifyCodeVO codeVO);
}
package com.cnooc.expert.auth.service;
public interface SmsService {
/**
* 校验短信验证码
*
* @param phone 手机号
* @param code 验证码
* @return 是否校验通过
*/
boolean verifySmsCode(String phone, String code);
/**
* 发送短信验证码
*
* @param phone 手机号
* @return 是否发送成功
*/
boolean sendSmsCode(String phone);
boolean sendMasSmsCode(String phone);
boolean sendMasSmsContent(String phone, String content);
void asyncSendMasSmsContent(String phone, String content);
}
package com.cnooc.expert.auth.service;
import com.cnooc.expert.system.entity.vo.SysCaptchaVO;
public interface SysCaptchaService {
/**
* 生成验证码
* @return 验证码captcha vo
*/
SysCaptchaVO generate();
/**
* 验证码效验
*
* @param key key
* @param code 验证码
* @return true:成功 false:失败
*/
boolean validate(String key, String code);
/**
* 是否开启登录验证码
*
* @return true:开启 false:关闭
*/
boolean isCaptchaEnabled();
}
package com.cnooc.expert.auth.service;
public class Test {
}
package com.cnooc.expert.auth.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdcardUtil;
import com.cnooc.expert.common.constant.TokenConstants;
import com.cnooc.expert.common.exception.BusinessException;
import com.cnooc.expert.common.utils.JwtUtils;
import com.cnooc.expert.common.utils.Sm2Util;
import com.cnooc.expert.common.utils.ValidUtils;
import com.cnooc.expert.external.expert.auth.service.LoginServicesClient;
import com.cnooc.expert.system.entity.vo.LoginVO;
import com.cnooc.expert.system.entity.vo.VerifyCodeVO;
import com.cnooc.expert.system.entity.vo.ZhuanJiaInfoAppVo;
import com.cnooc.expert.system.entity.vo.ZhuanJiaInfoVo;
import com.cnooc.expert.auth.service.LoginService;
import com.cnooc.expert.auth.service.SmsService;
import com.cnooc.expert.auth.service.SysCaptchaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.time.Duration;
import java.util.UUID;
/**
* @Author: songYuHang
* @CreateTime: 2025-09-18 15:23
*/
@Service
@Slf4j
public class LoginServiceImpl implements LoginService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private SmsService smsService;
@Autowired
private SysCaptchaService sysCaptchaService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private LoginServicesClient loginServicesClient;
/** 身份证密码登录 */
private static final int ID_NUMBER_PASSWORD = 1;
/** 手机验证码登录 */
private static final int PHONE_CODE = 2;
/**
* 登录逻辑
* @param loginVO 登录
* @return Result<String> token
*/
@Override
public String login(LoginVO loginVO) {
log.info("开始执行登录逻辑");
String token;
switch (loginVO.getLoginType()) {
case ID_NUMBER_PASSWORD:
token = idNumberLogin(loginVO);
break;
case PHONE_CODE:
token = phoneCodeLogin(loginVO);
break;
default:
throw new BusinessException("请选择登录类型");
}
return token;
}
/**
* 获取验证码逻辑
* @param vo 手机号等信息
* @return Result<String> token
*/
@Override
public String sendPhoneCode(LoginVO vo) {
log.info("开始执行获取验证码逻辑");
ValidUtils.isText(vo.getPhoneNumber(), "请输入手机号");
ValidUtils.isTrue(Validator.isMobile(vo.getPhoneNumber()), "请输入正确的手机号");
//待确认,验证码下发是调用第三方平台吗
//return Result.success("验证码已发送");
boolean flag = sysCaptchaService.validate(vo.getKey(), vo.getCaptcha());
if(!flag){
return "验证码错误";
}else{
boolean smsfalg = smsService.sendSmsCode(vo.getPhoneNumber());
if(!smsfalg) {
return "短信验证码发送失败";
} else {
return "短信验证码发送成功";
}
}
}
@Override
public String changePass(LoginVO loginVO) {
log.info("开始执行修改密码逻辑");
ValidUtils.isText(loginVO.getPhoneNumber(), "请输入手机号");
ValidUtils.isText(loginVO.getPhoneCode(), "请输入验证码");
ValidUtils.isTrue(Validator.isMobile(loginVO.getPhoneNumber()), "请输入正确格式的手机号");
ValidUtils.isText(loginVO.getPassword(), "请输入密码");
//1.根据手机号去库中查询是否存在
ZhuanJiaInfoVo zhuanJiaInfoVo = new ZhuanJiaInfoVo();
zhuanJiaInfoVo.setMobile(loginVO.getPhoneNumber());
zhuanJiaInfoVo = loginServicesClient.querySingleByShengFenZhengOrMobile(zhuanJiaInfoVo);
if(zhuanJiaInfoVo == null){
return "用户信息不存在";
}
//2.判断密码是否存在
ZhuanJiaInfoAppVo zhuanJiaInfoAppVo = new ZhuanJiaInfoAppVo();
zhuanJiaInfoAppVo.setBaseGuid(zhuanJiaInfoVo.getBaseGuid());
zhuanJiaInfoAppVo = loginServicesClient.getZhuanJiaInfoAppById(zhuanJiaInfoAppVo);
String pwd = Sm2Util.decrypt(loginVO.getPassword());
if(zhuanJiaInfoAppVo == null){
//没有记录的话就是新建记录
zhuanJiaInfoAppVo = new ZhuanJiaInfoAppVo();
zhuanJiaInfoAppVo.setPassword(passwordEncoder.encode(pwd));
loginServicesClient.saveZhuanJiaInfoApp(zhuanJiaInfoAppVo);
}else{
//否则更新密码
zhuanJiaInfoAppVo.setPassword(passwordEncoder.encode(pwd));
loginServicesClient.updateZhuanJiaInfoApp(zhuanJiaInfoAppVo);
}
//手机号,验证码,图形验证码都需要判断
//判断密码是否符合规则
//进行解密处理
//调用数据库更新密码
return "ok";
}
@Override
public String verifyCode(VerifyCodeVO codeVO) {
log.info("手机验证码验证逻辑");
boolean smsfalg = smsService.verifySmsCode(codeVO.getPhoneNumber(),codeVO.getPhoneCode());
if(!smsfalg){
return "短信验证码验证失败";
}else{
return "短信验证码验证成功";
}
}
/**
* 身份证密码登录
* @param loginVO 登录参数
* @return token
*/
private String idNumberLogin(LoginVO loginVO) {
log.info("开始执行身份证登录逻辑");
ValidUtils.isText(loginVO.getIdNumber(), "请输入身份证号");
ValidUtils.isText(loginVO.getPassword(), "请输入密码");
ValidUtils.isTrue(IdcardUtil.isValidCard(loginVO.getIdNumber()), "请输入正确的身份证号");
ZhuanJiaInfoVo zhuanJiaInfoVo = new ZhuanJiaInfoVo();
zhuanJiaInfoVo.setShengfenzheng(loginVO.getIdNumber());
zhuanJiaInfoVo = loginServicesClient.querySingleByShengFenZhengOrMobile(zhuanJiaInfoVo);
if(zhuanJiaInfoVo == null){
return "用户信息不存在";
}
ZhuanJiaInfoAppVo zhuanJiaInfoAppVo = new ZhuanJiaInfoAppVo();
zhuanJiaInfoAppVo.setBaseGuid(zhuanJiaInfoVo.getBaseGuid());
zhuanJiaInfoAppVo = loginServicesClient.getZhuanJiaInfoAppById(zhuanJiaInfoAppVo);
if(zhuanJiaInfoAppVo == null){
return "该用户还没有设置密码";
}
// 验证码效验
boolean flag = sysCaptchaService.validate(loginVO.getKey(), loginVO.getCaptcha());
if (!flag) {
// 保存登录日志
//sysLogLoginService.save(login.getUsername(), Constant.FAIL, LoginOperationEnum.CAPTCHA_FAIL.getValue());
throw new BusinessException("图形验证码错误");
}
//1.需要去库中查询,是否存在
//2.存在校验密码
//解密
String pwd = Sm2Util.decrypt(loginVO.getPassword());
flag = passwordEncoder.matches(pwd, zhuanJiaInfoAppVo.getPassword());
if (!flag) {
// 登录日志
//sysLogLoginService.savePortal(login.getAccountName(), Constant.FAIL, LoginOperationEnum.ACCOUNT_FAIL.getValue(), 1);
throw new IllegalArgumentException("密码错误");
}
//3.生成相应的uuid作为redis的key
// // todo userid
String uuidKey = UUID.randomUUID().toString();
String token = JwtUtils.createToken(1,uuidKey);
tokenSetRedis(token, uuidKey);
//6.返回token
return token;
}
/**
* 手机验证码登录
* @param loginVO 手机号登录参数
* @return token
*/
private String phoneCodeLogin(LoginVO loginVO) {
log.info("开始执行手机验证码登录逻辑");
ValidUtils.isText(loginVO.getPhoneNumber(), "请输入手机号");
ValidUtils.isText(loginVO.getPhoneCode(), "请输入验证码");
ValidUtils.isTrue(Validator.isMobile(loginVO.getPhoneNumber()), "请输入正确格式的手机号");
//1.根据手机号去库中查询是否存在
ZhuanJiaInfoVo zhuanJiaInfoVo = new ZhuanJiaInfoVo();
zhuanJiaInfoVo.setMobile(loginVO.getPhoneNumber());
zhuanJiaInfoVo = loginServicesClient.querySingleByShengFenZhengOrMobile(zhuanJiaInfoVo);
if(zhuanJiaInfoVo == null){
return "用户信息不存在";
}
//2.存在校验验证码
if (!smsService.verifySmsCode(loginVO.getPhoneNumber(), loginVO.getPhoneCode())) {
//登录日志
//sysLogLoginService.savePortal(login.getPhone(), Constant.FAIL, LoginOperationEnum.CAPTCHA_FAIL.getValue(), 1);
throw new IllegalArgumentException("手机验证码错误");
}
//3.生成相应的uuid作为redis的key
// todo userid
String uuidKey = UUID.randomUUID().toString();
String token = JwtUtils.createToken(1,uuidKey);
tokenSetRedis(token, uuidKey);
return token;
}
/**
* 缓存存入token
* @param token token
* @param uuidKey uuidKey
*/
private void tokenSetRedis(String token, String uuidKey) {
String redisTokenKey = TokenConstants.TOKEN_KEY_ + uuidKey;
redisTemplate.opsForValue().set(redisTokenKey, token, Duration.ofHours(24));
}
}
package com.cnooc.expert.auth.service.impl;
import cn.hutool.core.util.RandomUtil;
import com.cnooc.expert.common.constant.TokenConstants;
import com.cnooc.expert.common.utils.SmsHttpUtil;
import com.cnooc.expert.common.utils.SmsUtil;
import com.cnooc.expert.system.entity.vo.SmsConfig;
import com.cnooc.expert.auth.service.SmsService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@Service
@AllArgsConstructor
@Slf4j
public class SmsServiceImpl implements SmsService {
public static final String SMS_CODE_CONTENT = "福建通信行业融合创新服务平台,您的验证码是:%s(有效期为2分钟),请勿泄露给他人,如非本人操作,请忽略此消息。";
@Autowired
private StringRedisTemplate redisTemplate;
private final SmsConfig smsConfig;
/**
* 校验短信验证码
*
* @param phone 手机号
* @param code 用户输入的验证码
* @return 是否校验通过
*/
@Override
public boolean verifySmsCode(String phone, String code) {
String key = "sms:code:" + phone;
String storedCode = redisTemplate.opsForValue().get(key);
if (storedCode != null && storedCode.equals(code)) {
// 验证码正确,删除缓存中的验证码
// smsCodeRedisCache.delete(key);
return true;
}
return false;
}
/**
* 发送短信验证码(可选方法)
*
* @param phone 手机号
* @return 是否发送成功
*/
public boolean sendSmsCode(String phone) {
// 生成6位验证码
//String code = RandomUtil.randomNumbers(6);
String code = "666666";
String key = "sms:code:" + phone;
//redisTemplate.opsForValue().set(key, code, 60 * 60 * 1L);// 存入 Redis,设置过期时间为5分钟
System.out.println("发送短信验证码:" + phone + " -> " + code);// 模拟发送短信,实际应调用第三方短信服务
return true;
//return sendMasSmsCode(phone);
}
@Override
public boolean sendMasSmsCode(String phone) {
// 生成验证码并构建缓存键
String code = SmsUtil.generateVerificationCode();
System.out.println("发送短信验证码:" + phone + " -> " + code);
String key = SmsUtil.buildCacheKey(phone);
try {
// 构建短信内容
String content = String.format(SMS_CODE_CONTENT, code);
System.out.println("发送短信验证码:" + phone + " -> " + code);
log.info("云MAS业务平台 发送手机号: {},短信验证码:{}", phone, code);
System.out.println("云MAS业务平台 短信内容: " + content);
boolean result = sendMasSmsContent(phone, content);
if(result){
redisTemplate.opsForValue().set(key, code, 2 * 60); // 存入 Redis,设置过期时间为2分钟
}
return result;
} catch (Exception e) {
log.error("云MAS业务平台短信验证码发送失败: {}", e.getMessage(), e);
return false;
}
}
@Override
public boolean sendMasSmsContent(String phone, String content) {
try {
log.info("云MAS业务平台 短信内容: {}", content);
log.info("云MAS业务平台 发送手机号: {},短信内容:{}", phone, content);
// 生成 MAC 签名
String mac = SmsUtil.calculateMac(smsConfig.getEcName(), smsConfig.getApId(), smsConfig.getSecretKey(),
phone, content, smsConfig.getSign(), smsConfig.getAddSerial());
System.out.println("云MAS业务平台 mac签名: " + mac);
log.info("云MAS业务平台 mac签名: {}", mac);
// 构建请求体并转换为 Base64 编码的 JSON 字符串
String encodedJson = SmsUtil.encodeRequestBodyToBase64(buildRequestBody(phone, content, mac));
System.out.println("云MAS业务平台 请求体Base64编码: " + encodedJson);
log.info("云MAS业务平台 请求体Base64编码: {}", encodedJson);
// 设置请求头并发送 POST 请求
String response = SmsHttpUtil.sendPostRequest(smsConfig.getApiUrl(), encodedJson);
System.out.println("云MAS业务平台 响应体: " + response);
log.info("云MAS业务平台 响应体response: {}", response);
// 解析响应并处理结果
return SmsUtil.handleResponse(response);
} catch (Exception e) {
log.error("云MAS业务平台短信发送失败: {}", e.getMessage(), e);
return false;
}
}
@Override
public void asyncSendMasSmsContent(String phone, String content){
CompletableFuture.runAsync(() -> {
try {
System.out.println("======开始发送短信======");
sendMasSmsContent(phone, content);
System.out.println("======发送短信结束======");
} catch (Exception e) {
log.error("asyncSendMasSmsContent异步执行失败", e);
}
}
);
}
/**
* 构建请求体
*/
private Map<String, String> buildRequestBody(String phone, String content, String mac) {
Map<String, String> requestBody = new HashMap<>();
requestBody.put("ecName", smsConfig.getEcName());
requestBody.put("apId", smsConfig.getApId());
requestBody.put("mobiles", phone);
requestBody.put("content", content);
requestBody.put("secretKey", smsConfig.getSecretKey());
requestBody.put("sign", smsConfig.getSign());
requestBody.put("addSerial", smsConfig.getAddSerial());
requestBody.put("mac", mac);
return requestBody;
}
}
package com.cnooc.expert.auth.service.impl;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.cnooc.expert.common.cache.RedisKeys;
import com.cnooc.expert.auth.service.SysCaptchaService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.cnooc.expert.system.entity.vo.SysCaptchaVO;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import java.util.concurrent.TimeUnit;
@Service
public class SysCaptchaServiceImpl implements SysCaptchaService {
private static final int EXPIRE_MINUTES = 5;
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public SysCaptchaVO generate() {
// 生成验证码key
String key = UUID.randomUUID().toString();
// 生成验证码
SpecCaptcha captcha = new SpecCaptcha(150, 40);
captcha.setLen(4);
captcha.setCharType(Captcha.TYPE_DEFAULT);
String image = captcha.toBase64();
// 保存到缓存
String redisKey = RedisKeys.getCaptchaKey(key);
//redisTemplate.opsForValue().set(redisKey, captcha.text(), EXPIRE_MINUTES, TimeUnit.MINUTES);
// 封装返回数据
SysCaptchaVO captchaVO = new SysCaptchaVO();
captchaVO.setKey(key);
captchaVO.setImage(image);
return captchaVO;
}
@Override
public boolean validate(String key, String code) {
// 如果关闭了验证码,则直接效验通过
if (!isCaptchaEnabled()) {
return true;
}
if (StrUtil.isBlank(key) || StrUtil.isBlank(code)) {
return false;
}
// 获取验证码
String captcha = getCache(key);
// 效验成功
return code.equalsIgnoreCase(captcha);
}
@Override
public boolean isCaptchaEnabled() {
return true;
}
private String getCache(String key) {
key = RedisKeys.getCaptchaKey(key);
String captcha = redisTemplate.opsForValue().get(key);
// 删除验证码
if (captcha != null) {
redisTemplate.delete(key);
}
return captcha;
}
}
package com.cnooc.expert.common.cache;
/**
* Redis Key管理
*
*/
public class RedisKeys {
private static final String CAPTCHA_KEY = "CAPTCHA_";
/**
* 验证码Key
*/
public static String getCaptchaKey(String key) {
// return "sys:captcha:" + key;
return CAPTCHA_KEY + key;
}
/**
* accessToken Key
*/
public static String getAccessTokenKey(String accessToken) {
return "sys:token:" + accessToken;
}
public static String getLogKey() {
return "sys:log";
}
}
package com.cnooc.expert.common.constant;
/**
* Token的Key常量
*
* @author FuHongZhang
*/
public class TokenConstants
{
/**
* 令牌前缀
* todo 暂时不用
*/
public static final String PREFIX = "Bearer ";
/**
* 令牌秘钥
*/
public final static String SECRET = "DjLRzlhhd/gdx6kPXtfBUSMiL8d0VzDYAtWBKpt3BuA=";
/**
* uuid_key 用于拼接缓存token的key
*/
public static final String UUID_KEY = "uuid_key";
/**
* 用户id
*/
public static final String USER_ID = "user_id";
/**
* 用于拼接缓存token的key
*/
public static final String TOKEN_KEY_ = "TOKEN:TOKEN_KEY_";
}
package com.cnooc.expert.common.crypto;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 采用国密SM3加密算法,对系统密码进行加密
*
*/
public class Sm3PasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return SmUtil.sm3(rawPassword.toString());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return StrUtil.equals(SmUtil.sm3(rawPassword.toString()), encodedPassword);
}
}
......@@ -2,21 +2,154 @@ package com.cnooc.expert.common.exception;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
/**
* 通用业务自定义异常
* 适用于参数校验、业务规则冲突、权限校验等所有业务层面的异常场景
*/
@Getter
@Slf4j
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
// ------------------- Getter 方法(仅提供获取,不允许修改) -------------------
/**
* 错误码(标准化,如:40001=参数错误,40301=权限不足,50001=系统异常)
* 建议遵循 HTTP 状态码规范,扩展业务子码(如 400 开头为客户端错误,500 开头为服务端错误)
*/
private final Integer errorCode;
/**
* 业务场景标识(如:"user_register"=用户注册,"order_pay"=订单支付,便于定位异常来源)
*/
private final String businessScene;
/**
* 异常关联数据(可选,存储导致异常的关键业务数据,如:{"userId":"10086","phone":"13800138000"})
*/
private final Map<String, Object> errorData;
// ------------------- 构造方法(按场景重载) -------------------
/**
* 基础构造器:仅包含错误信息(适用于简单场景)
* @param message 异常描述信息
*/
public BusinessException(String message) {
super(message);
this.errorCode = 500; // 默认错误码
this.businessScene = "系统错误"; // 默认未知场景
this.errorData = null;
// 记录日志
log.error("BusinessException occurred: {}", message);
}
/**
* 构造器:带错误码+错误信息(最常用,标准化异常响应)
* @param errorCode 错误码(如:"40001")
* @param message 异常描述信息
*/
public BusinessException(Integer errorCode, String message) {
super(message);
this.errorCode = errorCode;
this.businessScene = "UNKNOWN";
this.errorData = null;
// 记录日志
log.error("BusinessException occurred - Code: {}, Message: {}", errorCode, message);
}
/**
* 构造器:带错误码+错误信息+业务场景(精准定位异常来源)
* @param errorCode 错误码
* @param message 异常描述信息
* @param businessScene 业务场景(如:"user_login")
*/
public BusinessException(Integer errorCode, String message, String businessScene) {
super(message);
this.errorCode = errorCode;
this.businessScene = businessScene;
this.errorData = null;
// 记录日志
log.error("BusinessException occurred - Code: {}, Scene: {}, Message: {}",
errorCode, businessScene, message);
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMsg());
/**
* 构造器:带完整信息(错误码+信息+场景+异常数据,便于排查)
* @param errorCode 错误码
* @param message 异常描述信息
* @param businessScene 业务场景
* @param errorData 异常关联数据(如错误的参数、用户ID等)
*/
public BusinessException(Integer errorCode, String message, String businessScene, Map<String, Object> errorData) {
super(message);
this.errorCode = errorCode;
this.businessScene = businessScene;
this.errorData = errorData;
// 记录日志
log.error("BusinessException occurred - Code: {}, Scene: {}, Message: {}, Data: {}",
errorCode, businessScene, message, errorData);
}
/**
* 构造器:带根因异常(用于包装底层异常,如SQL异常、IO异常)
* @param errorCode 错误码
* @param message 异常描述信息
* @param cause 根因异常(底层原始异常)
*/
public BusinessException(Integer errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
this.businessScene = "UNKNOWN";
this.errorData = null;
// 记录日志
log.error("BusinessException occurred - Code: {}, Message: {}, Cause: {}",
errorCode, message, cause.getClass().getSimpleName());
}
public BusinessException(ErrorCode errorCode, String message) {
super(StringUtils.defaultIfBlank(message, errorCode.getMsg()));
/**
* 构造器:全参数(适配复杂场景)
* @param errorCode 错误码
* @param message 异常描述信息
* @param businessScene 业务场景
* @param errorData 异常关联数据
* @param cause 根因异常
*/
public BusinessException(Integer errorCode, String message, String businessScene, Map<String, Object> errorData, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
this.businessScene = businessScene;
this.errorData = errorData;
// 记录日志
log.error("BusinessException occurred - Code: {}, Scene: {}, Message: {}, Data: {}, Cause: {}",
errorCode, businessScene, message, errorData, cause.getClass().getSimpleName());
}
// ------------------- 增强异常信息可读性 -------------------
@Override
public String toString() {
StringBuilder sb = new StringBuilder("BusinessException: [");
sb.append("errorCode=").append(errorCode)
.append(", businessScene=").append(businessScene)
.append(", message=").append(getMessage()).append("]");
// 追加异常数据(若存在)
if (errorData != null && !errorData.isEmpty()) {
sb.append(" | errorData=").append(errorData);
}
// 追加根因异常(若存在)
if (getCause() != null) {
sb.append(" | cause=").append(getCause().getClass().getSimpleName());
}
return sb.toString();
}
}
}
\ No newline at end of file
......@@ -45,16 +45,12 @@ public class GlobalExceptionHandler {
/**
* 处理其他异常
*/
@ExceptionHandler(Exception.class)
public ApiResult<String> handleException(Exception e, HttpServletRequest request) {
log.error("系统异常: {},请求URL: {}", e.getMessage(), request.getRequestURI(), e);
return ApiResult.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), GlobalErrorCodeConstants.SYSTEM_ERROR.getMsg());
}
@ExceptionHandler(BusinessException.class)
public ApiResult<String> handleBusinessException(BusinessException e, HttpServletRequest request) {
log.error("业务异常: {},请求URL: {}", e.getMessage(), request.getRequestURI(), e);
return ApiResult.error(e.getErrorCode().getCode(), e.getMessage());
return ApiResult.error(e.getErrorCode(), e.getMessage());
}
@ExceptionHandler(NullPointerException.class)
......@@ -62,4 +58,10 @@ public class GlobalExceptionHandler {
log.error("空指针异常: {},请求URL: {}", e.getMessage(), request.getRequestURI(), e);
return ApiResult.error(GlobalErrorCodeConstants.SYSTEM_ERROR.getCode(), GlobalErrorCodeConstants.SYSTEM_ERROR.getMsg());
}
@ExceptionHandler(Exception.class)
public ApiResult<String> handleException(Exception e, HttpServletRequest request) {
log.error("系统异常: {},请求URL: {}", e.getMessage(), request.getRequestURI(), e);
return ApiResult.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), GlobalErrorCodeConstants.SYSTEM_ERROR.getMsg());
}
}
package com.cnooc.expert.common.interceptor;
import com.cnooc.expert.common.constant.TokenConstants;
import com.cnooc.expert.common.exception.BusinessException;
import com.cnooc.expert.common.exception.GlobalErrorCodeConstants;
import com.cnooc.expert.common.utils.JwtUtils;
import com.cnooc.expert.common.utils.UserUtils;
import com.cnooc.expert.common.utils.ValidUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* @author: FuHongZhang
* @date 2025-10-16 10:22
*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || token.trim().isEmpty()) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
throw new BusinessException(HttpServletResponse.SC_UNAUTHORIZED, GlobalErrorCodeConstants.LOGIN_EXPIRED.getMsg());
}
try {
Map<String, String> userMap = JwtUtils.getTokenInfo(token);
String userId = userMap.get(TokenConstants.USER_ID);
String uuidKey = userMap.get(TokenConstants.UUID_KEY);
ValidUtils.isNotNull(uuidKey, "登录异常,请重新登录");
// String cachedToken = redisTemplate.opsForValue().get(TokenConstants.TOKEN_KEY_ + userId);
//
// if (cachedToken == null || !cachedToken.equals(token)) {
// // token不存在或不匹配,说明已退出登录或token失效
// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// response.getWriter().write("{\"code\":401,\"msg\":\"请先登录\"}");
// return false;
// }
// todo 通过token解析出用户id,代用UserUtils.setUserId(userId)方法进行存储
// 所有条件都满足,放行请求
return true;
} catch (Exception e) {
log.info("token解析异常 {}",e.getMessage(),e);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
UserUtils.clear();
throw new BusinessException(401, "请先登录");
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserUtils.clear();
}
}
package com.cnooc.expert.common.utils;
import com.cnooc.expert.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
/**
* @author lyh
* 2016年10月24日 上午9:34:32
*/
@Slf4j
public class AesUtil {
private static final String KEY_TYPE = "AES";
private static final String CIPHER_TYPE = "AES/ECB/PKCS5Padding";
/**
* 加密
*
* @param content
* 要加密的内容
* @param password
* 密码
* @return 加密后的字符串
*/
public static String encrypt(String content, String password) {
String realKey = AesUtil.md5Encode(password);
SecretKeySpec sks = new SecretKeySpec(AesUtil.hex2byte(realKey),
KEY_TYPE);
byte[] cipherByte = null;
try {
Cipher c1 = Cipher.getInstance(CIPHER_TYPE);
c1.init(Cipher.ENCRYPT_MODE, sks);
cipherByte = c1.doFinal(content.getBytes("UTF-8"));
// 返回密文的十六进制形式
return byte2hex(cipherByte);
} catch (Exception e) {
log.error("e:{}", e);
throw new BusinessException("加密失败");
}
}
/**
* 解密
*
* @param content
* 要解密的内容
* @param password
* 密码
* @return 解密结果
*/
public static String decrypt(String content, String password) {
String realKey = AesUtil.md5Encode(password);
SecretKeySpec sks = new SecretKeySpec(AesUtil.hex2byte(realKey),
KEY_TYPE);
byte[] cipherByte = null;
try {
Cipher c1 = Cipher.getInstance(CIPHER_TYPE);
c1.init(Cipher.DECRYPT_MODE, sks);
cipherByte = c1.doFinal(hex2byte(content));
return new String(cipherByte);
} catch (Exception e) {
log.error("e:{}", e);
throw new BusinessException("解密失败");
}
}
/**
* 生成MD5摘要
*/
public static String md5Encode(String info) {
try {
MessageDigest alga = MessageDigest.getInstance("MD5");
alga.update(info.getBytes("UTF-8"));
return byte2hex(alga.digest());
} catch (Exception e) {
return null;
}
}
/**
* 将二进制转化为16进制字符串
*/
public static String byte2hex(byte[] b) {
StringBuilder bder = new StringBuilder();
String stmp = null;
for (int n = 0; n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0XFF);
if (stmp.length() == 1) {
bder.append("0");
}
bder.append(stmp);
}
return bder.toString().toUpperCase();
}
/**
* 十六进制字符串转化为byte数组
*/
private static byte[] hex2byte(String hex) {
if (hex.length() % 2 != 0) {
throw new IllegalArgumentException("参数长度不合法");
}
byte[] result = new byte[hex.length() / 2];
for (int i = 0; i < result.length; i++) {
result[i] = uniteBytes(hex.charAt(i * 2), hex.charAt(i * 2 + 1));
}
return result;
}
/**
* 将两个ASCII字符合成一个字节; 如:"EF"--> 0xEF
*
* @param c1
* @param c2
* @return byte
*/
private static byte uniteBytes(char c1, char c2) {
byte _b0 = Byte.decode("0x" + c1);
_b0 = (byte) (_b0 << 4);
byte _b1 = Byte.decode("0x" + c2);
return (byte) (_b0 ^ _b1);
}
}
package com.cnooc.expert.common.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
/**
* 公用的工具类
* @author zhengkai
*
* @date 2015年5月25日 上午11:34:35
*/
public class CommonUtil {
public static final Long SO_FAR_TINE = new Long(-1L);
/**
* 时间转换成字符串(采用 yyyy-MM-dd HH:mm:ss)
* @param longtime
* @return
* @author zhengkai
* @date 2015年5月9日 下午2:43:28F
*/
public static String longTimeToString(Long longtime){
if (SO_FAR_TINE.equals(longtime)) {
return "至今";
}
if(longtime!=null){
Date dt=(new Date(longtime));
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(dt);
}
return "";
}
public static String LongTimeToStringOnDay(Long longtime){
if (SO_FAR_TINE.equals(longtime)) {
return "至今";
}
if(longtime!=null){
Date dt=(new Date(longtime));
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(dt);
}
return "";
}
/**
* 时间转换成字符串
* @param longtime
* @param formatter
* @return
* @author zhengkai
* @date 2015年5月9日 下午2:43:28
*/
public static String LongTimeToString(Long longtime,String formatter){
if (SO_FAR_TINE.equals(longtime)) {
return "至今";
}
if(longtime!=null){
Date dt=(new Date(longtime));
SimpleDateFormat sdf=new SimpleDateFormat(formatter);
return sdf.format(dt);
}
return "";
}
/**
* 字符串值是否相同
* <p>
* 注意:只比较视觉值 即null = '' = ' '
* </p>
*
* @param str1
* @param str2
* @return
*/
public static boolean stringValSame(String str1, String str2){
str1 = Objects.toString(str1, "").trim();
str2 = Objects.toString(str2, "").trim();
return str1.equals(str2);
}
/**
* 二开 - Boolean类型转成字符串展示
*
* @param val
* @return true->是;false->否
*/
public static String convertBool2Str(Boolean val){
if (Objects.isNull(val)) {
return "";
}
return Boolean.TRUE.equals(val) ? "是" : "否";
}
}
package com.cnooc.expert.common.utils;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.util.ArrayList;
import java.util.List;
/**
* JSON 工具类
*
*/
public class JsonUtils {
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.registerModule(new JavaTimeModule());
// objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
public static String toJsonString(Object object) {
try {
return objectMapper.writeValueAsString(object);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, Class<T> clazz) {
if (StrUtil.isEmpty(text)) {
return null;
}
try {
return objectMapper.readValue(text, clazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
if (ArrayUtil.isEmpty(bytes)) {
return null;
}
try {
return objectMapper.readValue(bytes, clazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
try {
return objectMapper.readValue(text, typeReference);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> List<T> parseArray(String text, Class<T> clazz) {
if (StrUtil.isEmpty(text)) {
return new ArrayList<>();
}
try {
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
package com.cnooc.expert.common.utils;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import com.cnooc.expert.common.constant.TokenConstants;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具类,用于生成、解析和验证token
* @author FuHongZhang
*/
@Slf4j
public class JwtUtils {
// 密钥,实际项目中应放在配置文件中
private static final String SECRET_KEY = TokenConstants.SECRET;
// Token过期时间,这里设置为48小时
private static final long EXPIRATION_TIME = 48 * 60 * 60 * 1000;
// private static final long EXPIRATION_TIME = 3 * 1000;
/**
* 生成token
* @return token字符串
*/
public static String createToken(Integer userId , String uuidKey) {
// 生成token
return Jwts.builder()
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.setIssuedAt(new Date())
.claim(TokenConstants.UUID_KEY, uuidKey)
.claim(TokenConstants.USER_ID, userId)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
/**
* 从token中获取用于拼接缓存的key uuid_key 和用户 user_id
*/
public static Map<String, String> getTokenInfo(String token) {
// 如果过期,会抛Exception
Map<String, String> userInfo = new HashMap<>();
Claims claims = extractAllClaims(token);
userInfo.put(TokenConstants.UUID_KEY, (String) claims.get(TokenConstants.UUID_KEY));
userInfo.put(TokenConstants.USER_ID, claims.get(TokenConstants.USER_ID) + "");
return userInfo;
}
/**
*
* @param token
* @return
*/
private static Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
public static void main(String[] args) throws Exception{
String portalToken = JwtUtils.createToken(111, "testtest");
Thread.sleep(5000);
Map<String, String> claims = JwtUtils.getTokenInfo(portalToken);
System.out.print(JSON.toJSONString(claims, true));
}
}
package com.cnooc.expert.common.utils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
public class RestUtils {
private static final RestTemplate restTemplate = new RestTemplate();
/**
* 发送 Base64 编码的 JSON POST 请求
*
* @param apiUrl API 地址
* @param requestBody 请求体 Map
* @return 响应字符串
*/
public static <T> T sendPostRequest(String apiUrl, Map<String, String> headersMap,
String requestBody, ParameterizedTypeReference<T> responseType) {
try {
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
if(!CollectionUtils.isEmpty(headersMap)) {
headersMap.forEach(headers::set);
}
// 构建请求实体
HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
ResponseEntity<T> response = restTemplate.exchange(apiUrl, HttpMethod.POST, requestEntity, responseType);
return response.getBody();
} catch (Exception e) {
throw new RuntimeException("发送 POST 请求失败", e);
}
}
public static <T> T sendPostRequest2Expert(String relativeUrl, String requestBody, ParameterizedTypeReference<T> responseType){
String fullUrl = "http://10.xxx.xxx.xxx"+relativeUrl;
Map<String, String> headers = new HashMap<>();
headers.put("Access-Key", "Ysy9ht");
return sendPostRequest(fullUrl, headers, requestBody, responseType);
}
}
package com.cnooc.expert.common.utils;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.util.encoders.Hex;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* 国密SM2加密算法
*
*/
public class Sm2Util {
/**
* 公钥
*/
private final static String PUBLIC_KEY = "3059301306072a8648ce3d020106082a811ccf5501822d034200040a302b5e4b961afb3908a4ae191266ac5866be100fc52e3b8dba9707c8620e64ae790ceffc3bfbf262dc098d293dd3e303356cb91b54861c767997799d2f0060";
/**
* 私钥
*/
private final static String PRIVATE_KEY = "308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420d7840173df3d6cd72cad4040dfc7dbfcde539f5b490b54f3cd5c4125544b38aea00a06082a811ccf5501822da144034200040a302b5e4b961afb3908a4ae191266ac5866be100fc52e3b8dba9707c8620e64ae790ceffc3bfbf262dc098d293dd3e303356cb91b54861c767997799d2f0060";
private final static SM2 sm2;
static {
sm2 = SmUtil.sm2(PRIVATE_KEY, PUBLIC_KEY);
}
/**
* 加密
*
* @param data 明文
* @return 加密后的密文
*/
public static String encrypt(String data) {
return sm2.encryptBase64(data, KeyType.PublicKey);
}
/**
* 解密
*
* @param data 加密后的密文
* @return 解密后的明文
*/
public static String decrypt(String data) {
return sm2.decryptStr(data, KeyType.PrivateKey);
}
public static void main(String[] args) {
KeyPair keyPair = SecureUtil.generateKeyPair("SM2");
System.out.println("privateKey:" + HexUtil.encodeHexStr(keyPair.getPrivate().getEncoded()));
System.out.println("publicKey:" + HexUtil.encodeHexStr(keyPair.getPublic().getEncoded()));
PublicKey publicKey = keyPair.getPublic();
if (publicKey instanceof BCECPublicKey) {
// 获取65字节非压缩缩的十六进制公钥串(0x04)
String publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
System.out.println("SM2公钥:" + publicKeyHex);
}
PrivateKey privateKey = keyPair.getPrivate();
if (privateKey instanceof BCECPrivateKey) {
// 获取32字节十六进制私钥串
String privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
System.out.println("SM2私钥:" + privateKeyHex);
}
String password = "admin";
String sm2Password = Sm2Util.encrypt(password);
System.out.println("sm2 加密:" + sm2Password);
System.out.println("sm2 解密:" + Sm2Util.decrypt(sm2Password));
System.out.println("sm3 解密:" + SmUtil.sm3("admin"));
}
}
package com.cnooc.expert.common.utils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;
public class SmsHttpUtil {
private static final RestTemplate restTemplate = new RestTemplate();
/**
* 发送 Base64 编码的 JSON POST 请求
*
* @param apiUrl API 地址
* @param requestBody 请求体 Map
* @return 响应字符串
*/
public static String sendPostRequest(String apiUrl, String requestBody) {
try {
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 构建请求实体
HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
return restTemplate.postForObject(apiUrl, requestEntity, String.class);
} catch (Exception e) {
throw new RuntimeException("发送 POST 请求失败", e);
}
}
}
package com.cnooc.expert.common.utils;
import cn.hutool.core.util.RandomUtil;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Map;
@Slf4j
public class SmsUtil {
/**
* 生成6位随机验证码
*/
public static String generateVerificationCode() {
return RandomUtil.randomNumbers(6);
}
/**
* 构建 Redis 缓存键
*/
public static String buildCacheKey(String phone) {
return "sms:code:" + phone;
}
/**
* 构建短信内容
*/
public static String buildSmsContent(String code) {
return "福建通信行业融合创新服务平台,您的验证码是:" + code + "(有效期为2分钟),请勿泄露给他人,如非本人操作,请忽略此消息。";
}
/**
* 将请求体转换为 Base64 编码的 JSON 字符串
*/
public static String encodeRequestBodyToBase64(Map<String, String> requestBody) {
String jsonString = JsonUtils.toJsonString(requestBody);
return Base64.getEncoder().encodeToString(jsonString.getBytes(StandardCharsets.UTF_8));
}
/**
* 处理响应结果
*/
public static boolean handleResponse(String response) throws Exception {
JsonNode jsonNode = JsonUtils.parseObject(response, JsonNode.class);
if (jsonNode.get("success").asBoolean()) {
System.out.println("云MAS业务平台短信验证码发送成功");
log.info("云MAS业务平台短信验证码发送成功");
return true;
} else {
System.out.println("云MAS业务平台短信验证码发送失败");
log.info("云MAS业务平台短信验证码发送失败");
return false;
}
}
public static String calculateMac(String ecName, String apld, String secretKey, String mobiles, String content, String sign, String addSerial) {
try{
// 根据文档中的规则计算MAC
StringBuilder sb = new StringBuilder();
sb.append(ecName).append(apld).append(secretKey).append(mobiles).append(content).append(sign).append(addSerial);
return DigestUtils.md5Hex(sb.toString());
} catch (Exception e) {
throw new RuntimeException("生成MAC签名失败", e);
}
}
public static String generateMD5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (Exception e) {
throw new RuntimeException("生成MAC签名失败", e);
}
}
}
package com.cnooc.expert.common.utils;
import com.cnooc.expert.system.entity.pojo.ZhuanJiaUser;
import org.springframework.stereotype.Component;
/**
* 用户id获取工具类
* @author: FuHongZhang
* @date 2025-10-24 11:14:07
*/
@Component("userUtils")
public class UserUtils {
public static ZhuanJiaUser getCurrentUser() {
ZhuanJiaUser zhuanJiaUser = USER_INFO.get();
if (zhuanJiaUser != null) {
return zhuanJiaUser;
}
return null;
}
private static final ThreadLocal<ZhuanJiaUser> USER_INFO = new ThreadLocal<>();
public static void setUserId(ZhuanJiaUser zhuanJiaUser) {
USER_INFO.set(zhuanJiaUser);
}
public static ZhuanJiaUser getUserId() {
return USER_INFO.get();
}
public static void clear() {
USER_INFO.remove();
}
}
\ No newline at end of file
package com.cnooc.expert.common.utils;
import com.cnooc.expert.common.exception.BusinessException;
import com.cnooc.expert.common.exception.ErrorCode;
import com.cnooc.expert.common.exception.GlobalErrorCodeConstants;
import java.util.Collection;
/**
* @Author: FuHongZhang
* @CreateTime: 2025-09-18 16:11
*/
public class ValidUtils {
/**
* 校验字符串是否为空
* @param text 待校验的字符串
* @param message 异常信息
*/
public static void isText(String text, String message) {
if (text == null || text.trim().isEmpty()) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
/**
* 校验对象是否为null
* @param obj 待校验的对象
* @param message 异常信息
*/
public static void isNotNull(Object obj, String message) {
if (obj == null) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
/**
* 自定义错误码及错误信息
* @param obj
* @param errorCode
*/
public static void isNotNull(Object obj, ErrorCode errorCode) {
if (obj == null) {
throw new BusinessException(errorCode.getCode(), errorCode.getMsg());
}
}
/**
* 校验集合是否为空
* @param collection 待校验的集合
* @param message 异常信息
*/
public static void isNullList(Collection<?> collection, String message) {
if (collection == null || collection.isEmpty()) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
/**
* 校验表达式是否为true
* @param expression 待校验的布尔表达式
* @param message 异常信息
*/
public static void isTrue(boolean expression, String message) {
if (!expression) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
/**
* 校验表达式是否为true
* @param expression
* @param errorCode
*/
public static void isTrue(boolean expression, ErrorCode errorCode) {
if (!expression) {
throw new BusinessException(errorCode.getCode(), errorCode.getMsg());
}
}
/**
* 校验表达式是否为false
* @param expression 待校验的布尔表达式
* @param message 异常信息
*/
public static void isFalse(boolean expression, String message) {
if (expression) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
}
package com.cnooc.expert.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.cnooc.expert.common.crypto.Sm3PasswordEncoder;
/**
* 加密配置
*
*/
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
// 使用国密SM3加密
return new Sm3PasswordEncoder();
}
}
package com.cnooc.expert.config;
import com.cnooc.expert.common.interceptor.LoginInterceptor;
import com.cnooc.expert.common.interceptor.WebAuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
......@@ -9,8 +11,22 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class TheWebMvcConfigurer implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册登录拦截器,并设置拦截路径和排除路径
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns( // 排除一些路径
"/sys/**",
"/verify/**",
"/person/**",
"/text/**"
);
registry.addInterceptor(webAuthInterceptor());
}
......
package com.cnooc.expert.external.expert.api;
import com.cnooc.expert.external.expert.model.request.ExpertInfoGetReq;
import com.cnooc.expert.external.expert.model.response.ExpertInfoGetResp;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.HeaderMap;
import retrofit2.http.POST;
import java.util.Map;
public interface LoginServiceApi {
@POST("/")
Call<ExpertInfoGetResp> expertDetailUpDate(@HeaderMap Map<String, Object> headers, @Body ExpertInfoGetReq user);
}
package com.cnooc.expert.external.expert.auth.service;
import com.cnooc.expert.external.common.AbstractRetrofitManager;
import com.cnooc.expert.system.entity.vo.ZhuanJiaInfoVo;
import com.cnooc.expert.system.entity.vo.ZhuanJiaInfoAppVo;
import org.springframework.stereotype.Service;
@Service
public class LoginServicesClient extends AbstractRetrofitManager {
public ZhuanJiaInfoVo querySingleByShengFenZhengOrMobile(ZhuanJiaInfoVo zhuanJiaInfoVo){
ZhuanJiaInfoVo res = new ZhuanJiaInfoVo();
/* String jsonString = JsonUtils.toJsonString(zhuanJiaInfoVo);
res = RestUtils.sendPostRequest2Expert("/zjfw/zggrxxgl/querySingleByShengFenZhengOrMobile",jsonString,null);
*/ return res;
}
public ZhuanJiaInfoAppVo getZhuanJiaInfoAppById(ZhuanJiaInfoAppVo zhuanJiaInfoAppVo){
ZhuanJiaInfoAppVo res = new ZhuanJiaInfoAppVo();
/* String jsonString = JsonUtils.toJsonString(zhuanJiaInfoAppVo);
res = RestUtils.sendPostRequest2Expert("/zjfw/zggrxxgl/getZhuanJiaInfoAppById",jsonString,null);*/
return res;
}
public ZhuanJiaInfoAppVo saveZhuanJiaInfoApp(ZhuanJiaInfoAppVo zhuanJiaInfoAppVo){
ZhuanJiaInfoAppVo res = new ZhuanJiaInfoAppVo();
/* String jsonString = JsonUtils.toJsonString(zhuanJiaInfoAppVo);
res = RestUtils.sendPostRequest2Expert("/zjfw/zggrxxgl/saveZhuanJiaInfoApp",jsonString,null);*/
return res;
}
public ZhuanJiaInfoAppVo updateZhuanJiaInfoApp(ZhuanJiaInfoAppVo zhuanJiaInfoAppVo){
ZhuanJiaInfoAppVo res = new ZhuanJiaInfoAppVo();
/* String jsonString = JsonUtils.toJsonString(zhuanJiaInfoAppVo);
res = RestUtils.sendPostRequest2Expert("/zjfw/zggrxxgl/updateZhuanJiaInfoApp",jsonString,null);*/
return res;
}
}
package com.cnooc.expert.external.expert.auth.service;
public class Test {
}
package com.cnooc.expert.system.entity.pojo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: FuHongZhang
* @date 2025-10-30 18:00
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ZhuanJiaUser {
private static final long serialVersionUID = -1L;
private String zhuanJiaGuid;
private String zhuanJiaShiXiangGuid;
private String zhuanJiaName;
private String zhuanJiaCode;
private String shenFenZheng;
private String mobile;
private Short zhuanJiaZhuangTai;
private Boolean shiFouYiFaZhuanJia;
private String suoShuBuMeng;//所在部门
}
package com.cnooc.expert.system.entity.vo;
import lombok.*;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
/**
* 接收登录的form表单数据
* @Author: FuHongZhang
* @CreateTime: 2025-09-18 15:11
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LoginVO {
private String phoneNumber;
private String phoneCode;
private String idNumber;
private String password;
@Max(value = 2, message = "登录类型只能是1,手机验证码登录 2.身份证密码登录")
@Min(value = 1)
private Integer loginType;
//图形验证码的key
private String key;
//图片验证码
private String captcha;
}
package com.cnooc.expert.system.entity.vo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "sms.config")
public class SmsConfig {
private String apiUrl;
private String ecName;
private String apId;
private String secretKey;
private String sign;
private String addSerial;
}
package com.cnooc.expert.system.entity.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class SysCaptchaVO implements Serializable {
private static final long serialVersionUID = 1L;
private String key;
//image base64
private String image;
}
\ No newline at end of file
package com.cnooc.expert.system.entity.vo;
import lombok.Data;
import java.io.Serializable;
/**
* @author: FuHongZhang
* @date 2025-10-22 17:03
*/
@Data
public class VerifyCodeVO implements Serializable {
private String phoneNumber;
private String phoneCode;
}
package com.cnooc.expert.system.entity.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class ZhuanJiaInfoAppVo implements Serializable {
private static final long serialVersionUID = 1L;
private String baseGuid;
private String password;
}
package com.cnooc.expert.system.entity.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class ZhuanJiaInfoVo implements Serializable {
private String baseGuid;
private String mobile;
private String shengfenzheng;
private String password;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment