生成方法
生成私钥和公钥
java
import org.apache.commons.codec.binary.Base64;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* 你本地运行:生成 RSA 公私钥
*/
public class RsaKeyGenerate {
public static void main(String[] args) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String pubKey = Base64.encodeBase64String(publicKey.getEncoded());
String priKey = Base64.encodeBase64String(privateKey.getEncoded());
System.out.println("=====公钥(给SpringBoot项目)=====");
System.out.println(pubKey);
System.out.println("\n=====私钥(你自己保存,不给客户)=====");
System.out.println(priKey);
}
}
根据私钥生成加密文件
java
import com.alibaba.fastjson.JSON;
import com.jiliason.business.safety.License;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.LocalDateTime;
/**
* 你本地专用:生成 license.lic 加密文件
* 设置过期时间、最大可用天数
*/
public class LicenseGenerateTool {
// 粘贴你刚才生成的私钥
private static final String PRIVATE_KEY = "生成的私钥";
// RSA 私钥加密
public static String encrypt(String content) throws Exception {
byte[] keyBytes = Base64.decodeBase64(PRIVATE_KEY);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return Base64.encodeBase64String(cipher.doFinal(content.getBytes()));
}
public static void main(String[] args) throws Exception {
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(600);
// ========================================
License license = new License();
license.setExpireTime(expireTime);
// 对象转JSON
String jsonStr = JSON.toJSONString(license);
// 私钥加密
String encryptStr = encrypt(jsonStr);
// 输出加密后的授权内容,你复制保存为 license.lic
System.out.println("=====生成的license.lic内容=====");
System.out.println(encryptStr);
}
}
拦截器实现
定义实体类
java
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class License {
private LocalDateTime expireTime; // 到期时间
}
定义拦截器方法
java
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Value("${safety.useKey}")
private String useKey;
@Value("${safety.publicKey}")
private String publicKey;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
getLicense();
return true;
} catch (Exception e) {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":403,\"msg\":\"产品已过期,请联系开发商续费\"}");
return false;
}
}
public License getLicense() {
// 解密
try {
String json = decryptByPublicKey(useKey);
License license = new com.alibaba.fastjson.JSONObject().parseObject(json, License.class);
// 校验:不能超过到期时间
if (LocalDateTime.now().isAfter(license.getExpireTime())) {
throw new RuntimeException("产品已到期!");
}
return license;
} catch (Exception e) {
throw new RuntimeException("授权文件非法!" + e);
}
}
// 公钥解密
public String decryptByPublicKey(String data) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return new String(cipher.doFinal(Base64.decodeBase64(data)));
}
}
使用拦截器
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/**")
// .excludePathPatterns("/login", "/static/**");
.excludePathPatterns("/static/**");
}
}
注意:
1.如果操作人,修改服务器时间,过期时间就没用了(有下面解决办法)
- 如果是外网,增加时间校验api,通过校验传递过来的时间和标准时间校验,判断时间是否一致
- 如果是内网,可以通过查询数据库中标志性数据判断是否修改了系统时间,或者增加文件用来记录最新操作时间
2.增加拦截器进行拦截,每次解密等操作还是比较费性能的,如果token有过期时间,可以在登录的时候进行校验
扩展
增加启动校验
java
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class LicenseCheckRunner implements CommandLineRunner {
@Override
public void run(String... args) {
//调用校验代码
}
}