1. springboot版本升级3.x

2. mybatis plus版本升级3.x
3. dynamic mybatis plus版本升级3.x
4. redis版本升级3.x
5. reids实现配置优化
6. 替换swagger依赖(支持3.x)
7. 新增请求头工具类
8. 参数校验异常捕获优化
9. 拦截器注册为spring容器管理
10. 新增本地日志配置文件
This commit is contained in:
xiongf
2024-10-12 17:32:56 +08:00
parent cf4c78766b
commit 247e6f456a
16 changed files with 361 additions and 174 deletions

View File

@@ -1,14 +1,21 @@
package cn.xf.basedemo.common.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: xiongfeng
@@ -58,4 +65,22 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler{
return new GenericResponse(exception.getCode(),null,exception.getMessage());
}
/**
* 重写handleMethodArgumentNotValid 方法自定义处理参数校验错误信息
*
* @param ex
* @param headers
* @param status
* @param request
* @return
*/
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error -> {
errors.put(error.getField(), error.getDefaultMessage());
});
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
}

View File

@@ -2,6 +2,7 @@ package cn.xf.basedemo.common.utils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
@@ -34,14 +35,15 @@ public class RSAUtils {
/**
* 创建rsa密匙对
*
* @return
*/
private static Map<String, String> createEncryptKey(){
private static Map<String, String> createEncryptKey() {
KeyPairGenerator kpg;
try {
kpg = KeyPairGenerator.getInstance(RSA);
}catch (Exception e){
} catch (Exception e) {
throw new IllegalArgumentException();
}
kpg.initialize(ENCRYPT_SIZE);
@@ -52,7 +54,7 @@ public class RSAUtils {
PrivateKey aPrivate = keyPair.getPrivate();
String privateKey = Base64.encodeBase64URLSafeString(aPrivate.getEncoded());
Map<String, String> map =new HashMap<>();
Map<String, String> map = new HashMap<>();
map.put("publicKey", publicKey);
map.put("privateKey", privateKey);
@@ -62,6 +64,7 @@ public class RSAUtils {
/**
* 获取ras公匙
*
* @param publicKeyStr 公匙加密字符串
* @return
* @throws NoSuchAlgorithmException
@@ -77,6 +80,7 @@ public class RSAUtils {
/**
* 获取ras私匙
*
* @param privateKeyStr 私匙加密字符串
* @return
* @throws NoSuchAlgorithmException
@@ -91,12 +95,13 @@ public class RSAUtils {
}
/**
* 公匙加密
* @param data 字符串
* 公匙加密
*
* @param data 字符串
* @param publicKey
* @return
*/
public static String publicEncrypt(String data, RSAPublicKey publicKey){
public static String publicEncrypt(String data, RSAPublicKey publicKey) {
try {
@@ -106,19 +111,19 @@ public class RSAUtils {
byte[] bytes = cipher.doFinal(data.getBytes());
return Base64.encodeBase64URLSafeString(bytes);
// return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
}catch (Exception e){
} catch (Exception e) {
throw new RuntimeException();
}
}
public static String privateDecryption(String data, RSAPrivateKey privateKey){
public static String privateDecryption(String data, RSAPrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// byte[] bytes = cipher.doFinal(Base64.decodeBase64(data.getBytes(CHARSET)));
// return new String(bytes);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data.getBytes(CHARSET)), privateKey.getModulus().bitLength()), CHARSET);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data.getBytes(CHARSET), 0, data.getBytes(CHARSET).length), privateKey.getModulus().bitLength()), CHARSET);
} catch (Exception e) {
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
}
@@ -178,7 +183,7 @@ public class RSAUtils {
// String s1 = privateDecryption(ss, getPrivateKey(privateKey1));
// System.out.println("解密后明文:" + s1);
}catch (Exception e){
} catch (Exception e) {
throw new IllegalArgumentException();
}
@@ -186,5 +191,4 @@ public class RSAUtils {
}
}

View File

@@ -0,0 +1,67 @@
package cn.xf.basedemo.common.utils;
import cn.xf.basedemo.common.exception.LoginException;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* packageName com.wenliao.customer.common.utils
*
* @author remaindertime
* @className RequestHeaderUtil
* @date 2024/10/10
* @description
*/
public class RequestHeaderUtil {
/**
* 获取所有请求头
* @param request HttpServletRequest 对象
* @return 请求头的键值对 Map
*/
public static Map<String, String> getHeaders(HttpServletRequest request) {
Map<String, String> headersMap = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
headersMap.put(headerName, headerValue);
}
}
return headersMap;
}
/**
* 获取特定的请求头
* @param request HttpServletRequest 对象
* @param headerName 请求头名称
* @return 请求头值,如果没有则返回 null
*/
public static String getHeader(HttpServletRequest request, String headerName) {
return request.getHeader(headerName);
}
/**
* 获取token
*/
public static String getToken(HttpServletRequest request) {
//登录处理
String token = request.getHeader("Authorization");
if (StringUtils.isEmpty(token))
token = request.getParameter("token");
if (StringUtils.isEmpty(token)) {
throw new LoginException("请先登录");
}
// 如果 Authorization 头部中存在且以 "Bearer " 开头,则提取 token
if (token.startsWith("Bearer ")) {
token = token.substring(7); // 去除 "Bearer " 部分,获取真正的 token
}
return token;
}
}

View File

@@ -0,0 +1,43 @@
package cn.xf.basedemo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Resource
private RedisConnectionFactory factory;
@Bean
public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer(){
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}

View File

@@ -1,70 +0,0 @@
package cn.xf.basedemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* @program: spring-boot-base-demo
* @ClassName SpringFoxSwaggerConfig
* @description:
* @author: xiongfeng
* @create: 2022-06-16 16:44
**/
@Configuration
@EnableSwagger2
public class SpringFoxSwaggerConfig {
@Bean
public Docket docket(Environment environment) {
// 添加接口请求头参数配置 没有的话 可以忽略
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<>();
tokenPar.name("token")
.description("令牌")
.defaultValue("")
.modelRef(new ModelRef("string"))
.parameterType("header").required(false).build();
pars.add(tokenPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
//是否启动swagger 默认启动
.enable(true)
//所在分组
.groupName("base")
.select()
//指定扫描的包路径
.apis(RequestHandlerSelectors.basePackage("cn.xf.basedemo.controller.business"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(pars);
}
private ApiInfo apiInfo() {
Contact author = new Contact("reamindertime", "https://blog.csdn.net/qq_39818325?type=blog", "2439534736@qq.com");
return new ApiInfo(
"开箱即用springboot基础项目文档",
"开箱即用springboot基础项目文档",
"1.0",
"",
author,
"",
"",
new ArrayList()
);
}
}

View File

@@ -5,8 +5,7 @@ import cn.xf.basedemo.common.model.RetObj;
import cn.xf.basedemo.interceptor.SessionContext;
import cn.xf.basedemo.model.res.LoginInfoRes;
import cn.xf.basedemo.service.UserService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.*;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -20,7 +19,6 @@ import org.springframework.web.bind.annotation.RestController;
* @author: xiongfeng
* @create: 2022-06-28 09:17
**/
@Api(tags = "用户控制器")
@RestController(value = "用户控制器")
@RequestMapping("/user")
public class UserController {
@@ -28,16 +26,14 @@ public class UserController {
@Autowired
private UserService userService;
@ApiOperation(value = "用户登录", notes = "用户登录")
@ApiOperationSupport(order = 1)
@Operation(summary = "用户登录", description = "用户登录")
@PostMapping("/login")
public RetObj login(@RequestBody LoginInfoRes res){
return userService.login(res);
}
@ApiOperation(value = "用户信息", notes = "用户信息")
@ApiOperationSupport(order = 2)
@Operation(summary = "用户信息", description = "用户信息")
@PostMapping("/info")
public RetObj info(){
LoginUser loginUser = SessionContext.getInstance().get();

View File

@@ -1,5 +1,6 @@
package cn.xf.basedemo.interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
@@ -15,16 +16,22 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
public TokenInterceptor tokenInterceptor() {
return new TokenInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor()) //登录逻辑拦截类
registry.addInterceptor(tokenInterceptor()) //登录逻辑拦截类
.addPathPatterns("/**") //需要拦截的请求(设置的全部拦截)
.excludePathPatterns("/user/login","/web/**"); //忽略的请求
.excludePathPatterns("/user/login", "/web/**"); //忽略的请求
}
/**
* 放行Knife4j请求
*
* @param registry
*/
@Override

View File

@@ -5,13 +5,14 @@ import cn.xf.basedemo.common.exception.ResponseCode;
import cn.xf.basedemo.common.model.LoginUser;
import cn.xf.basedemo.common.utils.ApplicationContextUtils;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -23,37 +24,38 @@ import java.util.concurrent.TimeUnit;
* @author: xiongfeng
* @create: 2022-06-16 14:17
**/
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
RedisTemplate<Object,Object> redisTemplate =
(RedisTemplate<Object,Object>) ApplicationContextUtils.getBean("redisTemplate");
private RedisTemplate redisTemplate;
//不拦截的请求列表
private static final List<String> EXCLUDE_PATH_LIST = Arrays.asList("/user/login","/web/login");
private static final List<String> EXCLUDE_PATH_LIST = Arrays.asList("/user/login", "/web/login");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
if(EXCLUDE_PATH_LIST.contains(requestURI)){
if (EXCLUDE_PATH_LIST.contains(requestURI)) {
return true;
}
//登录处理
String token = request.getHeader("Authorization");
if(StringUtils.isEmpty(token))
if (StringUtils.isEmpty(token))
token = request.getParameter("token");
if(StringUtils.isEmpty(token)){
if (StringUtils.isEmpty(token)) {
throw new LoginException("请先登录");
}
String value = (String) redisTemplate.opsForValue().get("token:" + token);
if(StringUtils.isEmpty(value)){
if (StringUtils.isEmpty(value)) {
throw new LoginException();
}
JSONObject jsonObject = JSONObject.parseObject(value);
//JSON对象转换成Java对象
LoginUser loginUserInfo = JSONObject.toJavaObject(jsonObject, LoginUser.class);
if(loginUserInfo == null || loginUserInfo.getId() <= 0){
if (loginUserInfo == null || loginUserInfo.getId() <= 0) {
throw new LoginException(ResponseCode.USER_INPUT_ERROR);
}
redisTemplate.expire(token, 86700, TimeUnit.SECONDS);

View File

@@ -1,7 +1,6 @@
package cn.xf.basedemo.model.res;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
@@ -12,12 +11,11 @@ import lombok.Data;
* @create: 2022-07-04 11:46
**/
@Data
@ApiModel(value = "登录请求对象")
public class LoginInfoRes {
/**
* 登录密文
*/
@ApiModelProperty(value = "encryptedData", name = "登录密文")
@Schema(name = "登录密文")
private String encryptedData;
}