Commit d8591f38 by kenzo

add login

parents b92a3e31 3bc7fe88
package com.cnooc.expert.auth.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.time.format.DateTimeFormatter;
@Service
public class AccountLockService {
@Autowired
private StringRedisTemplate redisTemplate;
// 锁定策略:3次错误 -> 10分钟,4次错误 -> 30分钟,5次及以上错误 -> 1小时
private static final int[] LOCK_DURATIONS = {10, 30, 60}; // 分钟
private static final int MAX_ATTEMPTS = 3;
private String getLockKey(String account) {
return "login:lock:" + account;
}
private String getAttemptKey(String account) {
return "login:attempt:" + account;
}
private int calculateLockMinutes(long failureCount) {
if (failureCount < MAX_ATTEMPTS) {
return 0; // 不锁定
} else if (failureCount == MAX_ATTEMPTS) {
return LOCK_DURATIONS[0]; // 10分钟
} else if (failureCount == MAX_ATTEMPTS + 1) {
return LOCK_DURATIONS[1]; // 30分钟
} else {
return LOCK_DURATIONS[2]; // 60分钟
}
}
private void lockAccount(String account, int lockMinutes) {
String lockKey = getLockKey(account);
String lockValue = LocalDateTime.now().toString() + "|" + lockMinutes;
redisTemplate.opsForValue().set(lockKey, lockValue, lockMinutes*60, TimeUnit.SECONDS);//这个超时时间需要考虑一下
}
/**
* 手动解锁账号
*/
public void unlockAccount(String account) {
String attemptKey = getAttemptKey(account);
String lockKey = getLockKey(account);
redisTemplate.delete(attemptKey);
redisTemplate.delete(lockKey);
}
/**
* 处理登录成功(重置失败计数和锁定)
*/
public void handleLoginSuccess(String account) {
String attemptKey = getAttemptKey(account);
String lockKey = getLockKey(account);
redisTemplate.delete(attemptKey);
redisTemplate.delete(lockKey);
}
/**
* 处理登录失败
*/
public void handleLoginFailure(String account) {
String attemptKey = getAttemptKey(account);
String lockKey = getLockKey(account);
// 如果已经锁定,不再增加计数
if (Boolean.TRUE.equals(redisTemplate.hasKey(lockKey))) {
return;
}
// 增加失败计数
//long failureCount = jedis.incr(attemptKey);
String failureCountStr = redisTemplate.opsForValue().get(attemptKey)!=null?redisTemplate.opsForValue().get(attemptKey).toString():"0";
long failureCount = Long.parseLong(failureCountStr)+1;
LocalDateTime now = LocalDateTime.now();
LocalDateTime midnight = now.toLocalDate().atTime(LocalTime.MAX);
redisTemplate.opsForValue().set(attemptKey,String.valueOf(failureCount),ChronoUnit.SECONDS.between(now, midnight), TimeUnit.SECONDS);
// 设置计数过期时间(避免无限增长)
if (failureCount == 1) {
redisTemplate.expire(attemptKey, 24 * 60 * 60, TimeUnit.SECONDS); // 24小时过期
}
// 根据失败次数确定锁定时间
int lockMinutes = calculateLockMinutes(failureCount);
if (lockMinutes > 0) {
lockAccount(account, lockMinutes);
// 重置失败计数(锁定期间不需要继续计数)
redisTemplate.delete(attemptKey);
}
}
/**
* 检查账号是否被锁定
*/
public boolean isAccountLocked(String account) {
String lockKey = getLockKey(account);
return Boolean.TRUE.equals(redisTemplate.hasKey(lockKey));
}
/**
* 获取锁定剩余时间
*/
public String getLockRemainingTime(String account) {
String lockKey = getLockKey(account);
boolean isExist = Boolean.TRUE.equals(redisTemplate.hasKey(lockKey));
if(isExist){
LocalDateTime now = LocalDateTime.now();
LocalDateTime futureTime = now.plusSeconds(redisTemplate.getExpire(lockKey,TimeUnit.SECONDS));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
return futureTime.format(formatter);
}else{
return "";
}
}
}
package com.cnooc.expert.auth.service;
import com.cnooc.expert.system.entity.pojo.ZhuanJiaUser;
import com.cnooc.expert.system.entity.vo.LoginVO;
import com.cnooc.expert.system.entity.vo.VerifyCodeVO;
public interface LoginService {
/**
* 登录,兼容短信登录和密码登录
* @param loginVO
* @return
*/
String login(LoginVO loginVO);
/**
* 发送短信验证码
* @param vo
* @return
*/
String sendPhoneCode(LoginVO vo);
/**
* 修改密码
* @param loginVO
* @return
*/
String changePass(LoginVO loginVO);
/**
* 验证码验证接口
* @param codeVO
* @return
*/
String verifyCode(VerifyCodeVO codeVO);
/**
* 根据token查询专家基础信息
* @param token
* @return
*/
ZhuanJiaUser getZhuanJiaUserByToken(String token);
}
package com.cnooc.expert.auth.service;
import com.cnooc.expert.system.entity.pojo.SlideImageResult;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.concurrent.ThreadLocalRandom;
@Service
public class SlideCaptchaService {
private static final int SLIDER_WIDTH = 50;
private static final int SLIDER_HEIGHT = 50;
@Value("classpath:images/backgrounds/*")
private Resource[] backgroundResources;
/**
* 使用预设背景图片生成验证码
*/
public SlideImageResult generateSlideImages() throws IOException {
// 随机选择背景图片
Resource bgResource = backgroundResources[
ThreadLocalRandom.current().nextInt(backgroundResources.length)
];
BufferedImage background = ImageIO.read(bgResource.getInputStream());
// 调整背景图尺寸
background = resizeImage(background, 300, 200);
// 生成滑块位置
int sliderX = ThreadLocalRandom.current().nextInt(50, 250);
int sliderY = ThreadLocalRandom.current().nextInt(10, 150);
// 创建滑块
BufferedImage sliderImage = createSliderWithShadow(background, sliderX, sliderY);
drawSliderHole(background, sliderX, sliderY);
// 转换为Base64
String bgBase64 = imageToBase64(background, "png");
String sliderBase64 = imageToBase64(sliderImage, "png");
SlideImageResult result = new SlideImageResult();
result.setBackgroundBase64(bgBase64);
result.setSlideBase64(sliderBase64);
result.setSliderX(sliderX);
result.setSliderY(sliderY);
return result;
}
/**
* 图片转换为Base64
*/
private String imageToBase64(BufferedImage image, String format) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, format, baos);
byte[] imageBytes = baos.toByteArray();
return Base64.getEncoder().encodeToString(imageBytes);
}
/**
* 在背景图上绘制滑块挖空效果
*/
private void drawSliderHole(BufferedImage background, int x, int y) {
Graphics2D g2d = background.createGraphics();
// 设置挖空区域
g2d.setComposite(AlphaComposite.Clear);
g2d.fillRect(x, y, SLIDER_WIDTH, SLIDER_HEIGHT);
// 重置合成规则,绘制边框
g2d.setComposite(AlphaComposite.SrcOver);
g2d.setColor(Color.GRAY);
g2d.setStroke(new BasicStroke(2));
g2d.drawRect(x, y, SLIDER_WIDTH, SLIDER_HEIGHT);
g2d.dispose();
}
/**
* 创建带阴影效果的滑块
*/
private BufferedImage createSliderWithShadow(BufferedImage background, int x, int y) {
int width = 50;
int height = 50;
int shadowSize = 3;
BufferedImage slider = new BufferedImage(
width + shadowSize, height + shadowSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = slider.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制阴影
g2d.setColor(new Color(0, 0, 0, 100));
g2d.fillRoundRect(shadowSize, shadowSize, width, height, 10, 10);
// 绘制滑块主体
Shape sliderShape = new RoundRectangle2D.Double(0, 0, width, height, 10, 10);
g2d.setClip(sliderShape);
BufferedImage subImage = background.getSubimage(x, y, width, height);
g2d.drawImage(subImage, 0, 0, null);
g2d.setClip(null);
// 绘制边框
g2d.setColor(new Color(64, 158, 255));
g2d.setStroke(new BasicStroke(2));
g2d.draw(sliderShape);
g2d.dispose();
return slider;
}
/**
* 调整图片尺寸
*/
private BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) {
BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = resizedImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
return resizedImage;
}
}
package com.cnooc.expert.auth.service;
import com.cnooc.expert.controller.auth.model.response.SlideCaptchaVO;
import com.cnooc.expert.system.entity.vo.SysCaptchaVO;
public interface SysCaptchaService {
......@@ -9,6 +10,7 @@ public interface SysCaptchaService {
*/
SysCaptchaVO generate();
SlideCaptchaVO generateSlide();
/**
* 验证码效验
*
......@@ -18,6 +20,8 @@ public interface SysCaptchaService {
*/
boolean validate(String key, String code);
boolean validateSlide(String code, Integer moveX);
/**
* 是否开启登录验证码
*
......
......@@ -2,6 +2,7 @@ package com.cnooc.expert.auth.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdcardUtil;
import com.cnooc.expert.auth.service.AccountLockService;
import com.cnooc.expert.common.constant.TokenConstants;
import com.cnooc.expert.common.exception.BusinessException;
import com.cnooc.expert.common.exception.GlobalErrorCodeConstants;
......@@ -21,12 +22,12 @@ 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.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
......@@ -45,6 +46,9 @@ public class LoginServiceImpl implements LoginService {
private SmsService smsService;
@Autowired
private AccountLockService accountLockService;
@Autowired
private SysCaptchaService sysCaptchaService;
@Autowired
......@@ -94,17 +98,17 @@ public class LoginServiceImpl implements LoginService {
//待确认,验证码下发是调用第三方平台吗
//return Result.success("验证码已发送");
boolean flag = sysCaptchaService.validate(vo.getKey(), vo.getCaptcha());
/* boolean flag = sysCaptchaService.validate(vo.getKey(), vo.getCaptcha());
if(!flag){
throw new BusinessException(GlobalErrorCodeConstants.CAPTCHA_EXPIRED.getCode(),GlobalErrorCodeConstants.CAPTCHA_EXPIRED.getMsg());
}else{
}else{*/
boolean smsfalg = smsService.sendSmsCode(vo.getPhoneNumber());
if(!smsfalg) {
throw new BusinessException(GlobalErrorCodeConstants.SEND_SMS_ERROR.getCode(),GlobalErrorCodeConstants.SEND_SMS_ERROR.getMsg());
} else {
return "短信验证码发送成功";
}
}
//}
}
@Override
......@@ -167,26 +171,30 @@ public class LoginServiceImpl implements LoginService {
if(expertInfoResp == null){
throw new BusinessException(GlobalErrorCodeConstants.USER_NOT_EXISTS.getCode(),GlobalErrorCodeConstants.USER_NOT_EXISTS.getMsg());
}
boolean isAccountLocked = accountLockService.isAccountLocked(expertInfoResp.getZhuanJiaGuid());
if(isAccountLocked){
//如果账号锁定了,返回错误信息
Map<String, Object> errorData = new HashMap<>();
errorData.put("dueDate", accountLockService.getLockRemainingTime(expertInfoResp.getZhuanJiaGuid()));
throw new BusinessException(GlobalErrorCodeConstants.USER_LOCKED.getCode(),GlobalErrorCodeConstants.USER_LOCKED.getMsg(), "user_login", errorData);
}
ExpertInfoAppResp expertInfoAppResp = loginServicesClient.getZhuanJiaInfoAppById(expertInfoResp.getZhuanJiaGuid());
if(expertInfoAppResp == null){
throw new BusinessException(GlobalErrorCodeConstants.PASSWORD_NOT_EXIST.getCode(),GlobalErrorCodeConstants.PASSWORD_NOT_EXIST.getMsg());
}
// 验证码效验
boolean flag = sysCaptchaService.validate(loginVO.getKey(), loginVO.getCaptcha());
/* boolean flag = sysCaptchaService.validate(loginVO.getKey(), loginVO.getCaptcha());
if (!flag) {
// 保存登录日志
//sysLogLoginService.save(login.getUsername(), Constant.FAIL, LoginOperationEnum.CAPTCHA_FAIL.getValue());
throw new BusinessException(GlobalErrorCodeConstants.CAPTCHA_EXPIRED.getCode(),GlobalErrorCodeConstants.CAPTCHA_EXPIRED.getMsg());
}
}*/
//1.需要去库中查询,是否存在
//2.存在校验密码
//解密
String pwd = Sm2Util.decrypt(loginVO.getPassword());
flag = passwordEncoder.matches(pwd, expertInfoAppResp.getPassword());
boolean flag = passwordEncoder.matches(pwd, expertInfoAppResp.getPassword());
if (!flag) {
// 登录日志
//sysLogLoginService.savePortal(login.getAccountName(), Constant.FAIL, LoginOperationEnum.ACCOUNT_FAIL.getValue(), 1);
accountLockService.handleLoginFailure(expertInfoResp.getZhuanJiaGuid());
throw new BusinessException(GlobalErrorCodeConstants.PASSWORD_ERROR.getCode(),GlobalErrorCodeConstants.PASSWORD_ERROR.getMsg());
}
......@@ -198,6 +206,7 @@ public class LoginServiceImpl implements LoginService {
UserUtils.setUserId(zhuanJiaUser);
String token = JwtUtils.createToken(expertInfoResp.getZhuanJiaGuid(),uuidKey);
//6.返回token
accountLockService.handleLoginSuccess(expertInfoResp.getZhuanJiaGuid());
return token;
}
......@@ -216,35 +225,31 @@ public class LoginServiceImpl implements LoginService {
if(expertInfoResp == null){
throw new BusinessException(GlobalErrorCodeConstants.USER_NOT_EXISTS.getCode(),GlobalErrorCodeConstants.USER_NOT_EXISTS.getMsg());
}
//expertInfoResp = new ExpertInfoResp();
//expertInfoResp.setZhuanJiaGuid("1234");
boolean isAccountLocked = accountLockService.isAccountLocked(expertInfoResp.getZhuanJiaGuid());
if(isAccountLocked){
//如果账号锁定了,返回错误信息
Map<String, Object> errorData = new HashMap<>();
errorData.put("dueDate", accountLockService.getLockRemainingTime(expertInfoResp.getZhuanJiaGuid()));
throw new BusinessException(GlobalErrorCodeConstants.USER_LOCKED.getCode(),GlobalErrorCodeConstants.USER_LOCKED.getMsg(), "user_login", errorData);
}
//2.存在校验验证码
if (!smsService.verifySmsCode(loginVO.getPhoneNumber(), loginVO.getPhoneCode())) {
//登录日志
//sysLogLoginService.savePortal(login.getPhone(), Constant.FAIL, LoginOperationEnum.CAPTCHA_FAIL.getValue(), 1);
accountLockService.handleLoginFailure(expertInfoResp.getZhuanJiaGuid());
throw new BusinessException(GlobalErrorCodeConstants.CAPTCHA_EXPIRED.getCode(),GlobalErrorCodeConstants.CAPTCHA_EXPIRED.getMsg());
}
//3.生成相应的uuid作为redis的key
String uuidKey = UUID.randomUUID().toString();
//expertInfoResp = new ExpertInfoResp();
//expertInfoResp.setZhuanJiaGuid("1234");
ZhuanJiaUser zhuanJiaUser = convert2ZhuanjiaUser( expertInfoResp );
redisTemplate.opsForValue().set(TokenConstants.LOGIN_USER_KEY_ + expertInfoResp.getZhuanJiaGuid(), zhuanJiaUser, 48, TimeUnit.HOURS);
UserUtils.setUserId(zhuanJiaUser);
String token = JwtUtils.createToken(expertInfoResp.getZhuanJiaGuid(),uuidKey);
accountLockService.handleLoginSuccess(expertInfoResp.getZhuanJiaGuid());
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));
// }
private ZhuanJiaUser convert2ZhuanjiaUser(ExpertInfoResp expertInfoResp){
if( expertInfoResp == null ){
return null;
......@@ -260,7 +265,24 @@ public class LoginServiceImpl implements LoginService {
zhuanJiaUser.setZhuanJiaName(expertInfoResp.getZhuanJiaName());
zhuanJiaUser.setZhuanJiaShiXiangGuid(expertInfoResp.getZhuanJiaShiXiangGuid());
zhuanJiaUser.setZhuanJiaZhuangTai(expertInfoResp.getZhuanJiaZhuangTai());
zhuanJiaUser.setAdAccount(expertInfoResp.getAdAccount());
return zhuanJiaUser;
}
@Override
public ZhuanJiaUser getZhuanJiaUserByToken(String token){
try {
Map<String, String> userMap = JwtUtils.getTokenInfo(token);
String userId = userMap.get(TokenConstants.USER_ID);
ZhuanJiaUser zhuanjiaUser = (ZhuanJiaUser)redisTemplate.opsForValue().get(TokenConstants.LOGIN_USER_KEY_ + userId);
if(zhuanjiaUser!=null) {
UserUtils.setUserId(zhuanjiaUser);
}
return zhuanjiaUser;
} catch (Exception e) {
log.info("token解析异常 {}",e.getMessage(),e);
return null;
}
}
}
......@@ -2,15 +2,19 @@ package com.cnooc.expert.auth.service.impl;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.cnooc.expert.common.cache.RedisKeys;
import com.cnooc.expert.auth.service.SysCaptchaService;
import lombok.AllArgsConstructor;
import com.cnooc.expert.auth.service.*;
import com.cnooc.expert.controller.auth.model.response.SlideCaptchaVO;
import com.cnooc.expert.system.entity.pojo.CaptchaData;
import com.cnooc.expert.system.entity.pojo.SlideImageResult;
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 org.apache.commons.lang3.StringUtils;
import java.util.concurrent.TimeUnit;
......@@ -19,6 +23,9 @@ public class SysCaptchaServiceImpl implements SysCaptchaService {
private static final int EXPIRE_MINUTES = 5;
@Autowired
private SlideCaptchaService slideCaptchaService;
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public SysCaptchaVO generate() {
......@@ -44,6 +51,43 @@ public class SysCaptchaServiceImpl implements SysCaptchaService {
}
@Override
public SlideCaptchaVO generateSlide(){
try {
// 1. 生成背景图和滑块图
SlideImageResult imageResult = slideCaptchaService.generateSlideImages();
// 2. 生成随机位置
// int x = ThreadLocalRandom.current().nextInt(50, 250);
// int y = ThreadLocalRandom.current().nextInt(10, 150);
int x = imageResult.getSliderX();
int y = imageResult.getSliderY();
// 3. 生成唯一标识
String token = UUID.randomUUID().toString();
// 4. 存储验证数据到Redis (5分钟过期)
CaptchaData captchaData = new CaptchaData(x, y, System.currentTimeMillis());
redisTemplate.opsForValue().set(
"captcha:" + token,
JSON.toJSONString(captchaData),
5, TimeUnit.MINUTES
);
// 5. 返回前端所需数据
SlideCaptchaVO vo = new SlideCaptchaVO();
vo.setToken(token);
vo.setBackgroundImage(imageResult.getBackgroundBase64());
vo.setSlideImage(imageResult.getSlideBase64());
vo.setStartY(y);
return vo;
} catch (Exception e) {
return null;
}
}
@Override
public boolean validate(String key, String code) {
// 如果关闭了验证码,则直接效验通过
if (!isCaptchaEnabled()) {
......@@ -62,6 +106,41 @@ public class SysCaptchaServiceImpl implements SysCaptchaService {
}
@Override
public boolean validateSlide(String token, Integer moveX) {
// 如果关闭了验证码,则直接效验通过
if (!isCaptchaEnabled()) {
return true;
}
if (StrUtil.isBlank(token)) {
return false;
}
// 1. 从Redis获取验证数据
String newkey = "captcha:" + token;
String dataStr = redisTemplate.opsForValue().get(newkey);
if (StringUtils.isEmpty(dataStr)) {
return false;
}
// 2. 解析数据
CaptchaData captchaData = JSON.parseObject(dataStr, CaptchaData.class);
// 3. 验证滑动距离 (允许±3像素的误差)
int expectedX = captchaData.getX();
int actualX = moveX;
if (Math.abs(actualX - expectedX) <= 3) {
// 验证成功,删除Redis中的key
redisTemplate.delete(newkey);
return true;
} else {
return false;
}
}
@Override
public boolean isCaptchaEnabled() {
return true;
}
......
......@@ -26,6 +26,7 @@ public interface GlobalErrorCodeConstants {
ErrorCode PASSWORD_ERROR = new ErrorCode(2003, "密码错误");
ErrorCode USER_DISABLED = new ErrorCode(2004, "用户已被禁用");
ErrorCode PASSWORD_NOT_EXIST = new ErrorCode(2005, "密码不存在,请先重置密码");
ErrorCode USER_LOCKED = new ErrorCode(2006, "账号已锁定");
// ========== 参数校验错误 (3000-3999) ==========
ErrorCode PARAM_REQUIRED = new ErrorCode(3001, "必填字段不能为空");
......
......@@ -51,6 +51,10 @@ public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ApiResult<String> handleBusinessException(BusinessException e, HttpServletRequest request) {
log.error("业务异常: {},请求URL: {}", e.getMessage(), request.getRequestURI(), e);
if(e.getErrorCode()==GlobalErrorCodeConstants.USER_LOCKED.getCode()){
//这个需要特殊处理
return ApiResult.errorWithResult(GlobalErrorCodeConstants.USER_LOCKED.getCode(),GlobalErrorCodeConstants.USER_LOCKED.getMsg(),String.valueOf(e.getErrorData().get("dueDate")));
}
return ApiResult.error(e.getErrorCode(), e.getMessage());
}
......
......@@ -66,6 +66,13 @@ public class ApiResult<T> implements Serializable {
return new ApiResult<>(httpCode, message, null);
}
/**
* 失败返回结果,并带有数据
*/
public static <T> ApiResult<T> errorWithResult(int httpCode, String message, T result) {
return new ApiResult<>(httpCode, message, result);
}
// Getters and Setters
}
......@@ -11,6 +11,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class TheWebMvcConfigurer implements WebMvcConfigurer {
<<<<<<< HEAD
// @Autowired
// private LoginInterceptor loginInterceptor;
//
......@@ -36,5 +37,32 @@ public class TheWebMvcConfigurer implements WebMvcConfigurer {
// public WebAuthInterceptor webAuthInterceptor() {
// return new WebAuthInterceptor();
// }
=======
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册登录拦截器,并设置拦截路径和排除路径
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns( // 排除一些路径
"/sys/**",
"/verify/**",
"/person/**",
"/text/**",
"/error/**",
"/error"
);
registry.addInterceptor(webAuthInterceptor());
}
@Bean
public WebAuthInterceptor webAuthInterceptor() {
return new WebAuthInterceptor();
}
>>>>>>> 3bc7fe8847766d39bf419c764c9495af3590dfa2
}
......@@ -7,6 +7,8 @@ import com.cnooc.expert.common.response.ApiResult;
import com.cnooc.expert.auth.service.LoginService;
import com.cnooc.expert.common.utils.JwtUtils;
import com.cnooc.expert.common.utils.KafkaProducerUtil;
import com.cnooc.expert.controller.auth.model.request.VerifyRequest;
import com.cnooc.expert.controller.auth.model.response.SlideCaptchaVO;
import com.cnooc.expert.external.expert.model.response.ExpertInfoResp;
import com.cnooc.expert.system.entity.pojo.ZhuanJiaUser;
import com.cnooc.expert.system.entity.vo.LoginVO;
......@@ -146,6 +148,32 @@ public class LoginController {
return ApiResult.successWithResult(response);
}
@GetMapping("/slideCaptcha")
public ApiResult<Map<String, String>> slideCaptcha() {
SlideCaptchaVO captchaVO = sysCaptchaService.generateSlide();
// 5. 构建响应
Map<String, String> response = new HashMap<>();
response.put("backgroundImage", captchaVO.getBackgroundImage());
response.put("slideImage", captchaVO.getSlideImage());
response.put("token", captchaVO.getToken());
response.put("startY",String.valueOf(captchaVO.getStartY()));
return ApiResult.successWithResult(response);
}
@PostMapping("/verifySlideCaptcha")
public ApiResult<Map<String, Object>> verifySlideCaptcha(
@RequestBody VerifyRequest request) {
Map<String, Object> result = new HashMap<>();
boolean flag = sysCaptchaService.validateSlide(request.getToken(),request.getMoveX());
String msg = "验证成功";
if (!flag) {
msg = "验证失败";
}
result.put("success", flag);
result.put("message", msg);
return ApiResult.successWithResult(result);
}
/**
* 验证图片验证码
*/
......
package com.cnooc.expert.controller.auth.model.request;
import lombok.Data;
@Data
public class VerifyRequest {
private String token;
private Integer moveX; // 滑块移动的X距离
}
package com.cnooc.expert.controller.auth.model.response;
import lombok.Data;
@Data
public class SlideCaptchaVO {
private String token;
private String backgroundImage; // base64格式
private String slideImage; // base64格式
private Integer startY; // 滑块初始Y坐标
}
package com.cnooc.expert.controller.auth.model.response;
import lombok.Data;
@Data
public class VerifyResult {
private Boolean success;
private String message;
public VerifyResult(Boolean success, String message) {
this.success = success;
this.message = message;
}
}
\ No newline at end of file
package com.cnooc.expert.external.expert.api;
<<<<<<< HEAD
import com.cnooc.expert.external.expert.model.request.ExpertInfoGetReq;
import com.cnooc.expert.external.expert.model.response.ExpertInfoGetTestResp;
=======
>>>>>>> 3bc7fe8847766d39bf419c764c9495af3590dfa2
import com.cnooc.expert.external.expert.model.request.ExpertInfoAppReq;
import com.cnooc.expert.external.expert.model.request.ExpertInfoGetReq;
import com.cnooc.expert.external.expert.model.request.ExpertInfoReq;
import com.cnooc.expert.external.expert.model.response.ExpertInfoAppResp;
import com.cnooc.expert.external.expert.model.response.ExpertInfoGetTestResp;
import com.cnooc.expert.external.expert.model.response.ExpertInfoResp;
import retrofit2.Call;
import retrofit2.http.Body;
......@@ -29,4 +34,8 @@ public interface LoginServiceApi {
@POST("/zjfw/zggrxxgl/updateZhuanJiaInfoApp")
Call<ExpertInfoAppResp> updateZhuanJiaInfoApp(@HeaderMap Map<String, Object> headers, @Body ExpertInfoAppReq expertInfoAppReq);
<<<<<<< HEAD
=======
>>>>>>> 3bc7fe8847766d39bf419c764c9495af3590dfa2
}
package com.cnooc.expert.system.entity.pojo;
import lombok.Data;
@Data
public class CaptchaData {
private Integer x;
private Integer y;
private Long timestamp;
public CaptchaData(Integer x, Integer y, Long timestamp) {
this.x = x;
this.y = y;
this.timestamp = timestamp;
}
}
\ No newline at end of file
package com.cnooc.expert.system.entity.pojo;
import lombok.Data;
@Data
public class SlideImageResult {
private String backgroundBase64;
private String slideBase64;
private Integer sliderX;
private Integer sliderY;
}
......@@ -35,6 +35,8 @@ public class ZhuanJiaUser {
private String suoShuBuMeng;//所在部门
private String adAccount;
}
package com.cnooc.expert.system.operatelog.aspect;
import com.alibaba.fastjson.JSON;
import com.cnooc.expert.common.utils.KafkaProducerUtil;
import com.cnooc.expert.common.utils.UserUtils;
import com.cnooc.expert.system.entity.pojo.ZhuanJiaUser;
import com.cnooc.expert.system.operatelog.dto.LogBody;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
......@@ -8,6 +11,8 @@ import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
......@@ -25,6 +30,14 @@ public class LogAspectj {
private static final String EXCEPTION_CODE = "500";
private String host="";
@Value("${app.info.appId}")
private String appId;
@Value("${app.info.appName}")
private String appName;
@Autowired
private KafkaProducerUtil kafkaProducerUtil;
@Pointcut("execution(* com.cnooc.expert.controller.auth.*.*(..)) ||"+
"execution(* com.cnooc.expert.controller.expert.*.*(..)) || " +
"execution(* com.cnooc.expert.controller.portal.*.*(..)) || " +
......@@ -90,9 +103,11 @@ public class LogAspectj {
HttpServletRequest request = sra.getRequest();
return request;
}
private String getCurrentUser() {
private ZhuanJiaUser getCurrentUser() {
//这里需要添加代码
return "";
ZhuanJiaUser zhuanJiaUser = UserUtils.getUserId();
System.out.println(zhuanJiaUser);
return zhuanJiaUser;
}
/**
* mysql
......@@ -105,7 +120,13 @@ public class LogAspectj {
System.out.println("进入sendLog方法========");
HttpServletRequest httpServletRequest = getHttpServletRequest();
String method = httpServletRequest.getMethod();
String account = getCurrentUser();
ZhuanJiaUser zhuanJiaUser = getCurrentUser();
if( zhuanJiaUser == null ){
log.info("用户为空,不记录日志");
return;
}
LogBody logBody = new LogBody();
Enumeration<String> headers = httpServletRequest.getHeaderNames();
......@@ -125,9 +146,28 @@ public class LogAspectj {
}else {
logBody.setResponseContent(JSON.toJSONString(returnValue));
}
logBody.setAppId(appId);
logBody.setAppName(appName);
// 用户id
logBody.setUserId(zhuanJiaUser.getZhuanJiaGuid());
// 用户account
logBody.setAccount(zhuanJiaUser.getAdAccount());
// 部门id
// logBody.setDomainId();
// 部门名称
logBody.setDomainName(zhuanJiaUser.getSuoShuBuMeng());
// logBody.setRole();
logBody.setRequestPath(httpServletRequest.getRequestURI());
String uuidKey = UUID.randomUUID().toString();
logBody.setOperateRecordId(uuidKey);
System.out.println("logBody对象信息如下=========");
System.out.println(logBody);
//将数据发送到kafka,这里需要加代码逻辑
// Kafka默认异步发送(Topic、key需要提供,如果有key的话这样写:kafkaProducerUtil.sendMessage("test-topic(topic值)", "key的值",logBody); )
kafkaProducerUtil.sendMessage("test-topic", logBody);
} catch (Exception e) {
e.printStackTrace();
......
......@@ -38,10 +38,10 @@ spring:
linger-ms: 1
buffer-memory: 33554432
app:
info:
appId: 10000
appName: 海油小程序
server:
port: 9090
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