乐于分享
好东西不私藏

若依框架实现软件授权

若依框架实现软件授权

我们的项目使用的若依前后端分离框架,均需部署在客户提供的服务器上,运维工作也主要通过客户开放的远程方式开展。前一段上班路上老板突然打电话给我:

老板:“如果客户后续不续费了,咱们有没有办法把系统服务停止掉?”

:“老板,要是客户一直给我们开放远程运维权限,我们就能直接操作停止服务;但如果客户把远程权限收回去了,我们就没辙了。”

老板:“那不行,这事儿必须解决,你赶紧想个办法,确保就算没有远程权限,客户不续费时,我们也能把服务停了。”

老板发了话,我只能立刻行动,开始围绕这一核心需求,梳理实现过程中需要规避的问题与具体落地思路。

一、核心需求与关键注意事项

要实现“客户不续费时可停止服务”的目标,首先要规避一系列潜在漏洞,毕竟所有程序和数据都部署在客户服务器上,客户存在篡改、破解的可能,因此以下5个关键点必须重点考量:

  1. 过期时间不可存储在数据库:数据库部署在客户服务器,客户可随意修改数据库中的过期时间,导致管控失效;
  2. 过期时间不可嵌入jar包:即便jar包经过加密处理,但jar包本身仍在客户服务器上,无法避免被客户破解、修改过期时间;
  3. 不可仅在启动时判断过期:若系统启动后一直不重启,即便已过有效期,服务仍会持续运行,无法达到关停目的,需加入定时检测机制;
  4. 不可使用若依自带定时任务:若依框架的定时任务可通过页面操作或数据库修改直接删除、关闭,客户可轻松规避检测,因此需采用Spring原生定时任务;
  5. 需防范服务器时间回拨:若客户调整服务器时间,使系统时间始终小于过期时间,上述所有管控手段都会失效,需加入时间防篡改校验。

二、初始实现思路(原方案)

结合上述注意事项,我梳理出一套初始实现方案,核心是通过授权文件管控过期时间,搭配定时检测和防时间回拨机制,具体步骤如下:

  1. 授权文件生成功能开发:开发授权文件生成功能,文件内包含过期时间和签名串,签名串用于防止客户篡改文件内容;
  2. 授权文件部署:将生成的授权文件放置在服务器指定目录下,确保文件可被系统读取但不易被客户随意找到;
  3. 启动时过期校验:系统启动时,自动读取授权文件,校验过期时间,若已过期则直接停止服务;
  4. Spring原生定时任务配置:基于Spring原生定时任务,设置每天凌晨1点执行过期检测,若检测到已过期,立即停止服务;
  5. Redis时间防回拨校验:定时任务启动时,若系统未过期,将当天时间存入Redis,后续检测时对比Redis中的时间与当前服务器时间,防范客户时间回拨;
  6. 登录提醒功能:当授权有效期不足30天时,在用户登录系统时弹出提醒,告知客户及时续费;

方案落地后,我们在自己的服务器上进行了测试,所有功能均正常运行,看似完美解决了老板提出的需求。但在测试人员更换不同日期的授权文件进行二次测试时,一个隐藏的漏洞被意外发现。

三、漏洞发现与优化方案(新方案)

测试人员询问更换授权文件后是否需要重启系统,这让我意识到,初始方案存在两个关键漏洞,若不解决,会导致客户续费时出现服务异常:

  1. 客户续费时,更换新的授权文件后,需要重启系统才能加载新的过期时间,若客户未及时重启,可能导致服务被误关停;
  2. 若客户在授权到期最后一天续费,且新授权文件的过期时间从当天0点开始,而定时任务在每天凌晨1点执行,会出现“新授权已生效,但定时任务未及时加载,仍按旧授权判断过期”的时间差问题,导致服务误关停。

针对上述漏洞,我对方案进行了针对性优化,放弃了“频繁提高定时任务执行频率”的低效方案(会增加服务器负载,且大部分时间无实际意义),重点优化授权文件的加载机制,具体优化措施如下:

  1. 授权文件信息记录:系统第一次读取授权文件时,同步记录文件的修改时间和文件内容(含签名串),存储在内存中;
  2. 定时任务文件校验与重新加载:每天定时任务执行时,不仅检测过期时间,还会重新读取授权文件,对比当前文件的修改时间、内容与内存中记录的信息,若存在差异,立即加载新的授权文件,无需重启系统;
  3. 手动刷新授权接口开发:针对授权到期最后一天续费的紧急情况,开发手动刷新授权文件接口,若出现时间差导致的异常,可通过该接口手动触发授权文件重新加载,快速解决问题;

优化完成后,再次进行多场景测试,无论是正常续费更换授权文件,还是紧急情况下的授权刷新,均能正常生效,彻底解决了初始方案的漏洞,确保授权管控功能既安全又灵活。

四、核心功能源代码

本节单独预留原方案、优化后方案的核心源代码位置,按功能模块分类,便于后续补充完善,对应前文实现思路与优化措施:

4.1 原方案(初始实现)源代码

@Componentpublic class LicenseManager {    private static final Logger log = LoggerFactory.getLogger(LicenseManager.class);    // 授权文件路径(和jar包同级)    private static final String LICENSE_FILE = "./license.lic";    // 签名密钥(和生成时保持一致)    private static final String SECRET_KEY = "ruoyi-2024";    // Redis防回拨key    private static final String REDIS_PROOF_KEY = "license:proof:date";    @Autowired(required = false)    private StringRedisTemplate redisTemplate;    private LocalDate expiryDate;    private boolean valid = false;    private long lastFileCheck = 0;    // ==================== 1. 启动检查 ====================    @PostConstruct    public void initCheck() {        log.info("=========================================");        log.info("开始授权校验...");        log.info("授权文件路径:{}"new File(LICENSE_FILE).getAbsolutePath());        try {            // 1.1 检查文件是否存在            File file = new File(LICENSE_FILE);            if (!file.exists()) {                log.error("授权文件不存在:{}", LICENSE_FILE);                kill("授权文件不存在");                return;            }            // 1.2 读取并解密授权文件            String encrypted = new String(Files.readAllBytes(file.toPath())).trim();            String plainText = AesDecryptUtils.decrypt(encrypted);            if (plainText == null) {                log.error("授权文件解密失败");                kill("授权文件格式错误");                return;            }            log.info("授权文件解密成功:{}", plainText);            // 1.3 解析日期和签名            String[] parts = plainText.split("\\|");            if (parts.length != 2) {                log.error("授权文件格式错误,应为:日期|签名");                kill("授权文件格式错误");                return;            }            String dateStr = parts[0];            String signature = parts[1];            // 1.4 验证签名            String expectedSign = DigestUtils.md5DigestAsHex(                (dateStr + SECRET_KEY).getBytes()            );            if (!expectedSign.equals(signature)) {                log.error("签名验证失败");                log.error("预期签名:{}", expectedSign);                log.error("实际签名:{}", signature);                kill("授权文件签名验证失败");                return;            }            // 1.5 解析过期日期            expiryDate = LocalDate.parse(dateStr);            // 1.6 检查是否过期            LocalDate now = LocalDate.now();            if (now.isAfter(expiryDate)) {                log.error("系统已过期!过期日期:{},当前日期:{}", expiryDate, now);                kill("系统已过期");                return;            }            // 1.7 检查时间是否被回拨            checkTimeRollback();            valid = true;            log.info("✅ 授权校验通过,有效期至:{}", expiryDate);            log.info("剩余有效期:{}天", ChronoUnit.DAYS.between(now, expiryDate));            // 1.8 记录文件修改时间            lastFileCheck = file.lastModified();            // 1.9 启动文件监控            startFileMonitor();        } catch (Exception e) {            log.error("授权校验异常", e);            kill("校验异常:" + e.getMessage());        }    }    // ==================== 2. 定时检查(每天2次)====================    @Scheduled(cron = "0 0 1 * * ?")  // 凌晨2点    public void scheduledCheck() {        if (!valid) return;        log.info("执行定时授权检查...");        try {            LocalDate now = LocalDate.now();            if (now.isAfter(expiryDate)) {                log.error("定时检查:系统已过期(过期日期:{})", expiryDate);                kill("定时检查触发");            } else {                log.info("定时检查通过,剩余有效期:{}天"                    ChronoUnit.DAYS.between(now, expiryDate));            }        } catch (Exception e) {            log.error("定时检查异常", e);        }    }    // ==================== 3. 时间回拨检查 ====================    private void checkTimeRollback() {        if (redisTemplate == null) {            log.warn("Redis未配置,跳过时间回拨检查");            return;        }        try {            String today = LocalDate.now().toString();            String yesterday = redisTemplate.opsForValue().get(REDIS_PROOF_KEY);            // 如果昨天记录的时间比今天还大,说明时间被回拨了            if (yesterday != null && LocalDate.parse(yesterday).isAfter(LocalDate.now())) {                log.error("检测到时间回拨!昨天记录:{},今天实际:{}", yesterday, today);                kill("时间回拨");                return;            }            // 记录今天的时间            redisTemplate.opsForValue().set(REDIS_PROOF_KEY, today, 30, TimeUnit.DAYS);        } catch (Exception e) {            log.error("Redis时间检查异常", e);        }    }    // ==================== 4. 文件监控 ====================    private void startFileMonitor() {        Thread monitor = new Thread(() -> {            while (valid) {                try {                    Thread.sleep(60 * 60 * 1000); // 每小时检查一次                    File file = new File(LICENSE_FILE);                    long lastModified = file.lastModified();                    if (lastModified != lastFileCheck) {                        log.warn("检测到授权文件发生变化,重新校验...");                        // 重新读取校验                        String encrypted = new String(Files.readAllBytes(file.toPath())).trim();                        String plainText = AesDecryptUtils.decrypt(encrypted);                        if (plainText == null) {                            kill("授权文件被篡改");                            return;                        }                        String[] parts = plainText.split("\\|");                        if (parts.length != 2) {                            kill("授权文件格式错误");                            return;                        }                        String dateStr = parts[0];                        String signature = parts[1];                        String expectedSign = DigestUtils.md5DigestAsHex(                            (dateStr + SECRET_KEY).getBytes()                        );                        if (!expectedSign.equals(signature)) {                            kill("授权文件签名失效");                            return;                        }                        LocalDate newExpiry = LocalDate.parse(dateStr);                        if (LocalDate.now().isAfter(newExpiry)) {                            kill("授权文件已过期");                            return;                        }                        expiryDate = newExpiry;                        lastFileCheck = lastModified;                        log.info("授权文件重新校验通过,新有效期:{}", expiryDate);                    }                } catch (InterruptedException e) {                    break;                } catch (Exception e) {                    log.error("文件监控异常", e);                }            }        });        monitor.setName("license-file-monitor");        monitor.setDaemon(true);        monitor.start();        log.info("授权文件监控已启动");    }    // ==================== 5. 杀进程 ====================    private void kill(String reason) {        log.error("==========================================");        log.error("系统授权失效,执行终止!");        log.error("授权文件:{}", LICENSE_FILE);        log.error("过期日期:{}", expiryDate);        log.error("当前日期:{}", LocalDate.now());        log.error("触发原因:{}", reason);        log.error("==========================================");        // 写入日志文件        try {            Files.write(                Paths.get("./license_kill.log"),                (LocalDateTime.now() + " 终止原因:" + reason + "\n").getBytes(),                java.nio.file.StandardOpenOption.CREATE,                java.nio.file.StandardOpenOption.APPEND            );        } catch (Exception e) {            // ignore        }        valid = false;        // 30秒后强制退出        new Thread(() -> {            try {                Thread.sleep(30000);            } catch (Exception e) {}            Runtime.getRuntime().halt(1);        }).start();        System.exit(-1);    }    // ==================== 6. 对外接口 ====================    /**     * 登录检查     */    public void checkLogin() {        if (!valid) {            throw new RuntimeException("系统授权异常,请联系供应商");        }        if (LocalDate.now().isAfter(expiryDate)) {            throw new RuntimeException("系统已过期,请联系供应商");        }    }}

4.2 优化后方案(新方案)源代码

@Slf4j@Componentpublic class LicenseManager {    // 授权文件路径(和jar包同级)    private static final String LICENSE_FILE = "./license.lic";    // 签名密钥(和生成时保持一致)    private static final String SECRET_KEY = "sty-license-2026";    // Redis防回拨key    private static final String REDIS_PROOF_KEY = "license:proof:date";    @Autowired(required = false)    private StringRedisTemplate redisTemplate;    private LocalDate expiryDate;    private boolean valid = false;    private long lastFileCheck = 0;    private String lastFileHash = null;  // 记录文件内容哈希    // ==================== 1. 启动检查 ====================    @PostConstruct    public void initCheck() {        log.info("=========================================");        log.info("开始授权校验...");        log.info("授权文件路径:{}"new File(LICENSE_FILE).getAbsolutePath());        try {            // 1.1 检查文件是否存在            File file = new File(LICENSE_FILE);            if (!file.exists()) {                log.error("授权文件不存在:{}", LICENSE_FILE);                kill("授权文件不存在");                return;            }            // 1.2 读取并解密授权文件            String encrypted = new String(Files.readAllBytes(file.toPath())).trim();            String plainText = AesDecryptUtils.decrypt(encrypted);            if (plainText == null) {                log.error("授权文件解密失败");                kill("授权文件格式错误");                return;            }            log.info("授权文件解密成功:{}", plainText);            //log.info("授权文件解密成功");            // 1.3 解析日期和签名            String[] parts = plainText.split("\\|");            if (parts.length != 2) {                log.error("授权文件格式错误,应为:日期|签名");                kill("授权文件格式错误");                return;            }            String dateStr = parts[0];            String signature = parts[1];            // 1.4 验证签名            String expectedSign = DigestUtils.md5DigestAsHex(                    (dateStr + SECRET_KEY).getBytes()            );            if (!expectedSign.equals(signature)) {                log.error("签名验证失败");                log.error("预期签名:{}", expectedSign);                log.error("实际签名:{}", signature);                kill("授权文件签名验证失败");                return;            }            // 1.5 解析过期日期            expiryDate = LocalDate.parse(dateStr);            // 1.6 检查是否过期            LocalDate now = LocalDate.now();            if (now.compareTo(expiryDate) >= 0) {                log.error("系统已过期!过期日期:{},当前日期:{}", expiryDate, now);                kill("系统已过期");                return;            }            // 1.7 检查时间是否被回拨            checkTimeRollback();            valid = true;            log.info("✅ 授权校验通过,有效期至:{}", expiryDate);            log.info("剩余有效期:{}天", ChronoUnit.DAYS.between(now, expiryDate));            // 1.8 记录文件修改时间和内容哈希            lastFileCheck = file.lastModified();            lastFileHash = DigestUtils.md5DigestAsHex(Files.readAllBytes(file.toPath()));        } catch (Exception e) {            log.error("授权校验异常", e);            kill("校验异常:" + e.getMessage());        }    }    // ==================== 2. 定时检查(每天 1 次)====================    @Scheduled(cron = "0 0 1 * * ?")  // 凌晨 1 点    public void scheduledCheck() {        log.info("执行定时授权检查...");        try {            // 2.1 检查授权文件是否被更新            File file = new File(LICENSE_FILE);            if (file.exists()) {                long currentLastModified = file.lastModified();                String currentHash = DigestUtils.md5DigestAsHex(Files.readAllBytes(file.toPath()));                // 如果文件修改时间或内容发生变化,重新加载授权文件                if (currentLastModified != lastFileCheck || !currentHash.equals(lastFileHash)) {                    log.info("检测到授权文件已更新,重新加载授权校验...");                    loadLicenseFile(currentLastModified, currentHash, true);                    return;                }            }            // 2.2 如果没有更新,执行常规检查            if (!valid) {                log.error("授权未通过,跳过定时检查");                return;            }            LocalDate now = LocalDate.now();            if (now.compareTo(expiryDate) >= 0) {                log.error("定时检查:系统已过期(过期日期:{})", expiryDate);                kill("定时检查触发");            } else {                log.info("定时检查通过,剩余有效期:{}天", ChronoUnit.DAYS.between(now, expiryDate));            }        } catch (Exception e) {            log.error("定时检查异常", e);        }    }    /**     * 对外公开的授权文件刷新方法(供 Controller 调用)     * @return 刷新结果 true-成功,false-失败     */    public boolean reloadLicenseFromFile() {        try {            log.info("开始手动刷新授权文件...");            File file = new File(LICENSE_FILE);            if (!file.exists()) {                log.error("授权文件不存在:{}", LICENSE_FILE);                return false;            }            // 检查文件是否发生变化            long currentLastModified = file.lastModified();            String currentHash = DigestUtils.md5DigestAsHex(Files.readAllBytes(file.toPath()));            if (currentLastModified == lastFileCheck && currentHash.equals(lastFileHash)) {                log.info("授权文件未发生变化,无需刷新");                log.info("当前授权有效期至:{}", expiryDate);                log.info("剩余有效期:{}天", ChronoUnit.DAYS.between(LocalDate.now(), expiryDate));                return true;  // 文件没变化,但也算成功            }            // 执行重新加载(不触发 kill)            return loadLicenseFile(currentLastModified, currentHash, false);        } catch (Exception e) {            log.error("手动刷新授权文件异常", e);            return false;        }    }    /**     * 通用授权文件加载方法     * @param newLastModified 新文件最后修改时间     * @param newHash 新文件内容哈希     * @param shouldKill 验证失败时是否终止系统     * @return 加载结果 true-成功,false-失败     */    private boolean loadLicenseFile(long newLastModified, String newHash, boolean shouldKill) {        try {            // 读取并解密授权文件            String encrypted = new String(Files.readAllBytes(Paths.get(LICENSE_FILE))).trim();            String plainText = AesDecryptUtils.decrypt(encrypted);            if (plainText == null) {                log.error("授权文件解密失败");                if (shouldKill) {                    kill("授权文件格式错误");                } else {                    valid = false;                }                return false;            }            log.info("授权文件解密成功");            // 解析日期和签名            String[] parts = plainText.split("\\|");            if (parts.length != 2) {                log.error("授权文件格式错误,应为:日期 | 签名");                if (shouldKill) {                    kill("授权文件格式错误");                } else {                    valid = false;                }                return false;            }            String dateStr = parts[0];            String signature = parts[1];            // 验证签名            String expectedSign = DigestUtils.md5DigestAsHex(                    (dateStr + SECRET_KEY).getBytes()            );            if (!expectedSign.equals(signature)) {                log.error("授权文件签名验证失败");                if (shouldKill) {                    kill("授权文件签名验证失败");                } else {                    valid = false;                }                return false;            }            // 解析过期日期            expiryDate = LocalDate.parse(dateStr);            // 检查是否过期            LocalDate now = LocalDate.now();            if (now.compareTo(expiryDate) >= 0) {                log.error("授权文件已过期!过期日期:{},当前日期:{}", expiryDate, now);                if (shouldKill) {                    kill("授权文件已过期");                } else {                    valid = false;                }                return false;            }            // 更新文件记录信息            lastFileCheck = newLastModified;            lastFileHash = newHash;            valid = true;            log.info("✅ 授权文件加载成功,有效期至:{}", expiryDate);            log.info("剩余有效期:{}天", ChronoUnit.DAYS.between(now, expiryDate));            return true;        } catch (Exception e) {            log.error("加载授权文件异常", e);            if (shouldKill) {                kill("加载授权文件异常:" + e.getMessage());            } else {                valid = false;            }            return false;        }    }    // ==================== 3. 时间回拨检查 ====================    private void checkTimeRollback() {        if (redisTemplate == null) {            log.warn("Redis未配置,跳过时间回拨检查");            return;        }        try {            String today = LocalDate.now().toString();            String yesterday = redisTemplate.opsForValue().get(REDIS_PROOF_KEY);            // 如果昨天记录的时间比今天还大,说明时间被回拨了            if (yesterday != null && LocalDate.parse(yesterday).isAfter(LocalDate.now())) {                log.error("检测到时间回拨!昨天记录:{},今天实际:{}", yesterday, today);                kill("时间回拨");                return;            }            // 记录今天的时间            redisTemplate.opsForValue().set(REDIS_PROOF_KEY, today, 30, TimeUnit.DAYS);        } catch (Exception e) {            log.error("Redis时间检查异常", e);        }    }    // ==================== 5. 杀进程 ====================    private void kill(String reason) {        log.error("==========================================");        log.error("系统授权失效,执行终止!");        log.error("授权文件:{}", LICENSE_FILE);        log.error("过期日期:{}", expiryDate);        log.error("当前日期:{}", LocalDate.now());        log.error("触发原因:{}", reason);        log.error("==========================================");        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");        String now = LocalDateTime.now().format(formatter);        // 写入日志文件        try {            Files.write(                    Paths.get("./logs/license_kill.log"),                    (now + " 终止原因:" + reason + "\n").getBytes(),                    java.nio.file.StandardOpenOption.CREATE,                    java.nio.file.StandardOpenOption.APPEND            );        } catch (Exception e) {            // ignore        }        valid = false;        // 30秒后强制退出        new Thread(() -> {            try {                Thread.sleep(30000);            } catch (Exception e) {}            Runtime.getRuntime().halt(1);        }).start();        System.exit(-1);    }    // ==================== 6. 对外接口 ====================    /**     * 登录检查     */    public void checkLogin() {        if (!valid) {            throw new RuntimeException("系统授权异常,请联系软件供应商");        }        if (LocalDate.now().compareTo(expiryDate) >= 0) {            throw new RuntimeException("系统已过期,请联系软件供应商");        }    }}

五、系统流程图

5.1 原方案(初始实现)系统流程图

5.2 新方案(优化后)系统流程图

六、总结

本次授权管控功能的实现,从最初的需求拆解、注意事项梳理,到初始方案落地、漏洞发现与优化,全程围绕“安全、可靠、灵活”的核心目标。核心难点在于,所有管控逻辑都需规避“客户篡改数据、破解程序”的风险,同时兼顾客户续费时的使用体验,避免出现服务误关停的情况。

通过授权文件+Spring定时任务+Redis时间校验的组合方式,既解决了客户不续费时的服务关停问题,又通过优化授权文件加载机制,解决了续费场景下的漏洞,最终实现了管控需求与用户体验的平衡。此次复盘也让我深刻意识到,技术实现不仅要满足核心需求,更要考虑到各种边界场景,多维度测试才能避免隐藏漏洞,确保功能稳定落地。

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-04-10 10:35:56 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/506699.html
  2. 运行时间 : 0.099112s [ 吞吐率:10.09req/s ] 内存消耗:4,961.05kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=9a6b4f0313d0818dea7611d5d6290cfa
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.80 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000612s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001079s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000352s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000277s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000481s ]
  6. SELECT * FROM `set` [ RunTime:0.000193s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000604s ]
  8. SELECT * FROM `article` WHERE `id` = 506699 LIMIT 1 [ RunTime:0.000597s ]
  9. UPDATE `article` SET `lasttime` = 1775788556 WHERE `id` = 506699 [ RunTime:0.007121s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000207s ]
  11. SELECT * FROM `article` WHERE `id` < 506699 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000497s ]
  12. SELECT * FROM `article` WHERE `id` > 506699 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000419s ]
  13. SELECT * FROM `article` WHERE `id` < 506699 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000720s ]
  14. SELECT * FROM `article` WHERE `id` < 506699 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000871s ]
  15. SELECT * FROM `article` WHERE `id` < 506699 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000822s ]
0.103084s