diff --git a/Dockerfile b/Dockerfile index 5f05bca..d5640e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,17 @@ -# java8运行环境 -FROM openjdk:8-jdk-alpine -# 作者名称 -MAINTAINER xiongfeng +# 使用 JDK 17 轻量级运行环境 +FROM eclipse-temurin:17-jre-alpine + +# 作者信息 +LABEL maintainer="xiongfeng" # 切换工作目录 WORKDIR /root/java -#1. coding自动化部署 -#COPY target/*.jar app.jar -## 暴露端口8080 -##EXPOSE 8080 -## 运行命令 -#ENTRYPOINT ["java","-Djava.security.egd=file:/dev/urandom","-Dfile.encoding=UTF-8","-Duser.timezone=Asia/Shanghai","-XX:MaxDirectMemorySize=1024m","-XX:MetaspaceSize=256m","-XX:MaxMetaspaceSize=512m","-XX:MaxRAMPercentage=80.0","-jar","app.jar"] +# 将编译好的 jar 包复制到容器中,避免硬编码包名版本号 +COPY target/*.jar app.jar -#2. 手动部署项目到docker环境中 -# 添加demo-start-1.0.0.jar文件到docker环境内 -ADD xf-boot-base-1.0.1.jar /root/java/xf-boot-base-1.0.1.jar -## 暴露端口8080 -#EXPOSE 8080 -## 运行命令 -ENTRYPOINT ["java", "-server", "-Xms512m", "-Xmx512m", "-jar", "/root/java/xf-boot-base-1.0.1.jar"] +# 暴露端口 8089 (对齐 application.yml) +EXPOSE 8089 + +# 运行命令,加入垃圾回收和内存优化参数,以及时区与字符编码设置 +ENTRYPOINT ["java", "-server", "-Xms512m", "-Xmx512m", "-Dfile.encoding=UTF-8", "-Duser.timezone=Asia/Shanghai", "-jar", "app.jar"] diff --git a/pom.xml b/pom.xml index d2a9b18..d356dbc 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,10 @@ java-jwt ${jwt.version} + + org.springframework.security + spring-security-crypto + org.apache.commons diff --git a/src/main/java/cn/xf/basedemo/common/enums/SystemStatus.java b/src/main/java/cn/xf/basedemo/common/enums/SystemStatus.java index 764de77..15c5d61 100644 --- a/src/main/java/cn/xf/basedemo/common/enums/SystemStatus.java +++ b/src/main/java/cn/xf/basedemo/common/enums/SystemStatus.java @@ -5,8 +5,11 @@ import lombok.Getter; @Getter public enum SystemStatus { - SUSSES(200, "请求成功"), - UNAVAILABILITY(401, "token无效"), + SUCCESS(200, "请求成功"), + USER_INPUT_ERROR(400, "参数或用户输入错误"), + UNAUTHORIZED(401, "token无效"), + FORBIDDEN(403, "禁止访问"), + TOO_FREQUENT_VISIT(429, "访问太频繁,请休息一会儿"), ERROR(500, "系统异常") ; diff --git a/src/main/java/cn/xf/basedemo/common/exception/BusinessException.java b/src/main/java/cn/xf/basedemo/common/exception/BusinessException.java index 23be9d2..65d8c56 100644 --- a/src/main/java/cn/xf/basedemo/common/exception/BusinessException.java +++ b/src/main/java/cn/xf/basedemo/common/exception/BusinessException.java @@ -1,5 +1,6 @@ package cn.xf.basedemo.common.exception; +import cn.xf.basedemo.common.enums.SystemStatus; import lombok.Getter; /** @@ -10,29 +11,29 @@ import lombok.Getter; */ @Getter public class BusinessException extends RuntimeException{ - private final ResponseCode code; + private final SystemStatus status; public BusinessException() { - super(String.format("%s", ResponseCode.INTERNAL_ERROR.getMessage())); - this.code = ResponseCode.INTERNAL_ERROR; + super(String.format("%s", SystemStatus.ERROR.getErrorMessage())); + this.status = SystemStatus.ERROR; } public BusinessException(Throwable e) { super(e); - this.code = ResponseCode.INTERNAL_ERROR; + this.status = SystemStatus.ERROR; } public BusinessException(String msg) { - this(ResponseCode.INTERNAL_ERROR, msg); + this(SystemStatus.ERROR, msg); } - public BusinessException(ResponseCode code) { - super(String.format("%s", code.getMessage())); - this.code = code; + public BusinessException(SystemStatus status) { + super(String.format("%s", status.getErrorMessage())); + this.status = status; } - public BusinessException(ResponseCode code, String msg) { + public BusinessException(SystemStatus status, String msg) { super(msg); - this.code = code; + this.status = status; } } diff --git a/src/main/java/cn/xf/basedemo/common/exception/GlobalExceptionHandler.java b/src/main/java/cn/xf/basedemo/common/exception/GlobalExceptionHandler.java index 0bcc700..42bbd7a 100644 --- a/src/main/java/cn/xf/basedemo/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/cn/xf/basedemo/common/exception/GlobalExceptionHandler.java @@ -1,5 +1,7 @@ package cn.xf.basedemo.common.exception; +import cn.xf.basedemo.common.enums.SystemStatus; +import cn.xf.basedemo.common.model.RetObj; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.validation.BindException; @@ -28,9 +30,9 @@ public class GlobalExceptionHandler { */ @ExceptionHandler(LoginException.class) @ResponseStatus(HttpStatus.FORBIDDEN) - public GenericResponse handleLoginException(LoginException e, HttpServletRequest request) { - log.warn("认证失败 [URL:{}]: code={}, message={}", request.getRequestURI(), e.getCode(), e.getMessage()); - return new GenericResponse<>(e.getCode(), null, e.getMessage()); + public RetObj handleLoginException(LoginException e, HttpServletRequest request) { + log.warn("认证失败 [URL:{}]: code={}, message={}", request.getRequestURI(), e.getStatus().getCode(), e.getMessage()); + return new RetObj<>(e.getStatus().getCode(), e.getMessage()); } /** @@ -38,9 +40,9 @@ public class GlobalExceptionHandler { */ @ExceptionHandler(BusinessException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) // 或者使用 HttpStatus.OK,根据前端约定 - public GenericResponse handleBusinessException(BusinessException e, HttpServletRequest request) { - log.warn("业务异常 [URL:{}]: code={}, message={}", request.getRequestURI(), e.getCode(), e.getMessage()); - return new GenericResponse<>(e.getCode(), null, e.getMessage()); + public RetObj handleBusinessException(BusinessException e, HttpServletRequest request) { + log.warn("业务异常 [URL:{}]: code={}, message={}", request.getRequestURI(), e.getStatus().getCode(), e.getMessage()); + return new RetObj<>(e.getStatus().getCode(), e.getMessage()); } /** @@ -48,7 +50,7 @@ public class GlobalExceptionHandler { */ @ExceptionHandler({ MethodArgumentNotValidException.class, BindException.class }) @ResponseStatus(HttpStatus.BAD_REQUEST) - public GenericResponse handleValidationException(Exception e, HttpServletRequest request) { + public RetObj handleValidationException(Exception e, HttpServletRequest request) { BindingResult bindingResult = null; if (e instanceof MethodArgumentNotValidException) { bindingResult = ((MethodArgumentNotValidException) e).getBindingResult(); @@ -64,7 +66,7 @@ public class GlobalExceptionHandler { } } log.warn("参数校验失败 [URL:{}]: {}", request.getRequestURI(), errorMsg); - return new GenericResponse<>(ResponseCode.USER_INPUT_ERROR.getCode(), null, errorMsg); + return new RetObj<>(SystemStatus.USER_INPUT_ERROR.getCode(), errorMsg); } /** @@ -72,9 +74,9 @@ public class GlobalExceptionHandler { */ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public GenericResponse handleSystemException(Exception e, HttpServletRequest request) { + public RetObj handleSystemException(Exception e, HttpServletRequest request) { // 生产级关键点:必须记录异常堆栈,否则无法排查 BUG log.error("系统发生未知异常 [URL:{}]", request.getRequestURI(), e); - return new GenericResponse<>(500, null, "系统内部繁忙,请稍后再试"); + return new RetObj<>(SystemStatus.ERROR.getCode(), "系统内部繁忙,请稍后再试"); } } diff --git a/src/main/java/cn/xf/basedemo/common/exception/LoginException.java b/src/main/java/cn/xf/basedemo/common/exception/LoginException.java index 2cdeeec..f415378 100644 --- a/src/main/java/cn/xf/basedemo/common/exception/LoginException.java +++ b/src/main/java/cn/xf/basedemo/common/exception/LoginException.java @@ -1,5 +1,6 @@ package cn.xf.basedemo.common.exception; +import cn.xf.basedemo.common.enums.SystemStatus; import lombok.Getter; /** @@ -11,30 +12,30 @@ import lombok.Getter; @Getter public class LoginException extends RuntimeException{ - private final ResponseCode code; + private final SystemStatus status; public LoginException() { - super(String.format("%s", ResponseCode.AUTHENTICATION_NEEDED.getMessage())); - this.code = ResponseCode.AUTHENTICATION_NEEDED; + super(String.format("%s", SystemStatus.UNAUTHORIZED.getErrorMessage())); + this.status = SystemStatus.UNAUTHORIZED; } public LoginException(Throwable e) { super(e); - this.code = ResponseCode.AUTHENTICATION_NEEDED; + this.status = SystemStatus.UNAUTHORIZED; } public LoginException(String msg) { - this(ResponseCode.AUTHENTICATION_NEEDED, msg); + this(SystemStatus.UNAUTHORIZED, msg); } - public LoginException(ResponseCode code) { - super(String.format("%s", code.getMessage())); - this.code = code; + public LoginException(SystemStatus status) { + super(String.format("%s", status.getErrorMessage())); + this.status = status; } - public LoginException(ResponseCode code, String msg) { + public LoginException(SystemStatus status, String msg) { super(msg); - this.code = code; + this.status = status; } } diff --git a/src/main/java/cn/xf/basedemo/common/model/RetObj.java b/src/main/java/cn/xf/basedemo/common/model/RetObj.java index 282a3c2..85a4468 100644 --- a/src/main/java/cn/xf/basedemo/common/model/RetObj.java +++ b/src/main/java/cn/xf/basedemo/common/model/RetObj.java @@ -46,20 +46,20 @@ public class RetObj { } public static RetObj success() { - return new RetObj(SystemStatus.SUSSES); + return new RetObj<>(SystemStatus.SUCCESS); } public static RetObj success(T data) { - return new RetObj(SystemStatus.SUSSES, data); + return new RetObj<>(SystemStatus.SUCCESS, data); } public static RetObj error(SystemStatus status) { - return new RetObj(status); + return new RetObj<>(status); } public static RetObj error(String errorMsg) { - return new RetObj(SystemStatus.ERROR.getCode(), errorMsg); + return new RetObj<>(SystemStatus.ERROR.getCode(), errorMsg); } } diff --git a/src/main/java/cn/xf/basedemo/common/utils/JwtTokenUtils.java b/src/main/java/cn/xf/basedemo/common/utils/JwtTokenUtils.java index ce73bc5..d70958f 100644 --- a/src/main/java/cn/xf/basedemo/common/utils/JwtTokenUtils.java +++ b/src/main/java/cn/xf/basedemo/common/utils/JwtTokenUtils.java @@ -106,7 +106,7 @@ public class JwtTokenUtils { */ public static Integer getUserId(String token) { Map claims = verifyToken(token); - if (claims != null) { + if (claims == null) { return null; } diff --git a/src/main/java/cn/xf/basedemo/common/utils/RSAUtils.java b/src/main/java/cn/xf/basedemo/common/utils/RSAUtils.java index ab2fc3d..667d729 100644 --- a/src/main/java/cn/xf/basedemo/common/utils/RSAUtils.java +++ b/src/main/java/cn/xf/basedemo/common/utils/RSAUtils.java @@ -1,8 +1,5 @@ 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.*; @@ -11,6 +8,7 @@ import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -20,7 +18,7 @@ import java.util.Map; * @description: 加密工具类 * @author: xiongfeng * @create: 2022-06-20 10:37 - **/ + * **/ public class RSAUtils { //算法类型 @@ -49,10 +47,10 @@ public class RSAUtils { kpg.initialize(ENCRYPT_SIZE); KeyPair keyPair = kpg.generateKeyPair(); PublicKey aPublic = keyPair.getPublic(); - String publicKey = Base64.encodeBase64URLSafeString(aPublic.getEncoded()); + String publicKey = Base64.getUrlEncoder().withoutPadding().encodeToString(aPublic.getEncoded()); PrivateKey aPrivate = keyPair.getPrivate(); - String privateKey = Base64.encodeBase64URLSafeString(aPrivate.getEncoded()); + String privateKey = Base64.getUrlEncoder().withoutPadding().encodeToString(aPrivate.getEncoded()); Map map = new HashMap<>(); @@ -73,7 +71,7 @@ public class RSAUtils { public static RSAPublicKey getPublicKey(String publicKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException { // 通过X509编码的Key指令获得公钥对象 KeyFactory keyFactory = KeyFactory.getInstance(RSA); - X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64URLSafe(publicKeyStr)); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getUrlDecoder().decode(publicKeyStr)); RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec); return key; } @@ -89,7 +87,7 @@ public class RSAUtils { public static RSAPrivateKey getPrivateKey(String privateKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException { KeyFactory keyFactory = KeyFactory.getInstance(RSA); - PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64URLSafe(privateKeyStr)); + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getUrlDecoder().decode(privateKeyStr)); RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8EncodedKeySpec); return privateKey; } @@ -109,8 +107,7 @@ public class RSAUtils { cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] bytes = cipher.doFinal(data.getBytes()); - return Base64.encodeBase64URLSafeString(bytes); -// return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength())); + return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); } catch (Exception e) { throw new RuntimeException(); } @@ -121,9 +118,7 @@ public class RSAUtils { 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), 0, data.getBytes(CHARSET).length), privateKey.getModulus().bitLength()), CHARSET); + return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.getUrlDecoder().decode(data.getBytes(CHARSET)), privateKey.getModulus().bitLength()), CHARSET); } catch (Exception e) { throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e); } @@ -137,11 +132,10 @@ public class RSAUtils { } else { maxBlock = keySize / 8 - 11; } - ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; - byte[] buff; int i = 0; - try { + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buff; while (datas.length > offSet) { if (datas.length - offSet > maxBlock) { //可以调用以下的doFinal()方法完成加密或解密数据: @@ -153,12 +147,10 @@ public class RSAUtils { i++; offSet = i * maxBlock; } + return out.toByteArray(); } catch (Exception e) { throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e); } - byte[] resultDatas = out.toByteArray(); - IOUtils.closeQuietly(out); - return resultDatas; } public static void main(String[] args) { diff --git a/src/main/java/cn/xf/basedemo/common/utils/RequestHeaderUtil.java b/src/main/java/cn/xf/basedemo/common/utils/RequestHeaderUtil.java index 2846697..63a9d68 100644 --- a/src/main/java/cn/xf/basedemo/common/utils/RequestHeaderUtil.java +++ b/src/main/java/cn/xf/basedemo/common/utils/RequestHeaderUtil.java @@ -52,9 +52,9 @@ public class RequestHeaderUtil { public static String getToken(HttpServletRequest request) { //登录处理 String token = request.getHeader("Authorization"); - if (StringUtils.isEmpty(token)) + if (!StringUtils.hasText(token)) token = request.getParameter("token"); - if (StringUtils.isEmpty(token)) { + if (!StringUtils.hasText(token)) { throw new LoginException("请先登录"); } diff --git a/src/main/java/cn/xf/basedemo/interceptor/TokenInterceptor.java b/src/main/java/cn/xf/basedemo/interceptor/TokenInterceptor.java index 98daf8b..0cdbff2 100644 --- a/src/main/java/cn/xf/basedemo/interceptor/TokenInterceptor.java +++ b/src/main/java/cn/xf/basedemo/interceptor/TokenInterceptor.java @@ -1,7 +1,7 @@ package cn.xf.basedemo.interceptor; import cn.xf.basedemo.common.exception.LoginException; -import cn.xf.basedemo.common.exception.ResponseCode; +import cn.xf.basedemo.common.enums.SystemStatus; import cn.xf.basedemo.common.model.LoginUser; import cn.xf.basedemo.common.utils.ApplicationContextUtils; import com.alibaba.fastjson.JSONObject; @@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit; * @description: * @author: xiongfeng * @create: 2022-06-16 14:17 + * @component **/ @Component public class TokenInterceptor implements HandlerInterceptor { @@ -45,28 +46,28 @@ public class TokenInterceptor implements HandlerInterceptor { } //登录处理 String token = request.getHeader("Authorization"); - if (StringUtils.isEmpty(token)) + if (!StringUtils.hasText(token)) token = request.getParameter("token"); - if (StringUtils.isEmpty(token)) { + if (!StringUtils.hasText(token)) { throw new LoginException("请先登录"); }else { //验证token if (!token.startsWith("Bearer ")) { - throw new LoginException(ResponseCode.USER_INPUT_ERROR); + throw new LoginException(SystemStatus.USER_INPUT_ERROR); } token = token.substring(7); } String value = (String) redisTemplate.opsForValue().get("token:" + token); - if (StringUtils.isEmpty(value)) { + if (!StringUtils.hasText(value)) { throw new LoginException(); } JSONObject jsonObject = JSONObject.parseObject(value); //JSON对象转换成Java对象 LoginUser loginUserInfo = JSONObject.toJavaObject(jsonObject, LoginUser.class); if (loginUserInfo == null || loginUserInfo.getId() <= 0) { - throw new LoginException(ResponseCode.USER_INPUT_ERROR); + throw new LoginException(SystemStatus.USER_INPUT_ERROR); } - redisTemplate.expire(token, 86700, TimeUnit.SECONDS); + redisTemplate.expire("token:" + token, 86700, TimeUnit.SECONDS); //用户信息设置到上下文 SessionContext.getInstance().set(loginUserInfo); diff --git a/src/main/java/cn/xf/basedemo/service/impl/UserServiceImpl.java b/src/main/java/cn/xf/basedemo/service/impl/UserServiceImpl.java index 8d76202..a736644 100644 --- a/src/main/java/cn/xf/basedemo/service/impl/UserServiceImpl.java +++ b/src/main/java/cn/xf/basedemo/service/impl/UserServiceImpl.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -46,10 +47,12 @@ public class UserServiceImpl implements UserService { @Autowired private RedisTemplate redisTemplate; + private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + @Override public RetObj login(LoginInfoRes res) { - if (Objects.isNull(res) || StringUtils.isEmpty(res.getEncryptedData())) { + if (Objects.isNull(res) || !StringUtils.hasText(res.getEncryptedData())) { return null; } String loginJson = ""; @@ -65,15 +68,14 @@ public class UserServiceImpl implements UserService { e.printStackTrace(); return RetObj.error("账号或密码错误"); } - if (!StringUtils.isEmpty(loginInfo.check())) { + if (StringUtils.hasText(loginInfo.check())) { return RetObj.error(loginInfo.check()); } //校验登录账号密码 - QueryWrapper queryWrapper = new QueryWrapper(); + QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("account", loginInfo.getAccount()); - queryWrapper.eq("password", loginInfo.getPwd()); User user = userMapper.selectOne(queryWrapper); - if (Objects.isNull(user)) { + if (Objects.isNull(user) || !passwordEncoder.matches(loginInfo.getPwd(), user.getPassword())) { return RetObj.error("账号或密码错误"); } LoginUser loginUser = new LoginUser(); diff --git a/src/main/resources/docker/boot-docker-compose.yml b/src/main/resources/docker/boot-docker-compose.yml index 2658886..07f3e58 100644 --- a/src/main/resources/docker/boot-docker-compose.yml +++ b/src/main/resources/docker/boot-docker-compose.yml @@ -1,12 +1,14 @@ -version: '1' +version: '3.8' services: - boot_docker_compose: - image: remaindertime/boot_docker_compose:v1.0.0 - container_name: boot_docker_compose + xf-boot-base: + build: + context: ../../../ + dockerfile: Dockerfile + container_name: xf-boot-base environment: - SERVER_ADDRESS=120.48.109.209 - MYSQL_NAME=root - MYSQL_PWD=123456 - REDIS_PWD=123456 ports: - - 8090:8088 \ No newline at end of file + - 8089:8089 \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..77c427e --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,85 @@ + + + xf-boot-base + + + + + + + + ${log_pattern} + UTF-8 + + + + + + ${log_home}/base-info.log + + ${log_home}/base-%d{yyyy-MM-dd}-info.%i.log + 200MB + 30 + 10GB + + + ${log_pattern} + UTF-8 + + + INFO + + + + + + ${log_home}/base-error.log + + ${log_home}/base-%d{yyyy-MM-dd}-error.%i.log + 200MB + 30 + 5GB + + + ${log_pattern} + UTF-8 + + + ERROR + + + + + + + + + ${logIps} + 5 minutes + 5 seconds + 30 seconds + sleeping + + {"host":"${HOSTNAME}", "app_name":"${appName}"} + + + + + + + + + + + + + + + + + + + + + +