Pārlūkot izejas kodu

app端项目初始化

xudm 2 mēneši atpakaļ
revīzija
23f25e658c

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 157 - 0
pom.xml

@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.3.6</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.xs</groupId>
+    <artifactId>tg-game-core</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>xs-tg-game</name>
+    <description>xs-tg-game</description>
+    <properties>
+        <java.version>17</java.version>
+        <mybatis-plus.version>3.5.9</mybatis-plus.version>
+        <sa-token.version>1.39.0</sa-token.version>
+        <mysql-connector.version>8.4.0</mysql-connector.version>
+        <hutool.version>5.8.25</hutool.version>
+        <fastjson2.version>2.0.53</fastjson2.version>
+        <knife4j.version>4.4.0</knife4j.version>
+        <redisson.version>3.36.0</redisson.version>
+        <mybatis-plus.version>3.5.9</mybatis-plus.version>
+        <velocity-engine.version>2.4.1</velocity-engine.version>
+        <mysql-connector.version>8.4.0</mysql-connector.version>
+        <esapi.version>2.5.3.1</esapi.version>
+        <bcprov-jdk15to18.version>1.76</bcprov-jdk15to18.version>
+        <bcprov-jdk15on.version>1.70</bcprov-jdk15on.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.tomcat.embed</groupId>
+                    <artifactId>tomcat-embed-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!--  redis      -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+
+        <!--  mybatis-plus      -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+            <version>${mybatis-plus.version}</version>
+        </dependency>
+
+        <!-- mysql数据库驱动 -->
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <version>${mysql-connector.version}</version>
+
+        </dependency>
+
+        <!--分布式锁-->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+            <version>${redisson.version}</version>
+        </dependency>
+
+        <!-- sa-token 权限认证, 在线文档:https://sa-token.cc/ -->
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-spring-boot3-starter</artifactId>
+            <version>${sa-token.version}</version>
+        </dependency>
+
+
+        <!-- Sa-Token 整合 Redis (使用 fastjson2 序列化方式) -->
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-redis-fastjson2</artifactId>
+            <version>${sa-token.version}</version>
+        </dependency>
+
+
+        <!--  hutool  -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+
+        <!-- fastjson2           -->
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2-extension-spring6</artifactId>
+            <version>${fastjson2.version}</version>
+        </dependency>
+
+        <!--   kinf4j接口文档集成         -->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
+            <version>${knife4j.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.owasp.esapi</groupId>
+            <artifactId>esapi</artifactId>
+            <version>${esapi.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15to18</artifactId>
+            <version>${bcprov-jdk15to18.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>${bcprov-jdk15on.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 13 - 0
src/main/java/com/xs/core/XsTgGameApplication.java

@@ -0,0 +1,13 @@
+package com.xs.core;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class XsTgGameApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(XsTgGameApplication.class, args);
+    }
+
+}

+ 23 - 0
src/main/java/com/xs/core/controller/user/LoginController.java

@@ -0,0 +1,23 @@
+package com.xs.core.controller.user;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.dev33.satoken.stp.SaTokenInfo;
+import com.alibaba.fastjson2.JSON;
+import com.xs.core.model.user.req.AppLoginReq;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@RestController
+@RequestMapping("/login")
+public class LoginController {
+    @SaIgnore
+    @PostMapping("/coinLogin")
+    @Operation(summary = "tg账号登录", description = "根据tg用户id进行登录")
+    public AppLoginReq coinLogin(@Validated @RequestBody AppLoginReq req) {
+        log.info(JSON.toJSONString(req));
+        return req;
+    }
+}

+ 239 - 0
src/main/java/com/xs/core/filter/RequestHandler.java

@@ -0,0 +1,239 @@
+package com.xs.core.filter;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.xs.core.model.ResponseResult;
+import com.xs.core.request.BodyRequestWrapper;
+import com.xs.core.request.RequestWrapper;
+import com.xs.core.utils.SecurityUtil;
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.owasp.esapi.ESAPI;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.regex.Pattern;
+
+import static java.util.regex.Pattern.*;
+
+/**
+ * 请求加解密过滤器
+ */
+@Slf4j
+@Component
+public class RequestHandler implements Filter {
+
+    @Value("${encryption.isEncryption}")
+    private boolean isEncryption;
+
+    @Value("${encryption.requestPrivateKey}")
+    private String requestPrivateKey;
+
+    @Value("${encryption.responsePublicKey}")
+    private String responsePublicKey;
+
+    @Value("${xssFilter.isOpenXssFilter}")
+    private boolean isOpenXssFilter;
+
+    @Value("${xssFilter.xssFilterWhileUrl}")
+    private String xssFilterWhileUrl;
+
+//    @Resource
+//    RedisService redisService;
+
+    /**
+     * 进行请求加密
+     */
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        byte[] results = _getErrorBytes();
+        // 头攻击检测  过滤主机名(非白名单中的直接返回 403)
+        String serverName = request.getServerName();
+        if (serverName == null || !checkBlankList(serverName)) {
+            log.info("加密解析===》[serverName deny access tips]->" + serverName);
+            servletResponse.setContentType("application/json; charset=UTF-8");
+            servletResponse.getOutputStream().write(results);
+            return;
+        }
+
+        request.setAttribute("startTime", System.currentTimeMillis());
+        request.setAttribute("encryptRequestParams", "");
+
+        if (StringUtils.isNotBlank(request.getContentType()) &&
+                request.getContentType().contains("application/json")) {
+            RequestWrapper requestWrapper = new RequestWrapper(request);
+            // 拿到加密串
+            String data = requestWrapper.getBody();
+            if (isEncryption) {
+                if (StringUtils.isBlank(data)) {
+                    chain.doFilter(requestWrapper, response);
+                    return;
+                }
+                // 开启加密后,需要过滤Knife4j过来的请求
+                ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+                HttpServletRequest httpRequest = requestAttributes.getRequest();
+                if (StringUtils.isNotBlank(httpRequest.getHeader("Knife4j-Gateway-Code"))) {
+                    chain.doFilter(requestWrapper, response);
+                    return;
+                }
+                data = data.replaceAll("\\s", "");
+
+                request.setAttribute("encryptRequestParams", data);
+
+                JSONObject json = JSONObject.parseObject(data);
+                // 验证签名
+                if (!SecurityUtil.verifySign(json.getString("encryptData"), json.getString("identifying"))) {
+                    log.error("加密解析===》签名有误!");
+                    servletResponse.setContentType("application/json; charset=UTF-8");
+                    servletResponse.getOutputStream().write(results);
+                    return;
+                }
+                // 解密
+                // 临时处理,后续删除
+                String source = json.getString("encryptData");
+                if (source.startsWith("04")) {
+                    source = source.substring(2);
+                }
+                String result = SecurityUtil.decryptSM2(requestPrivateKey, source);
+                // 请求有效性校验
+                JSONObject requestJson = JSONObject.parseObject(result);
+                if (!requestJson.containsKey("basicData")) {
+                    log.error("加密解析===》请求参数有误!");
+                    servletResponse.setContentType("application/json; charset=UTF-8");
+                    servletResponse.getOutputStream().write(results);
+                    return;
+                }
+                JSONObject basicJson = requestJson.getJSONObject("basicData");
+                if (!basicJson.containsKey("timeStamp") || !basicJson.containsKey("messageId")) {
+                    log.error("加密解析===》请求参数有误!");
+                    servletResponse.setContentType("application/json; charset=UTF-8");
+                    servletResponse.getOutputStream().write(results);
+                    return;
+                }
+                // 比对时间
+                LocalDateTime currTime = LocalDateTime.now();
+                LocalDateTime targetTime = LocalDateTimeUtil.parse(basicJson.getString("timeStamp"), DatePattern.PURE_DATETIME_PATTERN);
+                Duration between = LocalDateTimeUtil.between(currTime, targetTime);
+                long minutes = between.toMinutes();
+                if (minutes > 5L) {
+                    log.error("加密解析===》更新当前设备时间为北京时间!");
+                    servletResponse.setContentType("application/json; charset=UTF-8");
+                    servletResponse.getOutputStream().write(results);
+                    return;
+                }
+                // 验证请求唯一性
+                // --------
+                // 需要优化
+//                String key = String.format("%s%s", ConstantConfig.MESSAGE_ID_PREFIX, basicJson.getString("messageId"));
+//                if (redisService.hasKey(key)) {
+//                    log.error("加密解析===》已经接收过该请求!");
+//                    servletResponse.setContentType("application/json; charset=UTF-8");
+//                    servletResponse.getOutputStream().write(results);
+//                    return;
+//                }
+//                redisService.setNx(key, "0", 60 * 60 * 8);
+                // --------
+                if (!requestJson.containsKey("bizData") ||
+                        StringUtils.isBlank(requestJson.getString("bizData"))) {
+                    request = new BodyRequestWrapper(request, "[]");
+                } else {
+                    request = new BodyRequestWrapper(request, _cleanXSS(requestJson.getString("bizData"), request));
+                }
+            } else {
+                request = new BodyRequestWrapper(request, _cleanXSS(data, request));
+            }
+            chain.doFilter(request, response);
+        } else {
+            chain.doFilter(request, response);
+        }
+    }
+
+    private String _cleanXSS(String value, HttpServletRequest request) {
+        if (isOpenXssFilter && null != value &&
+                _judgeUrl(request.getRequestURI().replaceAll(request.getContextPath(), ""))) {
+            ESAPI.initialize("org.owasp.esapi.reference.DefaultSecurityConfiguration");
+            // 推荐使用ESAPI库来避免脚本攻击
+            value = ESAPI.encoder().canonicalize(value);
+            // 避免空字符串
+            value = value.replaceAll("", "");
+            // 避免script 标签
+            Pattern scriptPattern = compile("<script>(.*?)</script>", CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 避免src形式的表达式
+            scriptPattern = compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", CASE_INSENSITIVE | MULTILINE | DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 删除单个的 </script> 标签
+            scriptPattern = compile("</script>", CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 删除单个的<script ...> 标签
+            scriptPattern = compile("<script(.*?)>", CASE_INSENSITIVE | MULTILINE | DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 避免 eval(...) 形式表达式
+            scriptPattern = compile("eval\\((.*?)\\)", CASE_INSENSITIVE | MULTILINE | DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 避免 expression(...) 表达式
+            scriptPattern = compile("expression\\((.*?)\\)", CASE_INSENSITIVE | MULTILINE | DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 避免 javascript: 表达式
+            scriptPattern = compile("javascript:", CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 避免 vbscript: 表达式
+            scriptPattern = compile("vbscript:", CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 避免 onload= 表达式
+            scriptPattern = compile("onload(.*?)=", CASE_INSENSITIVE | MULTILINE | DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // 避免 onXX= 表达式
+            scriptPattern = compile("on.*(.*?)=", CASE_INSENSITIVE | MULTILINE | DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+        }
+        return value;
+    }
+
+    private boolean _judgeUrl(String url) {
+        if (StringUtils.isNotBlank(xssFilterWhileUrl)) {
+            String[] whiteUrls = xssFilterWhileUrl.split(",");
+            for (String whiteUrl : whiteUrls) {
+                if (url.startsWith(whiteUrl.replaceAll("\\*", ""))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private byte[] _getErrorBytes() {
+        if (isEncryption) {
+            String result = SecurityUtil.encryptSM2(responsePublicKey, JSON.toJSONString(ResponseResult.failed("error!")));
+            String sign = SecurityUtil.getSign(result);
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("encryptData", result);
+            jsonObject.put("identifying", sign);
+            return jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8);
+        } else {
+            return JSON.toJSONString(ResponseResult.failed("error!")).getBytes(StandardCharsets.UTF_8);
+        }
+    }
+
+    //判断主机是否存在白名单中
+    private boolean checkBlankList(String serverName) {
+//        if(serverName.equals("127.0.0.1"||serverName.equals("localhost")){//此处为自己网站的主机地址
+//            return true;
+//        }
+        return true;
+    }
+
+}

+ 86 - 0
src/main/java/com/xs/core/filter/ResponseHandler.java

@@ -0,0 +1,86 @@
+package com.xs.core.filter;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.util.ReflectUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.JSONWriter;
+import com.alibaba.fastjson2.filter.ContextValueFilter;
+import com.xs.core.utils.SecurityUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import java.lang.reflect.Field;
+import java.time.LocalDateTime;
+
+/**
+ * 响应加解密拦截器
+ */
+@Component
+@ControllerAdvice
+@Order(value = 20)
+public class ResponseHandler implements ResponseBodyAdvice<Object> {
+
+    @Value("${encryption.isEncryption}")
+    private boolean isEncryption;
+
+    @Value("${encryption.responsePublicKey}")
+    private String responsePublicKey;
+
+    /**
+     * 返回true,才会走beforeBodyWrite方法
+     */
+    @Override
+    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
+        return true;
+    }
+
+    /**
+     * 响应加密
+     */
+    @Override
+    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse serverHttpResponse) {
+        if (isEncryption) {
+            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            HttpServletRequest httpRequest = requestAttributes.getRequest();
+            if (StringUtils.isNotBlank(httpRequest.getHeader("Knife4j-Gateway-Code"))) {
+                return body;
+            }
+//            JSON.configWriterDateFormat(DatePattern.NORM_DATETIME_PATTERN);
+            // 拿到响应的数据
+            String json = JSON.toJSONString(body, (ContextValueFilter) (context, object, name, value) -> {
+                        Field field = ReflectUtil.getField(object.getClass(), name);
+                        //处理日期类型为空时返回空字符串
+                        if (null != field && value == null && LocalDateTime.class.isAssignableFrom(field.getType())) {
+                            return "";
+                        }
+                        return value;
+                    },
+                    JSONWriter.Feature.PrettyFormat,
+                    JSONWriter.Feature.WriteNullStringAsEmpty,
+                    JSONWriter.Feature.WriteNullListAsEmpty,
+                    JSONWriter.Feature.WriteMapNullValue,
+                    JSONWriter.Feature.WriteNulls);
+            // 进行加密
+            String result = SecurityUtil.encryptSM2(responsePublicKey, json);
+            String sign = SecurityUtil.getSign(result);
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("encryptData", result);
+            jsonObject.put("identifying", sign);
+            return jsonObject;
+        }
+        return body;
+    }
+}

+ 11 - 0
src/main/java/com/xs/core/model/IErrorCode.java

@@ -0,0 +1,11 @@
+package com.xs.core.model;
+
+/**
+ * @author zxw
+ * 封装API的错误码
+ */
+public interface IErrorCode {
+    Integer getCode();
+
+    String getMsg();
+}

+ 154 - 0
src/main/java/com/xs/core/model/ResponseResult.java

@@ -0,0 +1,154 @@
+package com.xs.core.model;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author zxw
+ * 通用返回对象
+ */
+@Data
+public class ResponseResult<T> implements Serializable {
+    public static final long serialVersionUID = 42L;
+
+    private Integer code;
+
+    private String msg;
+
+    private T data;
+
+    protected ResponseResult() {
+
+    }
+
+    protected ResponseResult(Integer code, String msg, T data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    protected ResponseResult(Integer code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    /**
+     * 成功返回结果
+     *
+     * @param data 获取的数据
+     */
+    public static <T> ResponseResult<T> success(T data) {
+        return new ResponseResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), data);
+    }
+
+    /**
+     * 成功返回结果
+     *
+     * @param data 获取的数据
+     * @param msg  提示信息
+     */
+    public static <T> ResponseResult<T> success(T data, String msg) {
+        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), msg, data);
+    }
+
+    /**
+     * 成功返回结果
+     *
+     * @param msg 提示信息
+     */
+    public static <T> ResponseResult<T> success(String msg) {
+        return new ResponseResult<>(ResultCode.SUCCESS.getCode(), msg);
+    }
+
+    /**
+     * 失败返回结果
+     *
+     * @param errorCode 错误码
+     */
+    public static <T> ResponseResult<T> failed(IErrorCode errorCode) {
+        return new ResponseResult<>(errorCode.getCode(), errorCode.getMsg(), null);
+    }
+
+    /**
+     * 失败返回结果
+     *
+     * @param errorCode 错误码
+     * @param msg       错误信息
+     */
+    public static <T> ResponseResult<T> failed(IErrorCode errorCode, String msg) {
+        return new ResponseResult<>(errorCode.getCode(), msg, null);
+    }
+
+    /**
+     * 失败返回结果
+     *
+     * @param msg 提示信息
+     */
+    public static <T> ResponseResult<T> failed(String msg) {
+        return new ResponseResult<>(ResultCode.FAILED.getCode(), msg, null);
+    }
+
+    /**
+     * 失败返回结果
+     */
+    public static <T> ResponseResult<T> failed() {
+        return failed(ResultCode.FAILED);
+    }
+
+    /**
+     * 未登录返回结果
+     */
+    public static <T> ResponseResult<T> unauthorized(T data) {
+        return new ResponseResult<>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMsg(), data);
+    }
+
+    /**
+     * 未授权返回结果
+     */
+    public static <T> ResponseResult<T> forbidden(T data) {
+        return new ResponseResult<>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMsg(), data);
+    }
+
+    /**
+     * 未授权返回结果
+     */
+    public static <T> ResponseResult<T> forbidden() {
+        return forbidden(null);
+    }
+
+    /**
+     * 请求类型或参数错误
+     */
+    public static <T> ResponseResult<T> badRequest(T data) {
+        return new ResponseResult<>(ResultCode.BAD_REQUEST.getCode(), ResultCode.BAD_REQUEST.getMsg(), data);
+    }
+
+    /**
+     * 请求类型或参数错误
+     */
+    public static <T> ResponseResult<T> badRequest() {
+        return badRequest(null);
+    }
+
+    /**
+     * 请求报文过大
+     */
+    public static <T> ResponseResult<T> requestTooLarge(T data) {
+        return new ResponseResult<>(ResultCode.REQUEST_ENTITY_TOO_LARGE.getCode(), ResultCode.REQUEST_ENTITY_TOO_LARGE.getMsg(), data);
+    }
+
+    /**
+     * 请求报文过大
+     */
+    public static <T> ResponseResult<T> unsupportedMediaType(T data) {
+        return new ResponseResult<>(ResultCode.UNSUPPORTED_MEDIA_TYPE.getCode(), ResultCode.UNSUPPORTED_MEDIA_TYPE.getMsg(), data);
+    }
+
+    /**
+     * 服务器未知错误
+     */
+    public static <T> ResponseResult<T> serverError(T data) {
+        return new ResponseResult<>(ResultCode.SERVER_ERROR.getCode(), ResultCode.SERVER_ERROR.getMsg(), data);
+    }
+}

+ 38 - 0
src/main/java/com/xs/core/model/ResultCode.java

@@ -0,0 +1,38 @@
+package com.xs.core.model;
+
+/**
+ *  @author zxw
+ * 枚举了一些常用API操作码
+ */
+public enum ResultCode implements IErrorCode {
+    SUCCESS(1000, "操作成功"),
+    FAILED(500, "操作失败"),
+
+    NOT_FOUND(404, "接口未找到"),
+    UNAUTHORIZED(401, "暂未登录或token已经过期"),
+    FORBIDDEN(403, "没有相关权限"),
+
+    BAD_REQUEST(400, "BAD_REQUEST"),
+    REQUEST_ENTITY_TOO_LARGE(413, "上传文件过大或请求报文过长"),
+
+    SERVER_ERROR(500, "服务器内部错误"),
+
+    UNSUPPORTED_MEDIA_TYPE(501, "服务器内部错误"),
+
+    METHOD_NOT_ALLOWED(405, "请求方式错误");
+    private final Integer code;
+    private final String msg;
+
+    private ResultCode(Integer code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 38 - 0
src/main/java/com/xs/core/model/user/req/AppLoginReq.java

@@ -0,0 +1,38 @@
+package com.xs.core.model.user.req;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Schema(description = "app端登录参数")
+@Data
+public class AppLoginReq {
+
+    @Schema(description = "", example = "true")
+    private boolean allowsWriteToPm;
+
+    @Schema(description = "名", example = "st")
+    private String firstName;
+    @Schema(description = "姓", example = "prpr")
+    private String lastName;
+
+    @Schema(description = "tg小程序用户id", example = "7678353292")
+    @NotBlank(message = "tg小程序用户id不能为空")
+    private String id;
+
+    @Schema(description = "语言编码", example = "zh-hans")
+    private String languageCode;
+
+    @Schema(description = "头像地址", example = "https%3A%2F%2Ft.me%2Fi%2Fuserpic%2F320%2F8CkFbucM04aA4Q1DYkGM_CSPabAPuL9e24ywa-bD4uLSil5_TxdNx_KrHD-SnS6v.svg")
+    private String photoUrl;
+
+    @Schema(description = "tg用户名", example = "baptistUnilamellar")
+    private String username;
+
+
+    @Schema(description = "年限", example = "1")
+    private String age_limit;
+
+    @Schema(description = "邀请码")
+    private String share_code;
+}

+ 59 - 0
src/main/java/com/xs/core/request/BodyRequestWrapper.java

@@ -0,0 +1,59 @@
+package com.xs.core.request;
+
+
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * 用来重新封装request
+ */
+public class BodyRequestWrapper extends HttpServletRequestWrapper {
+
+    /**
+     * 存放JSON数据主体
+     */
+    private String body;
+
+    public BodyRequestWrapper(HttpServletRequest request, String context) {
+        super(request);
+        body = context;
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("UTF-8"));
+        return new ServletInputStream() {
+            @Override
+            public int read() throws IOException {
+                return byteArrayInputStream.read();
+            }
+
+            @Override
+            public boolean isFinished() {
+                return false;
+            }
+
+            @Override
+            public boolean isReady() {
+                return false;
+            }
+
+            @Override
+            public void setReadListener(ReadListener listener) {
+
+            }
+        };
+    }
+
+    @Override
+    public BufferedReader getReader() throws IOException {
+        return new BufferedReader(new InputStreamReader(this.getInputStream()));
+    }
+}

+ 90 - 0
src/main/java/com/xs/core/request/RequestWrapper.java

@@ -0,0 +1,90 @@
+package com.xs.core.request;
+
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+
+import java.io.*;
+
+
+/**
+ * 用来读取body
+ */
+public class RequestWrapper extends HttpServletRequestWrapper {
+    private final String body;
+
+    public RequestWrapper(HttpServletRequest request) {
+        super(request);
+        StringBuilder stringBuilder = new StringBuilder();
+        BufferedReader bufferedReader = null;
+        InputStream inputStream = null;
+        try {
+            inputStream = request.getInputStream();
+            if (inputStream != null) {
+                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
+                char[] charBuffer = new char[128];
+                int bytesRead = -1;
+                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
+                    stringBuilder.append(charBuffer, 0, bytesRead);
+                }
+            } else {
+                stringBuilder.append("");
+            }
+        } catch (IOException ex) {
+
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (bufferedReader != null) {
+                try {
+                    bufferedReader.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        body = stringBuilder.toString();
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
+        ServletInputStream servletInputStream = new ServletInputStream() {
+            @Override
+            public boolean isFinished() {
+                return false;
+            }
+
+            @Override
+            public boolean isReady() {
+                return false;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener) {
+            }
+
+            @Override
+            public int read() throws IOException {
+                return byteArrayInputStream.read();
+            }
+        };
+        return servletInputStream;
+
+    }
+
+    @Override
+    public BufferedReader getReader() throws IOException {
+        return new BufferedReader(new InputStreamReader(this.getInputStream()));
+    }
+
+    public String getBody() {
+        return this.body;
+    }
+}

+ 187 - 0
src/main/java/com/xs/core/utils/SecurityUtil.java

@@ -0,0 +1,187 @@
+package com.xs.core.utils;
+
+import cn.hutool.core.util.HexUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.crypto.BCUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.SmUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.SM2;
+import cn.hutool.jwt.JWTUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.bouncycastle.crypto.engines.SM2Engine;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+
+public class SecurityUtil {
+
+    private static final String OPEN_KEY = "8d0915a5d5b4c923";
+
+    /**
+     * 生成秘钥对
+     *
+     * @return 公钥和私钥
+     */
+    public static Map<String, String> generatorSM2Key() {
+        SM2 sm2 = SmUtil.sm2();
+        String publicKey = HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false)).toUpperCase(Locale.ENGLISH);
+        String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey())).toUpperCase(Locale.ENGLISH);
+        return new HashMap<>(2) {{
+            put("publicKey", publicKey);
+            put("privateKey", privateKey);
+        }};
+    }
+
+    public static SecretKey generateSHA256Key() throws NoSuchAlgorithmException {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA256");
+        return keyGenerator.generateKey();
+    }
+
+    /**
+     * 加密
+     *
+     * @param publicKey 公钥
+     * @param data      明文
+     * @return 密文
+     */
+    public static String encryptSM2(String publicKey, String data) {
+        return SmUtil.sm2(null, publicKey)
+                // 不写默认就是C1C3C2
+                .setMode(SM2Engine.Mode.C1C3C2)
+                .encryptHex(data.getBytes(), KeyType.PublicKey)
+                // 加密后,密文前面会有04,需要去掉
+                .substring(2);
+    }
+
+    /**
+     * 解密
+     *
+     * @param privateKey 私钥
+     * @param data       密文
+     * @return 明文
+     */
+    public static String decryptSM2(String privateKey, String data) {
+        // 确定前端不会加04,所以后端直接加(上面处理方式可能造成报错(Invalid point coordinates):原因前端加密后密文自带04开头)
+//        if (!data.startsWith("04")){
+//            data = "04" + data;
+//        }
+        data = "04" + data;
+        return SmUtil.sm2(privateKey, null)
+                // 不写默认就是C1C3C2
+                .setMode(SM2Engine.Mode.C1C3C2)
+                .decryptStr(data, KeyType.PrivateKey);
+    }
+
+    /**
+     * 验证签名是否正确
+     *
+     * @param source sm2加密后的字符串
+     * @param sign   签名字符串
+     * @return 结果
+     */
+    public static boolean verifySign(String source, String sign) {
+        if (StringUtils.isNotBlank(source) && StringUtils.isNotBlank(sign)) {
+            String target = SecureUtil.sha256(source);
+            if (target.equals(sign)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static String getSign(String source) {
+        return SecureUtil.sha256(source);
+    }
+
+    /**
+     * 获取JWT加密后的token值
+     *
+     * @return
+     */
+    public static String getEncryptionToken(long time) {
+        Map<String, Object> headers = new HashMap<>();
+        headers.put("alg", "HS256");
+        headers.put("typ", "JWT");
+        Map<String, Object> payload = new HashMap<>();
+        payload.put("iat", time);
+        payload.put("exp", NumberUtil.add(time, 7200));
+        payload.put("jti", SecureUtil.md5(String.format("JWT%s.%s", UUID.randomUUID(), time)));
+        return JWTUtil.createToken(headers, payload, OPEN_KEY.getBytes());
+    }
+
+    public static void main(String[] args) throws NoSuchAlgorithmException {
+//        String appRequestPrivateKey = "14FDF4948E60F856524FBB1175E83716558B8CBF71A68318875F506B9C6D2A4A";
+//        String appRequestPublicKey = "04F36CD10986CE214D0E5C540C30E0552DC8499B64E5B2709245D03BF2CADAA0CCA3C2BC2C8DB511012A50FAA1E43FCD4B8ABC521418EAB2D96F0075AD940EB25F";
+//        String appResponsePrivateKey = "00C83135E19EBD958593091F42A3442DE3D03D975A5DBD4CE19F85C9FBF2D364B7";
+//        String appResponsePublicKey = "040D10F8CA3AAC83345DA54472CCE5AB495BBB15E21E960B2A2EEE1D9EEC2E9EB1BE3902606904BD767FF056F59CC1AD237D3074A3F8D452AE376FFE84113640C0";
+//        String webRequestPrivateKey = "14FDF4948E60F856524FBB1175E83716558B8CBF71A68318875F506B9C6D2A4A";
+//        String webRequestPublicKey = "04F36CD10986CE214D0E5C540C30E0552DC8499B64E5B2709245D03BF2CADAA0CCA3C2BC2C8DB511012A50FAA1E43FCD4B8ABC521418EAB2D96F0075AD940EB25F";
+//        String webResponsePrivateKey = "00C83135E19EBD958593091F42A3442DE3D03D975A5DBD4CE19F85C9FBF2D364B7";
+//        String webResponsePublicKey = "040D10F8CA3AAC83345DA54472CCE5AB495BBB15E21E960B2A2EEE1D9EEC2E9EB1BE3902606904BD767FF056F59CC1AD237D3074A3F8D452AE376FFE84113640C0";
+//        String marketRequestPrivateKey = "00C8F4837D0397FA20083E7209B5F5E1723285F8CB486777AF68257AA1A73A6E14";
+//        String marketRequestPublicKey = "04B3CB95E5C8F82688DC2A864B520FEE1439459A09A2710D3D21655D1EC88D1C4232F3D1DD3013716A1B537BF9C68FC2A15010B2544DC951058CACEE59661A8C84";
+//        String marketResponsePrivateKey = "3A5351F28BB08AFAC2CBA40C3389822563C86B9DC3704E85E4D69F2C58E642BC";
+//        String marketResponsePublicKey = "042D309FF19484EE1F5812CD4E95D095F2259BEFE2E9FD51E67063383C372437595324DABE7EBDC5D0931D4D39530CE918580A3773959CD24AFE897C03DC4B9D99";
+
+        // 加密=================================================================================
+//        String publicKey = "04F36CD10986CE214D0E5C540C30E0552DC8499B64E5B2709245D03BF2CADAA0CCA3C2BC2C8DB511012A50FAA1E43FCD4B8ABC521418EAB2D96F0075AD940EB25F";
+//        JSONObject json = new JSONObject();
+//        JSONObject header = new JSONObject();
+//        JSONObject body = new JSONObject();
+//        header.put("timeStamp", "20231020170620");
+//        header.put("messageId", "eac044675e5248898aae3fb6920295b8");
+//        json.put("basicData", header);
+//        body.put("name", "fangchao");
+//        body.put("detail", "yingyingying");
+//        body.put("test1", null);
+//        body.put("test2", "");
+//        body.put("array", new ArrayList<>());
+//        json.put("bizData", body);
+//        String jsonResult = encryptSM2(publicKey, json.toJSONString());
+//        System.out.println(jsonResult);
+        // 生成签名=====================================================================
+//        String sign = SecureUtil.sha256(jsonResult);
+//        System.out.println(sign);
+        // 解密=======================================================================
+//        String jsonResult = "5fbdab8e4654e4dbc98f8286e6910d37b0252ce8094f995faf38f1d4fb75c4c83c1751be1cfb529d3933dcabe8a41428ff041bda247edc9200dfdb9e98a64043ba877d1ce0a194ae3697c470872ef9019109bf0aabd2cc6ae09b10a73e76d2bf74ad5720cb7e1c4afd34d1c0dbb9bd6b1cb351a83071b30ed5ea5af2393c9d5cfa291828d051f1291f4a30c13326fd45189a5600f119f4a0484dd78940";
+//        String result = decryptSM2(webRequestPrivateKey, jsonResult);
+//        System.out.println(result);
+//        String errorToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEuNzE3NDA4MTZFOSwiaWF0IjoxNzE3NDAwOTAwLCJqdGkiOiIyMTNmNTcxOWY1NTczMGQyZmI2NzQ5MzliMzllOTA1NSJ9.sMeXBLRSYqgki9i0CEbckujfWsCDsMVUQAX9o-9c0g0";
+//        String successToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEuNzE3NDEwMzRFOSwiaWF0IjoxNzE3NDAzMTIwLCJqdGkiOiI4M2FjYmU4M2ZmOTk4YWIyM2Q2ZDg2MjkyNWYxNGZjYiJ9.Jdoh2cqJNgetOe9ojvYGEfyeO0yYX8mtC9JLKErSsXs";
+//        System.out.println("errorToken=====================================================");
+//        JWT jwt = JWTUtil.parseToken(errorToken);
+//        JSONObject headers = jwt.getHeaders();
+//        System.out.println(headers);
+//        JSONObject payloads = jwt.getPayloads();
+//        System.out.println(payloads);
+//        System.out.println("successToken=====================================================");
+//        jwt = JWTUtil.parseToken(successToken);
+//        headers = jwt.getHeaders();
+//        System.out.println(headers);
+//        payloads = jwt.getPayloads();
+//        System.out.println(payloads);
+
+//        String data = "6255e7c3f02e01fefec586ebcaa70ed49c2c13120cabdcbb771e6fa61b560c277f07cde5123e53632537e487988f02cbabb3b72bba8695639a705b47104490c2aa5171279a4ec794a7e54ca552864c1ed35cea75f30fd45b34a26b704dac5fb7f6a4e260dc09b6cbd3c8246f8b5a453fd844cc9595482c8e6eadbc6d968f";
+////        String privateKey = "14FDF4948E60F856524FBB1175E83716558B8CBF71A68318875F506B9C6D2A4A";
+//        String publicKey = "04F36CD10986CE214D0E5C540C30E0552DC8499B64E5B2709245D03BF2CADAA0CCA3C2BC2C8DB511012A50FAA1E43FCD4B8ABC521418EAB2D96F0075AD940EB25F";
+//        System.out.println(decryptSM2(privateKey, data));
+//        SecretKey key = generateSHA256Key();
+//        System.out.println(SecureUtil.getDHex());
+//        String token1 = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEuNjk5OTM2NTRFOSwiaWF0IjoxNjk5OTI5MzkwLCJqdGkiOiI0Mzg4NjFlZWU2NWVmNzNkZWYzYThkNTM2ZGU0YjZjZSJ9.QMiAByk_u3EAbqLWnNw5yKWeQW8EcqNC7nnmp01WVAM";
+//        String token2 = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEuNjk5OTM2NjdFOSwiaWF0IjoxNjk5OTI5NTIwLCJqdGkiOiIzMTg4YTE5MTk5M2IyMmM0Yzg5MWE3MDFlOTdjODRmNCJ9.gjH_WogjFcx_5sQ8EZEisYK2lK5tVFLFNem_zyajnVE";
+//        JWT jwt1 = JWTUtil.parseToken(token1);
+//        JWT jwt2 = JWTUtil.parseToken(token2);
+//        System.out.println(jwt1);
+//        System.out.println(jwt2);
+//        long time = (long) NumberUtil.div(System.currentTimeMillis(), 1000l);
+//        System.out.println(getEncryptionToken(time));
+    }
+
+}

+ 79 - 0
src/main/resources/application.yml

@@ -0,0 +1,79 @@
+server:
+  port: 8083
+  servlet:
+    context-path: /app
+
+spring:
+  application:
+    name: xs-tg-game
+  data:
+    redis:
+      host: 192.168.241.130
+      port: 6379
+      password: xudm5200
+      database: 5
+      lettuce:
+        pool:
+          # 连接池最大连接数
+          max-active: 200
+          # 连接池最大阻塞等待时间(使用负值表示没有限制)
+          max-wait: -1ms
+          # 连接池中的最大空闲连接
+          max-idle: 10
+          # 连接池中的最小空闲连接
+          min-idle: 2
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:p6spy:mysql://192.168.241.130:3306/continew?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
+    username: continew
+    password: continew123456
+    hikari:
+      # 最大连接数量(默认 10,根据实际环境调整)
+      # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
+      maximum-pool-size: 20
+      # 获取连接超时时间(默认 30000 毫秒,30 秒)
+      connection-timeout: 30000
+      # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟)
+      idle-timeout: 600000
+      # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用)
+      keepaliveTime: 30000
+      # 连接最大生存时间(默认 1800000 毫秒,30 分钟)
+      max-lifetime: 1800000
+
+
+############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
+sa-token:
+  # token 名称(同时也是 cookie 名称)
+  token-name: Authorization
+  # token前缀
+  token-prefix: Bearer
+  # token 有效期(单位:秒) 默认30天,-1 代表永久有效
+  timeout: 86400
+  # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
+  active-timeout: -1
+  # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
+  is-concurrent: false
+  # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
+  is-share: true
+  # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
+  token-style: tik
+  # 是否输出操作日志
+  is-log: false
+  jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk
+
+# 配置请求是否加密
+encryption:
+  isEncryption: true
+  requestPrivateKey: 14FDF4948E60F856524FBB1175E83716558B8CBF71A68318875F506B9C6D2A4A
+  requestPublicKey: 04F36CD10986CE214D0E5C540C30E0552DC8499B64E5B2709245D03BF2CADAA0CCA3C2BC2C8DB511012A50FAA1E43FCD4B8ABC521418EAB2D96F0075AD940EB25F
+  responsePrivateKey: 00C83135E19EBD958593091F42A3442DE3D03D975A5DBD4CE19F85C9FBF2D364B7
+  responsePublicKey: 040D10F8CA3AAC83345DA54472CCE5AB495BBB15E21E960B2A2EEE1D9EEC2E9EB1BE3902606904BD767FF056F59CC1AD237D3074A3F8D452AE376FFE84113640C0
+
+
+# 防止xss攻击配置
+xssFilter:
+  # 是否开启防止xss攻击
+  isOpenXssFilter: false
+  # 不拦截路径,多个用“,”分隔,例如/userBusiness/*,/sysRole/*
+  xssFilterWhileUrl: /process/*

+ 13 - 0
src/test/java/com/xs/core/XsTgGameApplicationTests.java

@@ -0,0 +1,13 @@
+package com.xs.core;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class XsTgGameApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}