跳转到主要内容

软件增加license管理

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;
    }

}