编程随笔 编程随笔
  • 前端
  • 后端
  • 嵌入式
  • 星球项目
  • 开源项目
  • 海康AGV
  • 四向车
  • 工具类
  • 项目仓库

    • 部署仓库 (opens new window)
    • 代码仓库 (opens new window)
  • vuepress插件

    • 自动生成导航栏与侧边栏 (opens new window)
    • 评论系统 (opens new window)
    • 全文搜索 (opens new window)
    • 选项卡 (opens new window)
    • 自动生成sitemap (opens new window)
  • 自主开发插件

    • 批量操作frontmatter (opens new window)
    • 链接美化 (opens new window)
    • 折叠代码块 (opens new window)
    • 复制代码块 (opens new window)

liyao52033

走运时,要想到倒霉,不要得意得过了头;倒霉时,要想到走运,不必垂头丧气。心态始终保持平衡,情绪始终保持稳定,此亦长寿之道
  • 前端
  • 后端
  • 嵌入式
  • 星球项目
  • 开源项目
  • 海康AGV
  • 四向车
  • 工具类
  • 项目仓库

    • 部署仓库 (opens new window)
    • 代码仓库 (opens new window)
  • vuepress插件

    • 自动生成导航栏与侧边栏 (opens new window)
    • 评论系统 (opens new window)
    • 全文搜索 (opens new window)
    • 选项卡 (opens new window)
    • 自动生成sitemap (opens new window)
  • 自主开发插件

    • 批量操作frontmatter (opens new window)
    • 链接美化 (opens new window)
    • 折叠代码块 (opens new window)
    • 复制代码块 (opens new window)
  • springboot

    • MyBatis Plus使用
    • springboot2引入swagger3
    • EasyCaptcha验证码存入redis的使用
    • 常用方法
    • Elasticsearch全文搜索
    • canal同步mysql数据到es中
    • SpringSecurity使用
    • StringUtils 工具类使用
    • HTTP各种参数发送
    • EasyExcel之Excel导入导出
    • EasyExcel具体使用
    • FreeMarker 模板引擎入门
    • FreeMarker生成文件及WEB使用
    • TrueLicense 创建及安装证书
      • 使用 keytool 生成公私钥证书库
      • 项目配置
      • 使用方式
      • 自定义 License 管理,创建、安装、校验等
  • 服务器相关

  • 腾讯云cos对象操作

  • 后端
  • springboot
华总
2024-10-25
0
0
目录

TrueLicense 创建及安装证书原创

# 使用 keytool 生成公私钥证书库

例如:私钥库密码为 priwd123456,公钥库密码为 pubwd123456,生成步骤如下:

# 1. 生成私钥库
# validity:私钥的有效期(天)
# alias:私钥别称
# keystore:私钥库文件名称(生成在当前目录)
# storepass:私钥库密码(获取 keystore 信息所需的密码,密钥库口令)
# keypass:别名条目的密码(密钥口令)
keytool -genkeypair -keysize 1024 -validity 3650 -alias "privateKey" -keystore "privateKeys.keystore" -storepass "pubwd123456" -keypass "priwd123456" -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN"

# 2. 把私钥库内的公钥导出到一个文件当中
# alias:私钥别称
# keystore:私钥库的名称(在当前目录查找)
# storepass:私钥库的密码
# file:证书名称
keytool -exportcert -alias "privateKey" -keystore "privateKeys.keystore" -storepass "pubwd123456" -file "certfile.cer"

# 3.再把这个证书文件导入到公钥库,certfile.cer 没用了可以删掉了
# alias:公钥名称
# file:证书名称
# keystore:公钥文件名称
# storepass:公钥库密码
keytool -import -alias "publicCert" -file "certfile.cer" -keystore "publicCerts.keystore" -storepass "pubwd123456"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 项目配置

server:
  port: 8080
# License 相关配置
license:
  # 主题
  subject: license_demo
  # 公钥别称
  publicAlias: publicCert
  # 访问公钥的密码
  storePass: pubwd123456
  # license 位置
  licensePath: E:/licenseTest/license.lic
  # licensePath: /root/license-test/license.lic
  # 公钥位置
  publicKeysStorePath: E:/licenseTest/publicCerts.keystore
  # publicKeysStorePath: /root/license-test/publicCerts.keystore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 使用方式

# 全局拦截过滤器


public class CustomFilter extends OncePerRequestFilter {

    @Resource
    private LicenseVerify licenseVerify;
  
    @Resource
    private UserService userService;

    // 指定允许通过的接口路径
    private static final List<String> ALLOWED_PATHS = Arrays.asList(
            // 添加允许通过的接口路径
            //swagger接口
            "/api/webjars/**",
            "/api/doc.html",
            "/api/swagger-resources/**",
            "/api/v3/api-docs/**",
            "/api/swagger-ui/**",
            "/api/swagger-ui.html",
            "/api/ws/**",
            "/api/ws-app/**",
            "/api/v3/api-docs/swagger-config",
            "/api/v3/api-docs/default",
            // 用户接口
            "/api/user/login",
            "/api/user/register",
            "/api/user/logout",
            "/api/user/getInfo",
            "/api/user/login/wx_open",
            "/api/user/refresh",
            // 验证码接口
            "/api/captcha/get",
            "/api/captcha/check",
            //授权文件接口
            "/api/license/upload",
            "/api/license/multipartUpload",
            "/api/license/generateLicense",
            "/api/license/getServerInfos"
    );

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response,
                                    @NotNull FilterChain filterChain) throws ServletException, IOException {

        if (request.getMethod().equals("OPTIONS")) {
            // 如果是预检请求,直接放行
            filterChain.doFilter(request, response);
            return;
        }

        String requestURI = request.getRequestURI();

        // 检查请求的URL是否在允许列表中
        if (ALLOWED_PATHS.stream().anyMatch(path -> antPathMatcher.match(path, requestURI))) {
            // 如果在允许列表中,则继续处理请求
            filterChain.doFilter(request, response);
        } else {

            try {

                boolean isLogin = userService.isLogin(request);
                if (!isLogin) {
                    extracted(response, ErrorCode.NOT_LOGIN_ERROR);
                    return;
                }

                licenseVerify.installLicense();
                filterChain.doFilter(request, response);

            } catch (Exception e) {

                // 处理 installLicense() 失败的情况
                extracted(response, ErrorCode.LICENCE_ERROR);

            }
        }
    }

    private static void extracted(@NotNull HttpServletResponse response, ErrorCode errorCode) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");

        // 构建 JSON 响应
        JSONObject jsonResponse = new JSONObject();
        jsonResponse.put("code", errorCode.getCode());
        jsonResponse.put("message", errorCode.getMessage());

        // 写入响应体
        PrintWriter out = response.getWriter();
        out.print(jsonResponse);
        out.flush();
        out.close();
    }


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

跨域配置中使用

/**
 * 全局跨域配置
 *
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {
  
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();

        // 允许携带凭据
        config.setAllowCredentials(true);
        // 允许所有域名发起请求
        config.addAllowedOriginPattern("*");
        // 允许所有请求头
        config.addAllowedHeader("*");
        // 允许的请求方法
        config.addAllowedMethod("GET");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("OPTIONS");
        // 允许暴露的响应头
        config.addExposedHeader("*");
        // 为所有路径应用此CORS配置
        source.registerCorsConfiguration("/**", config);

        return new CorsFilter(source);
    }


    @Bean
    public CustomFilter customFilter() {
        return new CustomFilter();
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# aop切面

# 授权文件切面

@Aspect
@Component
public class LicenseInterceptor {

    @Resource
    private LicenseVerify licenseVerify;

    @Around("@annotation(licenseCheck)")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint, LicenseCheck licenseCheck) throws Throwable {

        try {

            licenseVerify.installLicense();
            return joinPoint.proceed();

        } catch (Exception e) {
            // 处理 installLicense() 失败的情况
            return ResultUtils.error(ErrorCode.LICENCE_ERROR);
        }



    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 登录切面

@Aspect
@Component
public class LoginInterceptor {

    @Resource
    UserService userService;

    @Around("@annotation(loginCheck)")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint, LoginCheck loginCheck) throws Throwable {

        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

        try {
            boolean isLogin = userService.isLogin(request);

            if (!isLogin) {
                return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
            }
            return joinPoint.proceed();
        } catch (Exception e) {
            return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 自定义 License 管理,创建、安装、校验等

/**
 * 自定义 License 管理,创建、安装、校验等
 */
@Slf4j
public class CustomLicenseManager extends LicenseManager {
    /**
     * XML 编码
     */
    private static final String XML_CHARSET = "UTF-8";
    /**
     * 默认 BUFSIZE
     */
    private static final int DEFAULT_BUFSIZE = 8 * 1024;

    public CustomLicenseManager(LicenseParam param) {
        super(param);
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: 重写 License 创建 </p>
     *
     * @param content LicenseContent
     * @param notary  LicenseNotary
     * @return byte[]
     * @version 1.0
     */
    @Override
    protected synchronized byte[] create(LicenseContent content, LicenseNotary notary) throws Exception {
        initialize(content);
        this.validateCreate(content);
        final GenericCertificate certificate = notary.sign(content);
        return getPrivacyGuard().cert2key(certificate);
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: 重写 License 安装 </p>
     *
     * @param key    key
     * @param notary LicenseNotary
     * @return de.schlichtherle.license.LicenseContent
     * @version 1.0
     */
    @Override
    protected synchronized LicenseContent install(final byte[] key, final LicenseNotary notary) throws Exception {
        final GenericCertificate certificate = getPrivacyGuard().key2cert(key);

        notary.verify(certificate);
        final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
        this.validate(content);
        setLicenseKey(key);
        setCertificate(certificate);

        return content;
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: 重写 License 校验 </p>
     *
     * @param notary LicenseNotary
     * @return de.schlichtherle.license.LicenseContent
     * @version 1.0
     */
    @Override
    protected synchronized LicenseContent verify(final LicenseNotary notary) throws Exception {
        GenericCertificate certificate;

        // Load license key from preferences,
        final byte[] key = getLicenseKey();
        if (null == key) {
            throw new NoLicenseInstalledException(getLicenseParam().getSubject());
        }

        certificate = getPrivacyGuard().key2cert(key);
        notary.verify(certificate);
        final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
        this.validate(content);
        setCertificate(certificate);

        return content;
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: 校验生成证书的参数信息 </p>
     *
     * @param content LicenseContent
     * @return void
     * @version 1.0
     */
    protected synchronized void validateCreate(final LicenseContent content) throws LicenseContentException {
        final Date now = new Date();
        final Date notBefore = content.getNotBefore();
        final Date notAfter = content.getNotAfter();
        if(null != notBefore && now.before(notBefore)){
            throw new BusinessException(ErrorCode.LICENSE_INVALID,"证书生效时间未到,请等待");
        }
        if(null != notAfter && now.after(notAfter)){
            throw new BusinessException(ErrorCode.LICENCE_ERROR,"证书已过授权时间,请上传新的授权文件");
        }
        if (null != notBefore && null != notAfter && notAfter.before(notBefore)) {
            throw new BusinessException(ErrorCode.LICENCE_ERROR,"证书生效时间不能晚于证书失效时间");
        }
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: 重写 License 验证 </p>
     *
     * @param content LicenseContent
     * @return void
     * @version 1.0
     */
    @Override
    protected synchronized void validate(final LicenseContent content) throws LicenseContentException {
        //校验自定义的License参数
        //License中可被允许的参数信息
        LicenseCheckModel expectedCheckModel = (LicenseCheckModel) content.getExtra();
        //当前服务器真实的参数信息
        LicenseCheckModel serverCheckModel = getServerInfos();

        if (expectedCheckModel != null && serverCheckModel != null) {
            //校验IP地址
            if (!checkIpAddress(expectedCheckModel.getIpAddress(), serverCheckModel.getIpAddress())) {
                throw new BusinessException(ErrorCode.LICENCE_ERROR, "当前服务器的IP没在授权范围内");
            }

            //校验Mac地址
            if (!checkIpAddress(expectedCheckModel.getMacAddress(), serverCheckModel.getMacAddress())) {
                throw new BusinessException(ErrorCode.LICENCE_ERROR,"当前服务器的Mac地址没在授权范围内");
            }

            //校验主板序列号
            if (!checkSerial(expectedCheckModel.getMainBoardSerial(), serverCheckModel.getMainBoardSerial())) {
                throw new BusinessException(ErrorCode.LICENCE_ERROR,"当前服务器的主板序列号没在授权范围内");
            }

            //校验CPU序列号
            if (!checkSerial(expectedCheckModel.getCpuSerial(), serverCheckModel.getCpuSerial())) {
                throw new BusinessException(ErrorCode.LICENCE_ERROR,"当前服务器的CPU序列号没在授权范围内");
            }
        }
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: XMLDecoder 解析 XML </p>
     *
     * @param encoded encoded
     * @return java.lang.Object
     *
     * @version 1.0
     */
    private Object load(String encoded) {
        BufferedInputStream inputStream = null;
        XMLDecoder decoder = null;
        try {
            inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET)));

            decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE), null, null);

            return decoder.readObject();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } finally {
            try {
                if (decoder != null) {
                    decoder.close();
                }
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                log.error("XMLDecoder解析XML失败", e);
            }
        }
        return null;
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: 获取当前服务器需要额外校验的 License 参数 </p>
     *
     * @return com.example.demo.license.LicenseCheckModel
     *
     */
    private LicenseCheckModel getServerInfos() {
        //操作系统类型
        String osName = System.getProperty("os.name").toLowerCase();
        AbstractServerInfos abstractServerInfos;

        //根据不同操作系统类型选择不同的数据获取方法
        if (osName.startsWith("windows")) {
            abstractServerInfos = new WindowsServerInfos();
        } else if (osName.startsWith("linux")) {
            abstractServerInfos = new LinuxServerInfos();
        } else {//其他服务器类型
            abstractServerInfos = new LinuxServerInfos();
        }

        return abstractServerInfos.getServerInfos();
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内 </p>
     *
     * @param expectedList expectedList
     * @param serverList   serverList
     * @return boolean
     *
     */
    private boolean checkIpAddress(List<String> expectedList, List<String> serverList) {
        if (expectedList != null && !expectedList.isEmpty()) {
            if (serverList != null && !serverList.isEmpty()) {
                for (String expected : expectedList) {
                    if (serverList.contains(expected.trim())) {
                        return true;
                    }
                }
            }

            return false;
        } else {
            return true;
        }
    }

    /**
     * <p>项目名称: true-license-demo </p>
     * <p>文件名称: CustomLicenseManager.java </p>
     * <p>方法描述: 校验当前服务器硬件(主板、CPU 等)序列号是否在可允许范围内 </p>
     *
     * @param expectedSerial expectedSerial
     * @param serverSerial   serverSerial
     * @return boolean
     */
    private boolean checkSerial(String expectedSerial, String serverSerial) {
        if (StringUtils.hasText(expectedSerial)) {
            if (StringUtils.hasText(serverSerial)) {
                return expectedSerial.equals(serverSerial);
            }
            return false;
        } else {
            return true;
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
上次更新: 2025/02/18 14:46:10
FreeMarker生成文件及WEB使用
Docker使用

← FreeMarker生成文件及WEB使用 Docker使用→

最近更新
01
jFlash使用 原创
03-24
02
中央仓库上传指南 原创
03-23
03
模板生成工具 原创
02-18
04
RTC实时时钟 原创
02-12
05
keil调试 原创
01-21
更多文章>
Copyright © 2023-2025 liyao52033  All Rights Reserved
备案号:鄂ICP备2023023964号-1    
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式