软件增加license管理

需要增加2个文件，建议目录放置在config目录
FilterConfig.java
全局过滤器配置 ,对需要拦截的路径进行配置
package cn.vppark.whdev.license_test.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Filter配置
 *
 * @author Mark sunlightcs@gmail.com
 */
@Configuration
public class FilterConfig {

 @Value("${app.license:}")
 private String license;

 @Bean
 public FilterRegistrationBean licenseFilterRegistration() {
 FilterRegistrationBean registration = new FilterRegistrationBean();
 registration.setFilter(new LicenseFilter(license));
 registration.addUrlPatterns("/*");
 registration.setName("licenseFilter");
 registration.setOrder(Integer.MAX_VALUE);
 return registration;
 }

}

LicenseFilter.java
package cn.vppark.whdev.license_test.config;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;

/**
 * @Description: 证书校验
 * @date: 2024年4月9日 下午12:39:48
 * @Copyright:
 */
public class LicenseFilter implements Filter {

 private String license;

 /**
 * 公钥
 */
 private final String publicKey = "your public key";

 public LicenseFilter(String license) {
 this.license = license;
 }

 @Override
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 //设置返回参数
 HttpServletResponse httpServletResponse = (HttpServletResponse) response;
 httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
 httpServletResponse.setCharacterEncoding("UTF-8");
 httpServletResponse.setContentType("text/html; charset=UTF-8");

 try {
 if (this.license == null || license.isEmpty()) {
 // 优先使用配置文件的证书，因为调试运行的时候，会清空target目录，导致证书文件丢失
 this.license = IOUtils.toString(new FileInputStream(getLicensePath()), StandardCharsets.UTF_8);
 }
 //解密证书
 String licenseContent = verifyLicense(this.license);
 JSONObject json = JSONObject.parseObject(licenseContent);
 //获取过期时间
 Date expire = json.getDate("expire");
 httpServletResponse.setHeader("license-expire", expire.toString());
 //判断证书是否过期
 if (expire.getTime() < System.currentTimeMillis()) {
 httpServletResponse.getWriter().write("软件授权过期！");
 return;
 }
 httpServletResponse.setStatus(HttpServletResponse.SC_OK);
 } catch (FileNotFoundException e) {
 httpServletResponse.getWriter().write("证书文件丢失，请联系管理员！");
 e.printStackTrace();
 return;
 } catch (Exception e) {
 httpServletResponse.getWriter().write("证书文件已损坏，请联系管理员！");
 e.printStackTrace();
 return;
 }
 chain.doFilter(request, response);
 }

 /**
 * @throws
 * @Description: 根据公钥解密字符串
 * @param: licenseContent 证书内容
 */
 public String verifyLicense(String licenseContent) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
 Cipher cipher = Cipher.getInstance("RSA");
 cipher.init(Cipher.DECRYPT_MODE, KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey))));
 byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(licenseContent));
 String decryptedString = new String(decryptedBytes);
 // 打印解密后的证书内容
 System.out.println("licenseContent: " + decryptedString);
 return decryptedString;
 }

 /**
 * @throws
 * @Description: 获取license完整路径
 */
 private String getLicensePath() throws URISyntaxException {

 URL classUrl = this.getClass().getProtectionDomain().getCodeSource().getLocation();
 URI uri = classUrl.toURI();
 Path path;
 if (uri.getScheme().equals("jar")) {
 // 如果是一个JAR URL，我们需要找到JAR文件本身
 String jarPath = uri.getSchemeSpecificPart();
 jarPath = jarPath.substring(0, jarPath.indexOf('!'));
 path = Paths.get(new URI(jarPath)).getParent();
 } else {
 // 如果不是JAR URL，则可能是直接从文件系统加载的类
 path = Paths.get(uri);
 }
 String licensePath = path.resolve("license/license").toString();
 // 打印证书路径
 System.out.println("licensePath: " + licensePath);
 return licensePath;
 }

}

