代码修改
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package com.ruoyi.license.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
|
||||
import com.ruoyi.license.interceptor.LicenseInterceptor;
|
||||
|
||||
/**
|
||||
* 授权模块配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class LicenseConfig implements WebMvcConfigurer
|
||||
{
|
||||
@Autowired
|
||||
private LicenseInterceptor licenseInterceptor;
|
||||
|
||||
@Autowired
|
||||
private PermitAllUrlProperties permitAllUrl;
|
||||
|
||||
@Value("${license.enabled:false}")
|
||||
private boolean licenseEnabled;
|
||||
|
||||
private static final List<String> BASE_EXCLUDE = Arrays.asList(
|
||||
"/login",
|
||||
"/logout",
|
||||
"/register",
|
||||
"/captchaImage",
|
||||
"/system/license/active",
|
||||
"/swagger-ui.html",
|
||||
"/swagger-resources/**",
|
||||
"/webjars/**",
|
||||
"/*/api-docs",
|
||||
"/druid/**",
|
||||
"/*.html",
|
||||
"/**/*.html",
|
||||
"/**/*.css",
|
||||
"/**/*.js",
|
||||
"/profile/**"
|
||||
);
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry)
|
||||
{
|
||||
if (licenseEnabled)
|
||||
{
|
||||
List<String> excludeList = new ArrayList<>(BASE_EXCLUDE);
|
||||
excludeList.addAll(permitAllUrl.getUrls());
|
||||
registry.addInterceptor(licenseInterceptor)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(excludeList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.ruoyi.license.domain;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 系统授权码对象 sys_license
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SysLicense extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 授权ID */
|
||||
private Long licenseId;
|
||||
|
||||
/** 授权码 */
|
||||
private String licenseCode;
|
||||
|
||||
/** 授权开始时间 */
|
||||
private Date startTime;
|
||||
|
||||
/** 授权结束时间 */
|
||||
private Date expireTime;
|
||||
|
||||
/** 授权状态(0未激活 1已激活 2已过期) */
|
||||
private String status;
|
||||
|
||||
/** 激活时间 */
|
||||
private Date activeTime;
|
||||
|
||||
/** 激活人 */
|
||||
private String activeBy;
|
||||
|
||||
public Long getLicenseId()
|
||||
{
|
||||
return licenseId;
|
||||
}
|
||||
|
||||
public void setLicenseId(Long licenseId)
|
||||
{
|
||||
this.licenseId = licenseId;
|
||||
}
|
||||
|
||||
public String getLicenseCode()
|
||||
{
|
||||
return licenseCode;
|
||||
}
|
||||
|
||||
public void setLicenseCode(String licenseCode)
|
||||
{
|
||||
this.licenseCode = licenseCode;
|
||||
}
|
||||
|
||||
public Date getStartTime()
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(Date startTime)
|
||||
{
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public Date getExpireTime()
|
||||
{
|
||||
return expireTime;
|
||||
}
|
||||
|
||||
public void setExpireTime(Date expireTime)
|
||||
{
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Date getActiveTime()
|
||||
{
|
||||
return activeTime;
|
||||
}
|
||||
|
||||
public void setActiveTime(Date activeTime)
|
||||
{
|
||||
this.activeTime = activeTime;
|
||||
}
|
||||
|
||||
public String getActiveBy()
|
||||
{
|
||||
return activeBy;
|
||||
}
|
||||
|
||||
public void setActiveBy(String activeBy)
|
||||
{
|
||||
this.activeBy = activeBy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.ruoyi.license.interceptor;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import com.ruoyi.license.service.ISysLicenseService;
|
||||
|
||||
/**
|
||||
* 授权拦截器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class LicenseInterceptor implements HandlerInterceptor
|
||||
{
|
||||
@Autowired
|
||||
private ISysLicenseService licenseService;
|
||||
|
||||
@Value("${license.enabled:false}")
|
||||
private boolean licenseEnabled;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
|
||||
{
|
||||
if (!licenseEnabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!licenseService.checkLicenseValid())
|
||||
{
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.write("{\"code\":500,\"msg\":\"\u7cfb\u7edf\u6388\u6743\u5df2\u8fc7\u671f\uff0c\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u6fc0\u6d3b\u6388\u6743\u7801\"}");
|
||||
writer.flush();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.ruoyi.license.mapper;
|
||||
|
||||
import com.ruoyi.license.domain.SysLicense;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 授权码Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface SysLicenseMapper
|
||||
{
|
||||
/**
|
||||
* 查询授权码
|
||||
*
|
||||
* @param licenseId 授权码ID
|
||||
* @return 授权码
|
||||
*/
|
||||
public SysLicense selectSysLicenseById(Long licenseId);
|
||||
|
||||
/**
|
||||
* 查询授权码列表
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 授权码集合
|
||||
*/
|
||||
public List<SysLicense> selectSysLicenseList(SysLicense sysLicense);
|
||||
|
||||
/**
|
||||
* 查询当前有效的授权码
|
||||
*
|
||||
* @return 授权码
|
||||
*/
|
||||
public SysLicense selectActiveLicense();
|
||||
|
||||
/**
|
||||
* 新增授权码
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysLicense(SysLicense sysLicense);
|
||||
|
||||
/**
|
||||
* 修改授权码
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysLicense(SysLicense sysLicense);
|
||||
|
||||
/**
|
||||
* 删除授权码
|
||||
*
|
||||
* @param licenseId 授权码ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysLicenseById(Long licenseId);
|
||||
|
||||
/**
|
||||
* 批量删除授权码
|
||||
*
|
||||
* @param licenseIds 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysLicenseByIds(String[] licenseIds);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.ruoyi.license.service;
|
||||
|
||||
import com.ruoyi.license.domain.SysLicense;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 授权码Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface ISysLicenseService
|
||||
{
|
||||
/**
|
||||
* 查询授权码
|
||||
*
|
||||
* @param licenseId 授权码ID
|
||||
* @return 授权码
|
||||
*/
|
||||
public SysLicense selectSysLicenseById(Long licenseId);
|
||||
|
||||
/**
|
||||
* 查询授权码列表
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 授权码集合
|
||||
*/
|
||||
public List<SysLicense> selectSysLicenseList(SysLicense sysLicense);
|
||||
|
||||
/**
|
||||
* 新增授权码
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysLicense(SysLicense sysLicense);
|
||||
|
||||
/**
|
||||
* 修改授权码
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysLicense(SysLicense sysLicense);
|
||||
|
||||
/**
|
||||
* 批量删除授权码
|
||||
*
|
||||
* @param ids 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysLicenseByIds(String ids);
|
||||
|
||||
/**
|
||||
* 删除授权码信息
|
||||
*
|
||||
* @param licenseId 授权码ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysLicenseById(Long licenseId);
|
||||
|
||||
/**
|
||||
* 激活授权码
|
||||
*
|
||||
* @param licenseCode 授权码
|
||||
* @return 结果
|
||||
*/
|
||||
public int activeLicense(String licenseCode);
|
||||
|
||||
/**
|
||||
* 检查授权是否有效
|
||||
*
|
||||
* @return true有效 false无效
|
||||
*/
|
||||
public boolean checkLicenseValid();
|
||||
|
||||
/**
|
||||
* 获取当前有效授权信息
|
||||
*
|
||||
* @return 授权信息
|
||||
*/
|
||||
public SysLicense getActiveLicense();
|
||||
|
||||
/**
|
||||
* 更新过期授权码状态
|
||||
*/
|
||||
public void updateExpiredLicenses();
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
package com.ruoyi.license.service.impl;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.license.domain.SysLicense;
|
||||
import com.ruoyi.license.mapper.SysLicenseMapper;
|
||||
import com.ruoyi.license.service.ISysLicenseService;
|
||||
import com.ruoyi.license.util.LicenseUtils;
|
||||
|
||||
/**
|
||||
* 授权码Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service
|
||||
public class SysLicenseServiceImpl implements ISysLicenseService
|
||||
{
|
||||
@Autowired
|
||||
private SysLicenseMapper sysLicenseMapper;
|
||||
|
||||
@Autowired
|
||||
private com.ruoyi.system.mapper.SysConfigMapper sysConfigMapper;
|
||||
|
||||
@org.springframework.beans.factory.annotation.Value("${license.enabled:false}")
|
||||
private boolean licenseEnabled;
|
||||
|
||||
/**
|
||||
* 查询授权码
|
||||
*
|
||||
* @param licenseId 授权码ID
|
||||
* @return 授权码
|
||||
*/
|
||||
@Override
|
||||
public SysLicense selectSysLicenseById(Long licenseId)
|
||||
{
|
||||
return sysLicenseMapper.selectSysLicenseById(licenseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询授权码列表
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 授权码
|
||||
*/
|
||||
@Override
|
||||
public List<SysLicense> selectSysLicenseList(SysLicense sysLicense)
|
||||
{
|
||||
return sysLicenseMapper.selectSysLicenseList(sysLicense);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增授权码
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertSysLicense(SysLicense sysLicense)
|
||||
{
|
||||
sysLicense.setCreateTime(new Date());
|
||||
return sysLicenseMapper.insertSysLicense(sysLicense);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改授权码
|
||||
*
|
||||
* @param sysLicense 授权码
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateSysLicense(SysLicense sysLicense)
|
||||
{
|
||||
sysLicense.setUpdateTime(new Date());
|
||||
return sysLicenseMapper.updateSysLicense(sysLicense);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除授权码对象
|
||||
*
|
||||
* @param ids 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysLicenseByIds(String ids)
|
||||
{
|
||||
return sysLicenseMapper.deleteSysLicenseByIds(Convert.toStrArray(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除授权码信息
|
||||
*
|
||||
* @param licenseId 授权码ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysLicenseById(Long licenseId)
|
||||
{
|
||||
return sysLicenseMapper.deleteSysLicenseById(licenseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活授权码
|
||||
*
|
||||
* @param licenseCode 授权码
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int activeLicense(String licenseCode)
|
||||
{
|
||||
if (StringUtils.isEmpty(licenseCode))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 验证授权码格式
|
||||
if (!LicenseUtils.validateFormat(licenseCode))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 从配置表获取项目编号
|
||||
String projectNo = sysConfigMapper.selectConfigByKey("fortucky.project.no");
|
||||
if (StringUtils.isEmpty(projectNo))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 解析授权码
|
||||
long expireTime = LicenseUtils.parseLicense(licenseCode, projectNo);
|
||||
if (expireTime == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
Date startTime = now;
|
||||
Date expireDate = new Date(expireTime);
|
||||
|
||||
// 验证是否已过期
|
||||
if (expireDate.before(now))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 创建授权记录
|
||||
SysLicense license = new SysLicense();
|
||||
license.setLicenseCode(licenseCode);
|
||||
license.setStartTime(startTime);
|
||||
license.setExpireTime(expireDate);
|
||||
license.setStatus("1");
|
||||
license.setActiveTime(now);
|
||||
license.setCreateTime(now);
|
||||
|
||||
return sysLicenseMapper.insertSysLicense(license);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查授权是否有效
|
||||
*
|
||||
* @return true有效 false无效
|
||||
*/
|
||||
@Override
|
||||
public boolean checkLicenseValid()
|
||||
{
|
||||
// 如果授权码功能未启用,直接返回true
|
||||
if (!licenseEnabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SysLicense license = sysLicenseMapper.selectActiveLicense();
|
||||
if (license == null)
|
||||
{
|
||||
System.out.println("[授权检查] 没有找到激活的授权码");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从配置表获取项目编号
|
||||
String projectNo = sysConfigMapper.selectConfigByKey("fortucky.project.no");
|
||||
if (StringUtils.isEmpty(projectNo))
|
||||
{
|
||||
System.out.println("[授权检查] 项目编号未配置");
|
||||
return false;
|
||||
}
|
||||
|
||||
System.out.println("[授权检查] 授权码: " + license.getLicenseCode());
|
||||
System.out.println("[授权检查] 项目编号: " + projectNo);
|
||||
|
||||
// 从授权码解析过期时间
|
||||
long expireTime = LicenseUtils.parseLicense(license.getLicenseCode(), projectNo);
|
||||
if (expireTime == 0)
|
||||
{
|
||||
System.out.println("[授权检查] 授权码解析失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
System.out.println("[授权检查] 当前时间: " + now.getTime());
|
||||
System.out.println("[授权检查] 过期时间: " + expireTime);
|
||||
|
||||
if (expireTime < now.getTime())
|
||||
{
|
||||
System.out.println("[授权检查] 授权已过期");
|
||||
// 更新为过期状态
|
||||
license.setStatus("2");
|
||||
sysLicenseMapper.updateSysLicense(license);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前有效授权信息
|
||||
*
|
||||
* @return 授权信息
|
||||
*/
|
||||
@Override
|
||||
public SysLicense getActiveLicense()
|
||||
{
|
||||
return sysLicenseMapper.selectActiveLicense();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新过期授权码状态
|
||||
*/
|
||||
@Override
|
||||
public void updateExpiredLicenses()
|
||||
{
|
||||
SysLicense query = new SysLicense();
|
||||
query.setStatus("1");
|
||||
List<SysLicense> licenses = sysLicenseMapper.selectSysLicenseList(query);
|
||||
|
||||
// 从配置表获取项目编号
|
||||
String projectNo = sysConfigMapper.selectConfigByKey("fortucky.project.no");
|
||||
if (StringUtils.isEmpty(projectNo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
for (SysLicense license : licenses)
|
||||
{
|
||||
// 从授权码解析过期时间
|
||||
long expireTime = LicenseUtils.parseLicense(license.getLicenseCode(), projectNo);
|
||||
if (expireTime > 0 && expireTime < now.getTime())
|
||||
{
|
||||
license.setStatus("2");
|
||||
license.setUpdateTime(now);
|
||||
sysLicenseMapper.updateSysLicense(license);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.ruoyi.license.task;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.license.service.ISysLicenseService;
|
||||
|
||||
/**
|
||||
* 授权检查定时任务
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class LicenseCheckTask
|
||||
{
|
||||
@Autowired
|
||||
private ISysLicenseService licenseService;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Value("${license.enabled:false}")
|
||||
private boolean licenseEnabled;
|
||||
|
||||
/**
|
||||
* 每5分钟检查一次授权状态
|
||||
*/
|
||||
@Scheduled(cron = "0 */5 * * * ?")
|
||||
public void checkLicense()
|
||||
{
|
||||
if (!licenseEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!licenseService.checkLicenseValid())
|
||||
{
|
||||
// 授权无效,删除 Redis 中所有登录 token,强制踢出所有在线用户
|
||||
redisCache.deleteObject(redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"));
|
||||
}
|
||||
|
||||
licenseService.updateExpiredLicenses();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.ruoyi.license.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* 授权码生成工具(独立运行)
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class LicenseGenerator
|
||||
{
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
|
||||
System.out.println("========================================");
|
||||
System.out.println(" RuoYi 授权码生成工具");
|
||||
System.out.println("========================================");
|
||||
System.out.println();
|
||||
|
||||
try
|
||||
{
|
||||
// 选择授权类型
|
||||
System.out.println("请选择授权类型:");
|
||||
System.out.println("1. 5分钟测试");
|
||||
System.out.println("2. 1个月授权");
|
||||
System.out.println("3. 3个月授权");
|
||||
System.out.println("4. 1年授权");
|
||||
System.out.println("5. 5年授权");
|
||||
System.out.println("6. 长期授权");
|
||||
System.out.print("请输入选项(1-6): ");
|
||||
String typeChoice = scanner.nextLine().trim();
|
||||
|
||||
String type;
|
||||
String typeDesc;
|
||||
long expireTime;
|
||||
|
||||
switch (typeChoice)
|
||||
{
|
||||
case "1":
|
||||
type = LicenseUtils.TYPE_TEST;
|
||||
typeDesc = "5分钟测试";
|
||||
expireTime = System.currentTimeMillis() + 5 * 60 * 1000L;
|
||||
break;
|
||||
case "2":
|
||||
type = LicenseUtils.TYPE_MONTH;
|
||||
typeDesc = "1个月授权";
|
||||
expireTime = System.currentTimeMillis() + 30L * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case "3":
|
||||
type = LicenseUtils.TYPE_THREE;
|
||||
typeDesc = "3个月授权";
|
||||
expireTime = System.currentTimeMillis() + 90L * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case "4":
|
||||
type = LicenseUtils.TYPE_YEAR;
|
||||
typeDesc = "1年授权";
|
||||
expireTime = System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case "5":
|
||||
type = LicenseUtils.TYPE_FIVE;
|
||||
typeDesc = "5年授权";
|
||||
expireTime = System.currentTimeMillis() + 5 * 365L * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
case "6":
|
||||
type = LicenseUtils.TYPE_FOREVER;
|
||||
typeDesc = "长期授权";
|
||||
expireTime = System.currentTimeMillis() + 100 * 365L * 24 * 60 * 60 * 1000;
|
||||
break;
|
||||
default:
|
||||
System.out.println("错误:无效的选项");
|
||||
return;
|
||||
}
|
||||
|
||||
// 输入项目编号
|
||||
System.out.print("请输入项目编号: ");
|
||||
String projectNo = scanner.nextLine().trim();
|
||||
|
||||
if (projectNo.isEmpty())
|
||||
{
|
||||
System.out.println("错误:项目编号不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成授权码
|
||||
String licenseCode = LicenseUtils.generateLicense(type, projectNo);
|
||||
|
||||
if (licenseCode == null)
|
||||
{
|
||||
System.out.println("错误:授权码生成失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Date now = new Date();
|
||||
Date expire = new Date(expireTime);
|
||||
|
||||
System.out.println();
|
||||
System.out.println("========================================");
|
||||
System.out.println("授权码生成成功!");
|
||||
System.out.println("========================================");
|
||||
System.out.println("授权类型: " + typeDesc);
|
||||
System.out.println("项目编号: " + projectNo);
|
||||
System.out.println("当前时间: " + sdf.format(now));
|
||||
System.out.println("过期时间: " + sdf.format(expire));
|
||||
System.out.println("----------------------------------------");
|
||||
System.out.println("授权码:");
|
||||
System.out.println(licenseCode);
|
||||
System.out.println("========================================");
|
||||
System.out.println();
|
||||
System.out.println("请妥善保管授权码,在系统中激活后即可使用。");
|
||||
System.out.println("注意:系统配置中的项目编号必须与此处一致才能激活。");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.out.println("错误:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.ruoyi.license.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 授权码测试工具
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class LicenseTest
|
||||
{
|
||||
public static void main(String[] args)
|
||||
{
|
||||
System.out.println("========================================");
|
||||
System.out.println(" 授权码功能测试");
|
||||
System.out.println("========================================");
|
||||
System.out.println();
|
||||
|
||||
String projectNo = "FT541";
|
||||
|
||||
|
||||
// 测试1:生成各类型授权码
|
||||
System.out.println("【测试1】生成各类型授权码");
|
||||
String testCode = LicenseUtils.generateLicense(LicenseUtils.TYPE_TEST, projectNo);
|
||||
String yearCode = LicenseUtils.generateLicense(LicenseUtils.TYPE_YEAR, projectNo);
|
||||
String fiveCode = LicenseUtils.generateLicense(LicenseUtils.TYPE_FIVE, projectNo);
|
||||
String foreverCode = LicenseUtils.generateLicense(LicenseUtils.TYPE_FOREVER, projectNo);
|
||||
|
||||
System.out.println("5分钟测试: " + testCode);
|
||||
System.out.println("1年授权: " + yearCode);
|
||||
System.out.println("5年授权: " + fiveCode);
|
||||
System.out.println("长期授权: " + foreverCode);
|
||||
System.out.println("结果: ✓ 通过");
|
||||
System.out.println();
|
||||
|
||||
// 测试2:验证授权码格式
|
||||
System.out.println("【测试2】验证授权码格式");
|
||||
boolean isValid = LicenseUtils.validateFormat(yearCode);
|
||||
System.out.println("格式验证: " + (isValid ? "✓ 通过" : "✗ 失败"));
|
||||
System.out.println();
|
||||
|
||||
// 测试3:解析授权码
|
||||
System.out.println("【测试3】解析授权码");
|
||||
long expireTime = LicenseUtils.parseLicense(yearCode, projectNo);
|
||||
if (expireTime > 0)
|
||||
{
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Date expire = new Date(expireTime);
|
||||
System.out.println("过期时间: " + sdf.format(expire));
|
||||
System.out.println("结果: ✓ 通过");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("结果: ✗ 失败");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
// 测试4:项目编号验证
|
||||
System.out.println("【测试4】项目编号验证");
|
||||
long validResult = LicenseUtils.parseLicense(yearCode, projectNo);
|
||||
long invalidResult = LicenseUtils.parseLicense(yearCode, "WRONG001");
|
||||
System.out.println("正确项目编号: " + (validResult > 0 ? "✓ 通过" : "✗ 失败"));
|
||||
System.out.println("错误项目编号: " + (invalidResult == 0 ? "✓ 通过(正确拒绝)" : "✗ 失败"));
|
||||
System.out.println();
|
||||
|
||||
// 测试5:授权码格式展示
|
||||
System.out.println("【测试5】授权码格式展示");
|
||||
System.out.println("授权码: " + yearCode);
|
||||
String[] parts = yearCode.split("_");
|
||||
if (parts.length == 5)
|
||||
{
|
||||
System.out.println("前缀: " + parts[0]);
|
||||
System.out.println("类型: " + parts[1] + " (" + LicenseUtils.getTypeDesc(parts[1]) + ")");
|
||||
System.out.println("项目编号: " + parts[2]);
|
||||
System.out.println("加密时间: " + parts[3]);
|
||||
System.out.println("校验码: " + parts[4]);
|
||||
System.out.println("结果: ✓ 通过");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("结果: ✗ 失败");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
System.out.println("========================================");
|
||||
System.out.println("测试完成!");
|
||||
System.out.println("========================================");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.ruoyi.license.util;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* 授权码工具类
|
||||
* 格式: FORTUCKY_授权类型_项目编号_加密时间_校验码
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class LicenseUtils
|
||||
{
|
||||
// 密钥(16位)
|
||||
private static final String SECRET_KEY = "RuoYiLicense2024";
|
||||
|
||||
// 授权类型
|
||||
public static final String TYPE_TEST = "TEST"; // 5分钟测试
|
||||
public static final String TYPE_MONTH = "MONTH"; // 1个月
|
||||
public static final String TYPE_THREE = "THREE"; // 3个月
|
||||
public static final String TYPE_YEAR = "YEAR"; // 1年
|
||||
public static final String TYPE_FIVE = "FIVE"; // 5年
|
||||
public static final String TYPE_FOREVER = "FOREVER"; // 长期
|
||||
|
||||
/**
|
||||
* 生成授权码
|
||||
*
|
||||
* @param type 授权类型
|
||||
* @param projectNo 项目编号
|
||||
* @return 授权码
|
||||
*/
|
||||
public static String generateLicense(String type, String projectNo)
|
||||
{
|
||||
try
|
||||
{
|
||||
long expireTime = calculateExpireTime(type);
|
||||
if (expireTime == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 加密过期时间
|
||||
String encryptedTime = encrypt(String.valueOf(expireTime));
|
||||
if (encryptedTime == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 生成校验码(取加密时间的前8位MD5)
|
||||
String checkCode = generateCheckCode(type, projectNo, encryptedTime);
|
||||
|
||||
// 拼接授权码: FORTUCKY_类型_项目编号_加密时间_校验码
|
||||
return "FORTUCKY_" + type + "_" + projectNo + "_" + encryptedTime + "_" + checkCode;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证授权码格式
|
||||
*
|
||||
* @param licenseCode 授权码
|
||||
* @return true有效 false无效
|
||||
*/
|
||||
public static boolean validateFormat(String licenseCode)
|
||||
{
|
||||
if (licenseCode == null || licenseCode.trim().isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] parts = licenseCode.split("_");
|
||||
if (parts.length != 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!"FORTUCKY".equals(parts[0]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证授权类型
|
||||
String type = parts[1];
|
||||
if (!TYPE_TEST.equals(type) && !TYPE_MONTH.equals(type) && !TYPE_THREE.equals(type) &&
|
||||
!TYPE_YEAR.equals(type) && !TYPE_FIVE.equals(type) && !TYPE_FOREVER.equals(type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析授权码
|
||||
*
|
||||
* @param licenseCode 授权码
|
||||
* @param projectNo 系统项目编号
|
||||
* @return 过期时间戳,0表示解析失败
|
||||
*/
|
||||
public static long parseLicense(String licenseCode, String projectNo)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!validateFormat(licenseCode))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
String[] parts = licenseCode.split("_");
|
||||
String type = parts[1];
|
||||
String codeProjectNo = parts[2];
|
||||
String encryptedTime = parts[3];
|
||||
String checkCode = parts[4];
|
||||
|
||||
// 验证项目编号
|
||||
if (!codeProjectNo.equals(projectNo))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 验证校验码
|
||||
String expectedCheckCode = generateCheckCode(type, codeProjectNo, encryptedTime);
|
||||
if (!checkCode.equals(expectedCheckCode))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 解密时间
|
||||
String decrypted = decrypt(encryptedTime);
|
||||
if (decrypted == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Long.parseLong(decrypted);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权类型描述
|
||||
*
|
||||
* @param type 授权类型
|
||||
* @return 描述
|
||||
*/
|
||||
public static String getTypeDesc(String type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_TEST: return "5分钟测试";
|
||||
case TYPE_MONTH: return "1个月授权";
|
||||
case TYPE_THREE: return "3个月授权";
|
||||
case TYPE_YEAR: return "1年授权";
|
||||
case TYPE_FIVE: return "5年授权";
|
||||
case TYPE_FOREVER: return "长期授权";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算过期时间
|
||||
*
|
||||
* @param type 授权类型
|
||||
* @return 过期时间戳
|
||||
*/
|
||||
private static long calculateExpireTime(String type)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_TEST:
|
||||
return now + 5 * 60 * 1000L; // 5分钟
|
||||
case TYPE_MONTH:
|
||||
return now + 30L * 24 * 60 * 60 * 1000; // 1个月
|
||||
case TYPE_THREE:
|
||||
return now + 90L * 24 * 60 * 60 * 1000; // 3个月
|
||||
case TYPE_YEAR:
|
||||
return now + 365L * 24 * 60 * 60 * 1000; // 1年
|
||||
case TYPE_FIVE:
|
||||
return now + 5 * 365L * 24 * 60 * 60 * 1000; // 5年
|
||||
case TYPE_FOREVER:
|
||||
return now + 100 * 365L * 24 * 60 * 60 * 1000; // 100年(视为长期)
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成校验码
|
||||
*
|
||||
* @param type 授权类型
|
||||
* @param projectNo 项目编号
|
||||
* @param encryptedTime 加密时间
|
||||
* @return 校验码
|
||||
*/
|
||||
private static String generateCheckCode(String type, String projectNo, String encryptedTime)
|
||||
{
|
||||
try
|
||||
{
|
||||
String data = type + projectNo + encryptedTime;
|
||||
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
|
||||
byte[] hash = md.digest(data.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < Math.min(4, hash.length); i++)
|
||||
{
|
||||
sb.append(String.format("%02x", hash[i]));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return "00000000";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param data 原始数据
|
||||
* @return 加密后的数据
|
||||
*/
|
||||
private static String encrypt(String data)
|
||||
{
|
||||
try
|
||||
{
|
||||
SecretKeySpec key = new SecretKeySpec(SECRET_KEY.getBytes("UTF-8"), "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
byte[] encrypted = cipher.doFinal(data.getBytes("UTF-8"));
|
||||
return Base64.getEncoder().encodeToString(encrypted).replace("+", "-").replace("/", ".").replace("=", "");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param encryptedData 加密数据
|
||||
* @return 解密后的数据
|
||||
*/
|
||||
private static String decrypt(String encryptedData)
|
||||
{
|
||||
try
|
||||
{
|
||||
String base64Data = encryptedData.replace("-", "+").replace(".", "/");
|
||||
int padding = (4 - base64Data.length() % 4) % 4;
|
||||
for (int i = 0; i < padding; i++)
|
||||
{
|
||||
base64Data += "=";
|
||||
}
|
||||
|
||||
SecretKeySpec key = new SecretKeySpec(SECRET_KEY.getBytes("UTF-8"), "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(base64Data));
|
||||
return new String(decrypted, "UTF-8");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.license.mapper.SysLicenseMapper">
|
||||
|
||||
<resultMap type="com.ruoyi.license.domain.SysLicense" id="SysLicenseResult">
|
||||
<result property="licenseId" column="license_id" />
|
||||
<result property="licenseCode" column="license_code" />
|
||||
<result property="startTime" column="start_time" />
|
||||
<result property="expireTime" column="expire_time" />
|
||||
<result property="status" column="status" />
|
||||
<result property="activeTime" column="active_time" />
|
||||
<result property="activeBy" column="active_by" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysLicenseVo">
|
||||
select license_id, license_code, start_time, expire_time, status, active_time, active_by, create_by, create_time, update_by, update_time, remark from sys_license
|
||||
</sql>
|
||||
|
||||
<select id="selectSysLicenseList" parameterType="com.ruoyi.license.domain.SysLicense" resultMap="SysLicenseResult">
|
||||
<include refid="selectSysLicenseVo"/>
|
||||
<where>
|
||||
<if test="licenseCode != null and licenseCode != ''"> and license_code = #{licenseCode}</if>
|
||||
<if test="startTime != null "> and start_time = #{startTime}</if>
|
||||
<if test="expireTime != null "> and expire_time = #{expireTime}</if>
|
||||
<if test="status != null and status != ''"> and status = #{status}</if>
|
||||
</where>
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectSysLicenseById" parameterType="Long" resultMap="SysLicenseResult">
|
||||
<include refid="selectSysLicenseVo"/>
|
||||
where license_id = #{licenseId}
|
||||
</select>
|
||||
|
||||
<select id="selectActiveLicense" resultMap="SysLicenseResult">
|
||||
<include refid="selectSysLicenseVo"/>
|
||||
where status = '1'
|
||||
order by create_time desc
|
||||
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
|
||||
</select>
|
||||
|
||||
<insert id="insertSysLicense" parameterType="com.ruoyi.license.domain.SysLicense" useGeneratedKeys="true" keyProperty="licenseId">
|
||||
insert into sys_license
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="licenseCode != null and licenseCode != ''">license_code,</if>
|
||||
<if test="startTime != null">start_time,</if>
|
||||
<if test="expireTime != null">expire_time,</if>
|
||||
<if test="status != null and status != ''">status,</if>
|
||||
<if test="activeTime != null">active_time,</if>
|
||||
<if test="activeBy != null">active_by,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="licenseCode != null and licenseCode != ''">#{licenseCode},</if>
|
||||
<if test="startTime != null">#{startTime},</if>
|
||||
<if test="expireTime != null">#{expireTime},</if>
|
||||
<if test="status != null and status != ''">#{status},</if>
|
||||
<if test="activeTime != null">#{activeTime},</if>
|
||||
<if test="activeBy != null">#{activeBy},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateSysLicense" parameterType="com.ruoyi.license.domain.SysLicense">
|
||||
update sys_license
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="licenseCode != null and licenseCode != ''">license_code = #{licenseCode},</if>
|
||||
<if test="startTime != null">start_time = #{startTime},</if>
|
||||
<if test="expireTime != null">expire_time = #{expireTime},</if>
|
||||
<if test="status != null and status != ''">status = #{status},</if>
|
||||
<if test="activeTime != null">active_time = #{activeTime},</if>
|
||||
<if test="activeBy != null">active_by = #{activeBy},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
</trim>
|
||||
where license_id = #{licenseId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteSysLicenseById" parameterType="Long">
|
||||
delete from sys_license where license_id = #{licenseId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteSysLicenseByIds" parameterType="String">
|
||||
delete from sys_license where license_id in
|
||||
<foreach item="licenseId" collection="array" open="(" separator="," close=")">
|
||||
#{licenseId}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user