mirror of
https://github.com/RemainderTime/spring-boot-base-demo.git
synced 2026-02-25 23:50:46 +08:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
43
src/main/java/cn/xf/basedemo/config/RedisConfig.java
Normal file
43
src/main/java/cn/xf/basedemo/config/RedisConfig.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user