From 04cfab2f82d07f0867df63bf777255f28103649c Mon Sep 17 00:00:00 2001 From: liulu Date: Fri, 6 Dec 2024 15:38:22 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=92=8Ctoken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chsm}/constant/SecurityConstant.java | 2 +- .../chsm/enums/ManufacturerModelEnum.java | 2 +- .../chsm/mapper/ApplicationMapper.java | 16 +++ .../main/java/com/sunyard/chsm/model/R.java | 2 +- .../com/sunyard/chsm/model/entity/Device.java | 14 ++- .../com/sunyard/chsm/utils/CodecUtils.java | 46 +++++++ chsm-params/pom.xml | 8 ++ .../com/sunyard/chsm/param/AppTokenReq.java | 33 +++++ .../com/sunyard/chsm/param/AppTokenResp.java | 15 +++ .../java/com/sunyard/chsm/dto/DeviceDTO.java | 1 + .../jwt/AuthenticationSuccessHandler.java | 2 +- .../security/jwt/JWTAuthenticationFilter.java | 2 +- .../user/controller/ScUserController.java | 2 +- .../com/sunyard/ssp/utils/SecurityUtil.java | 2 +- chsm-web-server/pom.xml | 7 ++ .../com/sunyard/chsm/auth/AppTokenFilter.java | 111 +++++++++++++++++ .../java/com/sunyard/chsm/auth/AppUser.java | 19 +++ .../chsm/{config => auth}/AuthHandler.java | 2 +- .../com/sunyard/chsm/auth/UserContext.java | 19 +++ .../chsm/config/GlobalExceptionResolver.java | 96 +++++++++++++++ .../chsm/controller/AppLoginController.java | 34 +++++- .../chsm/controller/KeyManageController.java | 39 ++++++ .../sunyard/chsm/service/AppLoginService.java | 113 ++++++++++++++++++ .../src/main/resources/application.yml | 2 +- doc/ssp_dm.sql | 24 ++-- 25 files changed, 588 insertions(+), 25 deletions(-) rename {chsm-web-manage/src/main/java/com/sunyard/ssp/common => chsm-common/src/main/java/com/sunyard/chsm}/constant/SecurityConstant.java (97%) create mode 100644 chsm-common/src/main/java/com/sunyard/chsm/utils/CodecUtils.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenReq.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenResp.java create mode 100644 chsm-web-server/src/main/java/com/sunyard/chsm/auth/AppTokenFilter.java create mode 100644 chsm-web-server/src/main/java/com/sunyard/chsm/auth/AppUser.java rename chsm-web-server/src/main/java/com/sunyard/chsm/{config => auth}/AuthHandler.java (92%) create mode 100644 chsm-web-server/src/main/java/com/sunyard/chsm/auth/UserContext.java create mode 100644 chsm-web-server/src/main/java/com/sunyard/chsm/config/GlobalExceptionResolver.java create mode 100644 chsm-web-server/src/main/java/com/sunyard/chsm/controller/KeyManageController.java create mode 100644 chsm-web-server/src/main/java/com/sunyard/chsm/service/AppLoginService.java diff --git a/chsm-web-manage/src/main/java/com/sunyard/ssp/common/constant/SecurityConstant.java b/chsm-common/src/main/java/com/sunyard/chsm/constant/SecurityConstant.java similarity index 97% rename from chsm-web-manage/src/main/java/com/sunyard/ssp/common/constant/SecurityConstant.java rename to chsm-common/src/main/java/com/sunyard/chsm/constant/SecurityConstant.java index 0265448..a56e248 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/ssp/common/constant/SecurityConstant.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/constant/SecurityConstant.java @@ -1,4 +1,4 @@ -package com.sunyard.ssp.common.constant; +package com.sunyard.chsm.constant; /** * @author Exrickx diff --git a/chsm-common/src/main/java/com/sunyard/chsm/enums/ManufacturerModelEnum.java b/chsm-common/src/main/java/com/sunyard/chsm/enums/ManufacturerModelEnum.java index 8c2cf24..e6aaf12 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/enums/ManufacturerModelEnum.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/enums/ManufacturerModelEnum.java @@ -14,7 +14,7 @@ import java.util.Objects; @AllArgsConstructor public enum ManufacturerModelEnum { - enc001(ManufacturerEnum.SUNYARD, "enc001", "服务器密码机enc001"), + enc001(ManufacturerEnum.SUNYARD, "SYD-001", "服务器密码机"), ; private final ManufacturerEnum manufacturer; diff --git a/chsm-common/src/main/java/com/sunyard/chsm/mapper/ApplicationMapper.java b/chsm-common/src/main/java/com/sunyard/chsm/mapper/ApplicationMapper.java index 480b87a..77e765d 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/mapper/ApplicationMapper.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/mapper/ApplicationMapper.java @@ -1,8 +1,13 @@ package com.sunyard.chsm.mapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.sunyard.chsm.model.entity.Application; import org.apache.ibatis.annotations.Mapper; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +import java.util.List; /** * @author liulu @@ -10,4 +15,15 @@ import org.apache.ibatis.annotations.Mapper; */ @Mapper public interface ApplicationMapper extends BaseMapper { + + + default Application selectByAppKey(String appKey) { + List apps = selectList(new QueryWrapper().eq("app_key", appKey)); + if (CollectionUtils.isEmpty(apps)) { + return null; + } + Assert.isTrue(apps.size() == 1, "app 数据异常"); + return apps.iterator().next(); + } + } diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/R.java b/chsm-common/src/main/java/com/sunyard/chsm/model/R.java index 9bd77ff..f32e69b 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/model/R.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/model/R.java @@ -72,7 +72,7 @@ public class R { R r = new R<>(); r.setSuccess(false); r.setMessage(msg); - r.setCode(500); + r.setCode(400); return r; } diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java index bdd8527..ad3aeda 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java @@ -21,16 +21,20 @@ public class Device { private Integer managePort; private String manufacturer; private String manufacturerModel; + private Integer encKeyIdx; private String accessCredentials; - - private Boolean connected; - private LocalDateTime lastCheckTime; - private LocalDateTime lastConnectedTime; - private Long groupId; private String groupName; private Integer weight; + private String tmkStatus; + private String deviceSerial; + private String pubKey; + private String encTmk; + private Boolean connected; + private LocalDateTime lastCheckTime; + private LocalDateTime lastConnectedTime; + private String remark; private LocalDateTime createTime; private LocalDateTime updateTime; diff --git a/chsm-common/src/main/java/com/sunyard/chsm/utils/CodecUtils.java b/chsm-common/src/main/java/com/sunyard/chsm/utils/CodecUtils.java new file mode 100644 index 0000000..e90cb0a --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/utils/CodecUtils.java @@ -0,0 +1,46 @@ +package com.sunyard.chsm.utils; + +import org.bouncycastle.util.encoders.Hex; + +import java.util.Base64; + +/** + * @author liulu + * @since 2024/12/6 + */ +public abstract class CodecUtils { + + public static String encodeBase64(byte[] data) { + return Base64.getEncoder().encodeToString(data); + } + + public static byte[] decodeBase64(String str) { + try { + return Base64.getDecoder().decode(str); + } catch (Exception var3) { + if (str.length() > 32) { + str = str.substring(0, 32) + "..."; + } + + String format = String.format("参数[%s]不是正确的base64格式", str); + throw new IllegalArgumentException(format); + } + } + + public static String encodeHex(byte[] data) { + return Hex.toHexString(data); + } + + public static byte[] decodeHex(String str) { + try { + return Hex.decode(str); + } catch (Exception var3) { + if (str.length() > 32) { + str = str.substring(0, 32) + "..."; + } + String format = String.format("参数[%s]不是正确的hex格式", str); + throw new IllegalArgumentException(format); + } + } + +} diff --git a/chsm-params/pom.xml b/chsm-params/pom.xml index b54c4e8..c436f97 100644 --- a/chsm-params/pom.xml +++ b/chsm-params/pom.xml @@ -16,5 +16,13 @@ 8 UTF-8 + + + + jakarta.validation + jakarta.validation-api + + + \ No newline at end of file diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenReq.java new file mode 100644 index 0000000..7429ab3 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenReq.java @@ -0,0 +1,33 @@ +package com.sunyard.chsm.param; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * @author liulu + * @since 2024/11/13 + */ +@Data +public class AppTokenReq { + + /** + * 平台分配的appKey + */ + @NotBlank + @Size(min = 16, max = 64, message = "appKey长度必须为16-64") + private String appKey; + /** + * 时间戳毫秒数,与服务器时间不能超过5分钟 + */ + @NotNull + private Long random; + /** + * 使用appKey+random+appSecret作为原文,使用appSecret作为key,根据HmacSM3算法计算得到hmac值,转为Hex编码 + */ + @NotBlank + @Size(min = 32, max = 64, message = "hmac长度在60-64之间") + private String hmac; +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenResp.java new file mode 100644 index 0000000..e6a8fd6 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenResp.java @@ -0,0 +1,15 @@ +package com.sunyard.chsm.param; + +import lombok.Data; + +/** + * @author liulu + * @since 2024/11/13 + */ +@Data +public class AppTokenResp { + /** + * 获取的token的值 + */ + private String token; +} diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/DeviceDTO.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/DeviceDTO.java index a4480c1..b9f1740 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/DeviceDTO.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/DeviceDTO.java @@ -73,6 +73,7 @@ public abstract class DeviceDTO { * 管理端口 */ private Integer managePort; + private Integer encKeyIdx; /** * 访问凭证 */ diff --git a/chsm-web-manage/src/main/java/com/sunyard/config/security/jwt/AuthenticationSuccessHandler.java b/chsm-web-manage/src/main/java/com/sunyard/config/security/jwt/AuthenticationSuccessHandler.java index 28ed363..f54a9b8 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/config/security/jwt/AuthenticationSuccessHandler.java +++ b/chsm-web-manage/src/main/java/com/sunyard/config/security/jwt/AuthenticationSuccessHandler.java @@ -2,7 +2,7 @@ package com.sunyard.config.security.jwt; import cn.hutool.core.util.StrUtil; import com.google.gson.Gson; -import com.sunyard.ssp.common.constant.SecurityConstant; +import com.sunyard.chsm.constant.SecurityConstant; import com.sunyard.ssp.modules.user.entity.ScUser; import com.sunyard.ssp.modules.user.service.IScUserService; import com.sunyard.ssp.utils.ResponseUtil; diff --git a/chsm-web-manage/src/main/java/com/sunyard/config/security/jwt/JWTAuthenticationFilter.java b/chsm-web-manage/src/main/java/com/sunyard/config/security/jwt/JWTAuthenticationFilter.java index 3a5618e..afd5b2a 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/config/security/jwt/JWTAuthenticationFilter.java +++ b/chsm-web-manage/src/main/java/com/sunyard/config/security/jwt/JWTAuthenticationFilter.java @@ -3,7 +3,7 @@ package com.sunyard.config.security.jwt; import cn.hutool.core.util.StrUtil; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import com.sunyard.ssp.common.constant.SecurityConstant; +import com.sunyard.chsm.constant.SecurityConstant; import com.sunyard.ssp.utils.ResponseUtil; import com.sunyard.ssp.vo.TokenUser; import io.jsonwebtoken.Claims; diff --git a/chsm-web-manage/src/main/java/com/sunyard/ssp/modules/user/controller/ScUserController.java b/chsm-web-manage/src/main/java/com/sunyard/ssp/modules/user/controller/ScUserController.java index 6420287..966fff5 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/ssp/modules/user/controller/ScUserController.java +++ b/chsm-web-manage/src/main/java/com/sunyard/ssp/modules/user/controller/ScUserController.java @@ -5,10 +5,10 @@ import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.sunyard.chsm.constant.SecurityConstant; import com.sunyard.ssp.common.PageVo; import com.sunyard.ssp.common.Result; import com.sunyard.ssp.common.constant.CommonConstant; -import com.sunyard.ssp.common.constant.SecurityConstant; import com.sunyard.ssp.common.enums.UserHeaderTypeEnum; import com.sunyard.ssp.modules.user.entity.ScDepartment; import com.sunyard.ssp.modules.user.entity.ScDepartmentHeader; diff --git a/chsm-web-manage/src/main/java/com/sunyard/ssp/utils/SecurityUtil.java b/chsm-web-manage/src/main/java/com/sunyard/ssp/utils/SecurityUtil.java index aaf06f1..15575a4 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/ssp/utils/SecurityUtil.java +++ b/chsm-web-manage/src/main/java/com/sunyard/ssp/utils/SecurityUtil.java @@ -2,8 +2,8 @@ package com.sunyard.ssp.utils; import cn.hutool.core.util.StrUtil; import com.google.gson.Gson; +import com.sunyard.chsm.constant.SecurityConstant; import com.sunyard.ssp.common.constant.CommonConstant; -import com.sunyard.ssp.common.constant.SecurityConstant; import com.sunyard.ssp.modules.user.entity.ScPermission; import com.sunyard.ssp.modules.user.entity.ScRole; import com.sunyard.ssp.modules.user.entity.ScUser; diff --git a/chsm-web-server/pom.xml b/chsm-web-server/pom.xml index 5df15a7..dbd0d6a 100644 --- a/chsm-web-server/pom.xml +++ b/chsm-web-server/pom.xml @@ -49,6 +49,13 @@ com.dm DmJdbcDriver + + + + io.jsonwebtoken + jjwt + + diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/auth/AppTokenFilter.java b/chsm-web-server/src/main/java/com/sunyard/chsm/auth/AppTokenFilter.java new file mode 100644 index 0000000..9f7ac3c --- /dev/null +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/auth/AppTokenFilter.java @@ -0,0 +1,111 @@ +package com.sunyard.chsm.auth; + + +import com.sunyard.chsm.model.R; +import com.sunyard.chsm.service.AppLoginService; +import com.sunyard.chsm.utils.JsonUtils; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.JwtException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; + +/** + * @author liulu + * @version V1.0 + * @since 2023/8/4 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class AppTokenFilter extends OncePerRequestFilter { + + public static final Collection WHITE_URL = Arrays.asList( + "/appUser/getAppToken", + "/appUser/getAppTokenTest" + ); + + private final AppLoginService appLoginService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String requestURI = request.getRequestURI(); + if (WHITE_URL.contains(requestURI)) { + filterChain.doFilter(request, response); + return; + } + + String tokenValue = extractToken(request); + if (ObjectUtils.isEmpty(tokenValue)) { + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.getOutputStream().write(JsonUtils.toJsonBytes(R.error("请先获取token后再访问"))); + return; + } + + try { + AppUser user = appLoginService.verifyToken(tokenValue); + request.setAttribute("APP_USER", user); + filterChain.doFilter(request, response); + } catch (ExpiredJwtException ex) { + log.warn("token已过期: {}", requestURI); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.getOutputStream().write(JsonUtils.toJsonBytes(R.error("token已过期"))); + } catch (JwtException ex) { + log.warn("token格式错误: {}", requestURI); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.getOutputStream().write(JsonUtils.toJsonBytes(R.error("token格式错误"))); + } catch (Exception ex) { + log.error("未知异常: {}", requestURI, ex); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.getOutputStream().write(JsonUtils.toJsonBytes(R.error("发生异常"))); + } + + } + + protected String extractToken(HttpServletRequest request) { + // first check the header... + String token = extractHeaderToken(request); + + // bearer type allows a request parameter as well + if (token == null) { + logger.debug("Token not found in headers. Trying request parameters."); + token = request.getParameter("access_token"); + if (token == null) { + logger.debug("Token not found in request parameters. Not an OAuth2 request."); + } + } + return token; + } + + private static final String BEARER_TYPE = "Bearer"; + + private String extractHeaderToken(HttpServletRequest request) { + Enumeration headers = request.getHeaders("Authorization"); + while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that) + String value = headers.nextElement(); + if (value.toLowerCase().startsWith(BEARER_TYPE.toLowerCase())) { + String authHeaderValue = value.substring(BEARER_TYPE.length()).trim(); + int commaIndex = authHeaderValue.indexOf(','); + if (commaIndex > 0) { + authHeaderValue = authHeaderValue.substring(0, commaIndex); + } + return authHeaderValue; + } + } + + return null; + } + +} diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/auth/AppUser.java b/chsm-web-server/src/main/java/com/sunyard/chsm/auth/AppUser.java new file mode 100644 index 0000000..d7bfaa0 --- /dev/null +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/auth/AppUser.java @@ -0,0 +1,19 @@ +package com.sunyard.chsm.auth; + +import lombok.Data; + +import java.util.List; + +/** + * @author liulu + * @since 2024/12/6 + */ +@Data +public class AppUser { + + private Long appId; + private String name; + private List serviceIds; + + +} diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/config/AuthHandler.java b/chsm-web-server/src/main/java/com/sunyard/chsm/auth/AuthHandler.java similarity index 92% rename from chsm-web-server/src/main/java/com/sunyard/chsm/config/AuthHandler.java rename to chsm-web-server/src/main/java/com/sunyard/chsm/auth/AuthHandler.java index 906e5e1..23c038a 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/config/AuthHandler.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/auth/AuthHandler.java @@ -1,4 +1,4 @@ -package com.sunyard.chsm.config; +package com.sunyard.chsm.auth; import org.springframework.web.servlet.HandlerInterceptor; diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/auth/UserContext.java b/chsm-web-server/src/main/java/com/sunyard/chsm/auth/UserContext.java new file mode 100644 index 0000000..889c10c --- /dev/null +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/auth/UserContext.java @@ -0,0 +1,19 @@ +package com.sunyard.chsm.auth; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +/** + * @author liulu + * @since 2024/12/6 + */ +public class UserContext { + + + public static AppUser getCurrentUser() { + RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); + return (AppUser) requestAttributes.getAttribute("APP_USER", RequestAttributes.SCOPE_REQUEST); + } + + +} diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/config/GlobalExceptionResolver.java b/chsm-web-server/src/main/java/com/sunyard/chsm/config/GlobalExceptionResolver.java new file mode 100644 index 0000000..5c23874 --- /dev/null +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/config/GlobalExceptionResolver.java @@ -0,0 +1,96 @@ +package com.sunyard.chsm.config; + +import com.sunyard.chsm.model.R; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.IllegalFormatException; +import java.util.List; +import java.util.Optional; + +/** + * 全局controller异常处理 + * + * @auther: ZS + * @description: + * @param: + * @return: + * @date: 2020/5/25 16:24 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionResolver { + + /** + * 校验器 + */ + @ExceptionHandler(BindException.class) + @ResponseBody + public R handleBindException(BindException e) { + log.info("校验器异常", e); + FieldError fieldError = e.getBindingResult().getFieldError(); + return R.error(400, fieldError.getDefaultMessage()); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public R validExceptionHandler(MethodArgumentNotValidException ex) { + return Optional.of(ex) + .map(MethodArgumentNotValidException::getFieldError) + .map(FieldError::getDefaultMessage) + .map(message -> R.error(400, message)) + .get(); + } + + private static final List> EX_CLASS = Arrays.asList( + IllegalArgumentException.class, IllegalStateException.class, + IllegalFormatException.class, + UnsupportedOperationException.class); + + @ExceptionHandler(Exception.class) + public R exceptionHandler(Exception ex, HttpServletRequest request) { + String errorMessage = buildErrorMessage(ex); + for (Class eClass : EX_CLASS) { + Exception exception = findException(eClass, ex); + if (exception == null) { + continue; + } + log.warn("Validation failed -> {} ", errorMessage); + return R.error(400, exception.getMessage()); + } + log.error("request: {} - {}, 系统异常 -> ", request.getMethod(), request.getRequestURI(), ex); + return R.error("系统异常"); + } + + public static T findException(Class exClass, Throwable ex) { + for (int i = 0; ex != null && i < 3; i++) { + if (exClass.isAssignableFrom(ex.getClass())) { + //noinspection unchecked + return (T) ex; + } + ex = ex.getCause(); + } + return null; + } + + public static String buildErrorMessage(Throwable ex) { + StringBuilder sb = new StringBuilder(); + sb.append(ex.getMessage()); + StackTraceElement[] stackTrace = ex.getStackTrace(); + int i = 0; + for (StackTraceElement stackTraceElement : stackTrace) { + if (i++ >= 5) { + break; + } + sb.append("\n\t").append(stackTraceElement.toString()); + } + return sb.toString(); + } + +} diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/controller/AppLoginController.java b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/AppLoginController.java index 35e73c1..3751c89 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/controller/AppLoginController.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/AppLoginController.java @@ -1,17 +1,49 @@ package com.sunyard.chsm.controller; +import com.sunyard.chsm.param.AppTokenReq; +import com.sunyard.chsm.param.AppTokenResp; +import com.sunyard.chsm.service.AppLoginService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.Resource; +import javax.validation.Valid; + /** * 应用Token服务接口 * * @author liulu * @version V1.0 - * @since 2023/8/4 + * @since 2024/12/4 */ @RestController @RequestMapping public class AppLoginController { + @Resource + private AppLoginService appLoginService; + + + /** + * 获取应用Token接口 + * 密码服务平台为各个接入应用提供生成应用授权Token, + * 接入应用需在密码服务平台获取了到平台分配的appKey和appSecret + * + * @param appTokenReq 请求参数 + * @return + */ + @PostMapping("/appUser/getAppToken") + public AppTokenResp getAppToken(@Valid @RequestBody AppTokenReq appTokenReq) { + return appLoginService.getAppToken(appTokenReq); + } + + @PostMapping("/appUser/getAppTokenTest") + public AppTokenResp getAppTokenForTest(@Valid @RequestBody AppTokenReq appTokenReq) { + return appLoginService.getAppTokenForTest(appTokenReq); + } + + + } diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/controller/KeyManageController.java b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/KeyManageController.java new file mode 100644 index 0000000..db71e36 --- /dev/null +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/KeyManageController.java @@ -0,0 +1,39 @@ +package com.sunyard.chsm.controller; + +import com.sunyard.chsm.model.R; +import com.sunyard.chsm.model.dto.KeyInfoDTO; +import com.sunyard.chsm.service.KeyInfoService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * 密钥管理类接口 + * + * @author liulu + * @since 2024/12/6 + */ +@RestController +@RequestMapping("/key/manage") +public class KeyManageController { + + @Resource + private KeyInfoService keyInfoService; + + /** + * 启用密钥 + * + * @param param 密钥id + * @return id + */ + @PostMapping("/enable") + public R enableKey(@Validated @RequestBody KeyInfoDTO.IDs param) { + keyInfoService.enableKey(param.getIds()); + return R.ok(); + } + +} diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/service/AppLoginService.java b/chsm-web-server/src/main/java/com/sunyard/chsm/service/AppLoginService.java new file mode 100644 index 0000000..d7ab74c --- /dev/null +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/service/AppLoginService.java @@ -0,0 +1,113 @@ +package com.sunyard.chsm.service; + +import com.sunyard.chsm.auth.AppUser; +import com.sunyard.chsm.constant.SecurityConstant; +import com.sunyard.chsm.enums.EnableStatus; +import com.sunyard.chsm.mapper.ApplicationMapper; +import com.sunyard.chsm.model.entity.Application; +import com.sunyard.chsm.param.AppTokenReq; +import com.sunyard.chsm.param.AppTokenResp; +import com.sunyard.chsm.utils.CodecUtils; +import com.sunyard.chsm.utils.gm.BCSM3Utils; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +/** + * @author liulu + * @since 2024/12/5 + */ +@Slf4j +@Service +public class AppLoginService { + + /** + * token 过期时间, 分钟 + */ + @Value("${chsm.token.expireTime:720}") + private Integer tokenExpireTime; + @Resource + private ApplicationMapper applicationMapper; + + public AppTokenResp getAppToken(AppTokenReq req) { + Long random = req.getRandom(); + long now = System.currentTimeMillis(); + Assert.isTrue(now - random <= 5 * 60 * 1000, "请求已超时"); + String appKey = req.getAppKey(); + Application application = applicationMapper.selectByAppKey(appKey); + Assert.isTrue(EnableStatus.ENABLED.getCode().equals(application.getStatus()), "此应用已停用"); + String data = appKey + random + application.getAppSecret(); + byte[] hmac = BCSM3Utils.hmac(application.getAppSecret().getBytes(), data.getBytes()); + String serverHmac = CodecUtils.encodeHex(hmac); + if (!Objects.equals(req.getHmac(), serverHmac)) { + log.warn("appKey: {}, req hmac: {}, server hmac: {}", appKey, req.getHmac(), serverHmac); + throw new IllegalArgumentException("应用认证失败"); + } + AppTokenResp resp = new AppTokenResp(); + resp.setToken(genToken(application)); + return resp; + } + + private String genToken(Application app) { + Map claims = new HashMap<>(); + claims.put("appId", app.getId()); + claims.put("name", app.getName()); + claims.put("serviceIds", Collections.singletonList(app.getId())); + Date now = new Date(); + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) + .setClaims(claims) + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + tokenExpireTime * 60 * 1000)) + .setId(UUID.randomUUID().toString()) + .signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY) + .compact(); + } + + public AppTokenResp getAppTokenForTest(@Valid AppTokenReq req) { + +// Long random = req.getRandom(); +// long now = System.currentTimeMillis(); +// Assert.isTrue(now - random <= 5 * 60 * 1000, "请求已超时"); + Application application = applicationMapper.selectByAppKey(req.getAppKey()); + Assert.isTrue(EnableStatus.ENABLED.getCode().equals(application.getStatus()), "此应用已停用"); + if (!Objects.equals(req.getHmac(), application.getAppSecret())) { + log.warn("appKey: {}, req hmac: {},", req.getAppKey(), req.getHmac()); + throw new IllegalArgumentException("应用认证失败"); + } + AppTokenResp resp = new AppTokenResp(); + resp.setToken(genToken(application)); + return resp; + } + + + public AppUser verifyToken(String token) { + Claims claims = Jwts.parser() + .setSigningKey(SecurityConstant.JWT_SIGN_KEY) + .parseClaimsJws(token) + .getBody(); + + AppUser user = new AppUser(); + user.setAppId(claims.get("appId", Long.class)); + user.setName(claims.get("name", String.class)); + user.setServiceIds(claims.get("serviceIds", List.class)); + return user; + } + + +} diff --git a/chsm-web-server/src/main/resources/application.yml b/chsm-web-server/src/main/resources/application.yml index 6bced08..a0236b6 100644 --- a/chsm-web-server/src/main/resources/application.yml +++ b/chsm-web-server/src/main/resources/application.yml @@ -12,7 +12,7 @@ spring: # 数据源 datasource: driverClassName: dm.jdbc.driver.DmDriver - url: jdbc:dm://172.16.18.44:5236?schema=SSP&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8 + url: jdbc:dm://172.16.18.44:5236?schema=SUNYARD_SSP&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8 username: SUNYARD # Jasypt加密 可到common-utils中找到JasyptUtil加解密工具类生成加密结果 格式为ENC(加密结果) password: 123456 diff --git a/doc/ssp_dm.sql b/doc/ssp_dm.sql index 66ac042..44d9e2e 100644 --- a/doc/ssp_dm.sql +++ b/doc/ssp_dm.sql @@ -344,22 +344,26 @@ INSERT INTO SC_ROLE_PERMISSION (PERMISSION_ID, ROLE_ID) VALUES (189, 1); -- 密码设备 CREATE TABLE sp_device ( id BIGINT NOT NULL COMMENT 'id', - name VARCHAR(255) COMMENT '名称', - device_number VARCHAR(255) COMMENT '编号', - manufacturer VARCHAR(255) COMMENT '制造厂商', - manufacturer_model VARCHAR(255) COMMENT '制造厂商型号', - service_ip VARCHAR(30) COMMENT '服务ip', + name VARCHAR(255) DEFAULT '' COMMENT '名称', + manufacturer VARCHAR(255) DEFAULT '' COMMENT '制造厂商', + manufacturer_model VARCHAR(255) DEFAULT '' COMMENT '制造厂商型号', + service_ip VARCHAR(30) DEFAULT '' COMMENT '服务ip', service_port INT COMMENT '服务端口', - manage_ip VARCHAR(30) COMMENT '管理ip', + manage_ip VARCHAR(30) DEFAULT '' COMMENT '管理ip', manage_port INT COMMENT '管理端口', - access_credentials VARCHAR(1000) COMMENT '访问凭证', - connected TINYINT NOT NULL DEFAULT 0, - last_connected_time TIMESTAMP , - last_check_time TIMESTAMP , + enc_key_idx INT COMMENT '加密密钥索引', + access_credentials VARCHAR(255) DEFAULT '' COMMENT '访问凭证', status VARCHAR(25) DEFAULT '' COMMENT '设备状态', group_id BIGINT NOT NULL DEFAULT 0 COMMENT '设备组id', group_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '设备组名称', weight INT DEFAULT 1 COMMENT '负载时权重', + tmk_status VARCHAR(25) DEFAULT '' COMMENT 'tmk状态', + device_serial VARCHAR(25) NOT NULL DEFAULT '' COMMENT '设备序列号', + pub_key VARCHAR(400) NOT NULL DEFAULT '' COMMENT '设备公钥', + enc_tmk VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'tmk密文', + connected TINYINT NOT NULL DEFAULT 0, + last_connected_time TIMESTAMP , + last_check_time TIMESTAMP , remark VARCHAR(500) NOT NULL DEFAULT '' COMMENT '备注', update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),