Compare commits
2 Commits
f964e37531
...
bda0b64406
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bda0b64406 | ||
|
|
75c35369fc |
19
pom.xml
19
pom.xml
@ -201,6 +201,25 @@
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>8.0.1.Final</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 公共工具 -->
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>1.9.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<!-- 插件配置 -->
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
package asia.yulinling.workflow.annotation;
|
||||
|
||||
import asia.yulinling.workflow.annotation.validator.FieldMatchValidator;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 字段校验
|
||||
* </p>
|
||||
*
|
||||
* @author YLL
|
||||
* @since 2025/8/5
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Constraint(validatedBy = FieldMatchValidator.class)
|
||||
public @interface FieldMatch {
|
||||
String message() default "字段值不匹配";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
String first();
|
||||
|
||||
String second();
|
||||
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface List {
|
||||
FieldMatch[] value();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package asia.yulinling.workflow.annotation.validator;
|
||||
|
||||
import asia.yulinling.workflow.annotation.FieldMatch;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 校验逻辑
|
||||
* </p>
|
||||
*
|
||||
* @author YLL
|
||||
* @since 2025/8/5
|
||||
*/
|
||||
@Slf4j
|
||||
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
|
||||
private String firstFieldName;
|
||||
private String secondFieldName;
|
||||
|
||||
@Override
|
||||
public void initialize(FieldMatch constraintAnnotation) {
|
||||
firstFieldName = constraintAnnotation.first();
|
||||
secondFieldName = constraintAnnotation.second();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
|
||||
try {
|
||||
final Object firstObj = BeanUtils.getProperty(o, firstFieldName);
|
||||
final Object secondObj = BeanUtils.getProperty(o, secondFieldName);
|
||||
|
||||
return firstObj == null && secondObj == null ||
|
||||
firstObj != null && firstObj.equals(secondObj);
|
||||
} catch (final Exception ignore) {
|
||||
// ignore
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -8,18 +8,16 @@ import asia.yulinling.workflow.model.vo.LoginVO;
|
||||
import asia.yulinling.workflow.model.vo.RegisterVO;
|
||||
import asia.yulinling.workflow.service.AuthService;
|
||||
import asia.yulinling.workflow.utils.JwtUtil;
|
||||
import com.alibaba.druid.support.spring.stat.annotation.Stat;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -33,6 +31,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
* @author YLL
|
||||
* @since 2025/6/13
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/auth")
|
||||
@ -44,18 +43,12 @@ public class AuthController {
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<ApiResponse<LoginVO>> login(@Valid @RequestBody LoginRequest loginRequest) {
|
||||
try {
|
||||
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
loginRequest.getUsername(), loginRequest.getPassword()
|
||||
));
|
||||
|
||||
LoginVO loginVO = authService.login(authentication, loginRequest.getRememberMe());
|
||||
return ResponseEntity.ok().body(ApiResponse.ofStatus(Status.LOGIN_SUCCESS, loginVO));
|
||||
} catch (BadCredentialsException e) {
|
||||
return ResponseEntity.ok().body(ApiResponse.ofStatus(Status.USERNAME_PASSWORD_ERROR));
|
||||
} catch (LockedException e) {
|
||||
return ResponseEntity.ok().body(ApiResponse.ofStatus(Status.ACCOUNT_LOCKED));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
@ -69,8 +62,9 @@ public class AuthController {
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ApiResponse<RegisterVO> register(@RequestBody RegisterRequest registerRequest) throws Exception {
|
||||
return authService.register(registerRequest);
|
||||
public ApiResponse<RegisterVO> register(@Valid @RequestBody RegisterRequest registerRequest) throws Exception {
|
||||
authService.register(registerRequest);
|
||||
return ApiResponse.ofStatus(Status.SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package asia.yulinling.workflow.dto.request;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
/**
|
||||
* <p>
|
||||
@ -27,5 +28,6 @@ public class LoginRequest {
|
||||
/**
|
||||
* 记住我
|
||||
*/
|
||||
@NotNull(message = "记住我不能为空")
|
||||
private Boolean rememberMe = false;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package asia.yulinling.workflow.dto.request;
|
||||
|
||||
import asia.yulinling.workflow.annotation.FieldMatch;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
@ -15,6 +16,7 @@ import lombok.Data;
|
||||
* @since 2025/6/19
|
||||
*/
|
||||
@Data
|
||||
@FieldMatch(first = "password", second = "confirmPassword", message = "密码不一致")
|
||||
public class RegisterRequest {
|
||||
|
||||
/**
|
||||
|
||||
@ -4,11 +4,23 @@ import asia.yulinling.workflow.exception.PageException;
|
||||
import asia.yulinling.workflow.exception.ServiceException;
|
||||
import asia.yulinling.workflow.model.ApiResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 全局异常处理
|
||||
@ -22,6 +34,63 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
public class GlobalExceptionHandler {
|
||||
private static final String DEFAULT_ERROR_VIEW = "error";
|
||||
|
||||
/**
|
||||
* 统一 ServiceException 异常处理
|
||||
*
|
||||
* @param e ServiceException
|
||||
* @return 统一返回 json 格式
|
||||
*/
|
||||
@ExceptionHandler(ServiceException.class)
|
||||
@ResponseBody
|
||||
public ApiResponse<?> catchErrorHandler(ServiceException e) {
|
||||
log.error("service error:{}", e.getMessage());
|
||||
return ApiResponse.of(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage(), null);
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<?>> handleValidationExceptions(
|
||||
MethodArgumentNotValidException ex) {
|
||||
|
||||
// Map<String, String> errors = new LinkedHashMap<>();
|
||||
//
|
||||
// ex.getBindingResult().getAllErrors().forEach(error -> {
|
||||
// String fieldName;
|
||||
//
|
||||
// if (error instanceof FieldError) {
|
||||
// // 处理字段级错误(如 @NotBlank, @Size)
|
||||
// fieldName = ((FieldError) error).getField();
|
||||
// } else {
|
||||
// // 处理对象级错误(如 @FieldMatch)
|
||||
// fieldName = error.getObjectName(); // 返回对象名,如 "registerRequest"
|
||||
// }
|
||||
//
|
||||
// String errorMessage = error.getDefaultMessage(); // 获取默认错误消息
|
||||
// errors.put(fieldName, errorMessage);
|
||||
// });
|
||||
|
||||
ObjectError objectError = ex.getBindingResult().getAllErrors().get(0);
|
||||
String defaultMessage = objectError.getDefaultMessage();
|
||||
|
||||
ApiResponse<?> response = ApiResponse.of(HttpStatus.BAD_REQUEST.value(), defaultMessage, null);
|
||||
|
||||
return ResponseEntity.badRequest().body(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一 页面 异常处理
|
||||
*
|
||||
* @param e PageException
|
||||
* @return 统一跳转到异常页面
|
||||
*/
|
||||
@ExceptionHandler(PageException.class)
|
||||
public ModelAndView pageErrorHandler(PageException e) {
|
||||
log.error("test" + e.getMessage());
|
||||
ModelAndView view = new ModelAndView();
|
||||
view.addObject("exception", e);
|
||||
view.setViewName(DEFAULT_ERROR_VIEW);
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一 json 异常处理
|
||||
*
|
||||
@ -34,32 +103,4 @@ public class GlobalExceptionHandler {
|
||||
log.error("json error:{}", e.getMessage());
|
||||
return ApiResponse.of(500, e.getMessage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一 ServiceException 异常处理
|
||||
*
|
||||
* @param e ServiceException
|
||||
* @return 统一返回 json 格式
|
||||
*/
|
||||
@ExceptionHandler(ServiceException.class)
|
||||
@ResponseBody
|
||||
public ApiResponse<?> catchErrorHandler(ServiceException e) {
|
||||
log.error("service error:{}", e.getMessage());
|
||||
return ApiResponse.of(500, e.getMessage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一 页面 异常处理
|
||||
*
|
||||
* @param e PageException
|
||||
* @return 统一跳转到异常页面
|
||||
*/
|
||||
@ExceptionHandler(PageException.class)
|
||||
public ModelAndView pageErrorHandler(PageException e) {
|
||||
log.error(e.getMessage());
|
||||
ModelAndView view = new ModelAndView();
|
||||
view.addObject("exception", e);
|
||||
view.setViewName(DEFAULT_ERROR_VIEW);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,5 +37,5 @@ public interface AuthService {
|
||||
* @param request 注册请求
|
||||
* @return 请求结果
|
||||
*/
|
||||
ApiResponse<RegisterVO> register(RegisterRequest request) throws Exception;
|
||||
RegisterVO register(RegisterRequest request) throws Exception;
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package asia.yulinling.workflow.service.impl;
|
||||
|
||||
import asia.yulinling.workflow.constant.Status;
|
||||
import asia.yulinling.workflow.dto.request.LoginRequest;
|
||||
import asia.yulinling.workflow.dto.request.RegisterRequest;
|
||||
import asia.yulinling.workflow.mapper.UserMapper;
|
||||
import asia.yulinling.workflow.model.ApiResponse;
|
||||
@ -13,14 +12,10 @@ import asia.yulinling.workflow.service.AuthService;
|
||||
import asia.yulinling.workflow.utils.JwtUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -77,6 +72,10 @@ public class AuthServiceImpl implements AuthService {
|
||||
jwtUtil.invalidateToken(token);
|
||||
} catch (SecurityException e) {
|
||||
log.info("invalidateToken: {}", e.getMessage());
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected error during token invalidation: {}", e.getMessage(), e);
|
||||
throw new SecurityException("Failed to invalidate token", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,20 +87,14 @@ public class AuthServiceImpl implements AuthService {
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ApiResponse<RegisterVO> register(RegisterRequest request) throws Exception {
|
||||
|
||||
if (!StrUtil.equals(request.getConfirmPassword(), request.getPassword())) {
|
||||
log.info("password {}, confirmPassword {}", request.getConfirmPassword(), request.getPassword());
|
||||
return ApiResponse.of(400, "密码不一致", null);
|
||||
}
|
||||
|
||||
if (userMapper.exists(new LambdaQueryWrapper<User>().eq(User::getUsername, request.getUsername()))) {
|
||||
return ApiResponse.of(400, "用户名已存在", null);
|
||||
}
|
||||
|
||||
if (userMapper.exists(new LambdaQueryWrapper<User>().eq(User::getEmail, request.getEmail()))) {
|
||||
return ApiResponse.of(400, "邮箱已注册", null);
|
||||
}
|
||||
public RegisterVO register(RegisterRequest request) throws Exception {
|
||||
// if (userMapper.exists(new LambdaQueryWrapper<User>().eq(User::getUsername, request.getUsername()))) {
|
||||
// return ApiResponse.of(400, "用户名已存在", null);
|
||||
// }
|
||||
//
|
||||
// if (userMapper.exists(new LambdaQueryWrapper<User>().eq(User::getEmail, request.getEmail()))) {
|
||||
// return ApiResponse.of(400, "邮箱已注册", null);
|
||||
// }
|
||||
|
||||
User user = new User();
|
||||
user.setUsername(request.getUsername());
|
||||
@ -119,7 +112,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
log.info("insert user after: {}", user);
|
||||
RegisterVO registerVO = new RegisterVO();
|
||||
registerVO.setUserId(user.getId());
|
||||
return ApiResponse.ofStatus(Status.REGISTER_SUCCESS, registerVO);
|
||||
return registerVO;
|
||||
} catch (Exception e) {
|
||||
log.error("数据插入用户数据失败: {}", e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@ -2,26 +2,30 @@
|
||||
server.port=8080
|
||||
#server.servlet.context-path=/demo
|
||||
# mysql配置
|
||||
spring.datasource.url=jdbc:mysql://122.152.201.90:9912/workflow?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
|
||||
# MySQL 配置
|
||||
spring.datasource.url=jdbc:mysql://122.152.201.90:9912/workflow?\
|
||||
useUnicode=true&characterEncoding=UTF-8&useSSL=false&\
|
||||
rewriteBatchedStatements=true&cachePrepStmts=true&\
|
||||
prepStmtCacheSize=250&prepStmtCacheSqlLimit=2048&\
|
||||
useServerPrepStmts=true&connectionTimeout=3000&\
|
||||
socketTimeout=60000&serverTimezone=GMT%2B8
|
||||
spring.datasource.username=root
|
||||
spring.datasource.password=ENC(lLJgIEE5YJuSKnOpFBC4NFL+iqZZK97573fvgC7hZ8u3S6o/TlK15WfjnKTPOrQO)
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.sql.init.mode=always
|
||||
spring.sql.init.continue-on-error=true
|
||||
spring.sql.init.schema-locations=classpath:db/schema.sql
|
||||
spring.sql.init.data-locations=classpath:db/data.sql
|
||||
# 连接池配置
|
||||
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
|
||||
spring.datasource.druid.initial-size=4
|
||||
spring.datasource.druid.min-idle=6
|
||||
spring.datasource.druid.max-active=25
|
||||
spring.datasource.druid.max-wait=300000
|
||||
spring.datasource.druid.initial-size=10
|
||||
spring.datasource.druid.min-idle=10
|
||||
spring.datasource.druid.max-active=50
|
||||
spring.datasource.druid.max-wait=5000
|
||||
spring.datasource.druid.test-while-idle=true
|
||||
spring.datasource.druid.test-on-borrow=false
|
||||
spring.datasource.druid.test-on-return=false
|
||||
spring.datasource.druid.validation-query=SELECT 1
|
||||
spring.datasource.druid.time-between-eviction-runs-millis=60000
|
||||
spring.datasource.druid.min-evictable-idle-time-millis=300000
|
||||
# 关闭自动初始化(按需开启)
|
||||
spring.sql.init.mode=never
|
||||
# Redis配置
|
||||
spring.data.redis.host=122.152.201.90
|
||||
spring.data.redis.port=6379
|
||||
|
||||
Loading…
Reference in New Issue
Block a user