测试上传

环境:macOS、CloudFlare-Bed

1、ssl 证书问题,解决方法两个(任选其一):

  • 设置证书
  • 上传插件中,POST参数中,添加禁止 ssl 验证
const opts = {  
  // 添加这行
  rejectUnauthorized: false,  
  method: 'POST',  
  url: url,  
  headers: headers,  
  formData: formData  
}

2、本地网络不行,需要使用代理

  • 使用代理需要注意,协议必须是 http,而可以是 socks

Apple Silicon 虚拟旁路网关搭建

前言

前段时间一直在忙,而且因为你懂的原因,ip 封锁也很严重,也没太多时间弄这个 vps。就趁现在到杭州的间隙,搞一搞这个 vps 顺便用 Mac 实现一个旁路网关,省下一个软路由。

通过 UTM 方式安装 OpenWrt,会有磁盘容量过小的问题

  • 解决方案一:解压出镜像后安装之前,执行命令:qemu-img resize openwrt-22.03.4-armvirt-64-rootfs-squashfs.img +1G
  • 也可以参考:Change disk image size #2636

报错:OpenWRT br-lan: received packet on eth0 with own address as source address

  • 可能是虚拟机的虚拟网卡 mac 地址跟真实网卡 mac 地址冲突了,虚拟网卡可以随机生成一个新的 mac 地址,然后重启即可

根据t帖子:openwrt_in_qemu_aarch64_on_apple_silicon_macos_m1_hardware_native 安装后,OpenWrt 只能ping通局域网地址。

  • 在启动 Openwrt 前,需要确保-append root=/dev/vda启动参数是否已经添加,没添加的话,启动后会无限重启

  • 首先设置vi /etc/config/network ,按自己需求更改下面带注释的两行信息即可

config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fdd6:7224:70e7::/48'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'eth0'
	option ipv6 '0'

config interface 'lan'
	option device 'br-lan'
	option proto 'static'
	option ipaddr '192.168.1.234'   // 登陆 OpenWrt Web 的 Url
	option netmask '255.255.255.0'
	option ip6assign '60'
	option gateway '192.168.1.1'  // 指向主路由的网关
	list dns '192.168.1.1'

如果网络配置不太熟悉的小伙伴,在更改完上述的option ipaddr后,可以在浏览器输入192.168.1.234登录 OpenWrt 后台进行后续操作

Springboot 配置文件以及敏感信息脱敏

一个项目的配置文件往往保存了许多账号密码之类的重要信息,明文是非常不安全的,那么进行脱敏处理就很有必要了,下面看看如何操作吧~

首先介绍一下主角:Jasypt

这个开源工具提供了相当丰富的加密方式,具体可以参考其文档。

首先引入相关依赖:

 <!--配置文件加密-->
 <dependency>
     <groupId>com.github.ulisesbocchio</groupId>
     <artifactId>jasypt-spring-boot-starter</artifactId>
     <version>3.0.5</version>
 </dependency>

然后创建加密解密工具类:

public class JasyptUtil {
    // 盐:随机输入即可
    private static final String SALT = "SALT_CODE";

    // 加密
    private static void encrypt() {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword(SALT);
        System.out.println("username: " + encryptor.encrypt("root"));
        System.out.println("password: " + encryptor.encrypt("123456"));
    }

    // 解密
    private static void decrypt() {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword(SALT);
        System.out.println("username: " + encryptor.decrypt("w1tz6At82gnb4n6BZmgkaQ=="));
        System.out.println("password: " + encryptor.decrypt("UjPD7C8JowwL7vLL80iSdA=="));
    }

    // 根据你输入的盐值计算出来加密的结果
    public static void main(String[] args) {
        // w1tz6At82gnb4n6BZmgkaQ==
        // UjPD7C8JowwL7vLL80iSdA==
        encrypt();

        // root
        // 123456
        decrypt();
    }
}

注意:当生成了目标密码之后,最好清除盐值,避免泄露

JSOUP 获取 Nikke cdkey

这个网站的 cdkey 更新很及时,写了几行代码自动抓取信息,再配合邮件功能和定时器,看还会不会抢不到 XD

@Service
public class NikkeCDKeyService extends ServiceImpl<NikkeCdkeyMapper, NikkeCdkey> {

    @Resource
    private NikkeCdkeyMapper nikkeCdkeyMapper;

    // 爬取 cdkey 信息
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> getCdKeyInfoByCSS() {
        String url = "https://gamewith.jp/nikke/article/show/371014";
        String userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36";
        Document doc = Jsoup.connect(url)
                .userAgent(userAgent)
                .get();
        // 按日期分类出来的 cdkey 表格, 一个日期内可能有多个 cdkey
        // 主要是用来获取日期天数
        Elements codeTableElements = doc.select("#article-body > div.code"); // size:12
        // 总 h3 标签数对应的元素集合
        Elements h3Elements = doc.select("#article-body > h3");
        List<NikkeCdkey> nikkeCdkeyList = new ArrayList<>();
        for (int j = 0, codeTableElementsSize = codeTableElements.size(); j < codeTableElementsSize; j++) {
            Element element = codeTableElements.get(j);
            // 实际发布 cdkey 日期的 h3 标签
            String title = h3Elements.get(j).text();
            Elements cdkeyElements = element.select("table > tbody > tr > td:nth-child(1)");
            Elements rewardElements = element.select("table > tbody > tr > td:nth-child(2)");
            // cdkeyElements 与 rewardElements 是一一对应的, 用哪个 size 都一样
            for (int i = 0; i < cdkeyElements.size(); i++) {
                NikkeCdkey nikkeCdkey = new NikkeCdkey();
                String cdkey = cdkeyElements.get(i).text();
                String reward = rewardElements.get(i).text();
                nikkeCdkey.setTitle(title);
                nikkeCdkey.setCdkey(cdkey);
                nikkeCdkey.setReward(reward);
                nikkeCdkeyList.add(nikkeCdkey);
            }
        }
        int i = nikkeCdkeyMapper.saveOrUpdateByCdkey(nikkeCdkeyList);
        return Result.ok("集合大小: " + i);
    }

    // 展示 cdkey 信息
    public Result<Object> showCDKey() {
        List<NikkeCdkey> nikkeCdkeyList = nikkeCdkeyMapper.selectList(null);
        return Result.ok(nikkeCdkeyList);
    }

}

关于微服务之间用户信息传递出现问题

最近需要搭建一个货运相关的微服务架构,基本上都挺顺利,但是遇到了一个请求头传递信息的问题:

业务场景:某些模块是通过请求头传递用户 ID,以供Web子服务调用(feign)

问题:Web 子模块无法获取请求头信息,导致无法完成业务流程

解决方案:添加一个拦截器,统一拦截系统的request请求,再设置 request 信息即可
@Component
public class FeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        //  微服务远程通过feign互相调用时,获取相关信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //  再设置 header 数据
        requestTemplate.header("userTempId", request.getHeader("userTempId"));
        requestTemplate.header("userId", request.getHeader("userId"));
    }
}

若发现不生效,可能是启动类没扫描到拦截器,添加扫描即可,比如:

@SpringBootApplication
@EnableFeignClients("com.xxx.yyy.zzz")
@ComponentScan("com.xxx.yyy.zzz")
public class WebSysApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebAllApplication.class, args);
    }
}

爬虫限速Demo:控制接口调用的速率

使用guava漏桶算法实现速率控制

    @Test
    public void testRateLimiterParallel() throws Exception {

        Stopwatch started = Stopwatch.createStarted();
        ExecutorService executorService = Executors.newFixedThreadPool(140);
        RateLimiter rateLimiter = RateLimiter.create(140);//创建限流器 每秒只能通过140个请求
        ArrayList<CompletableFuture> objects = Lists.newArrayList();
        IntStream.range(0, 200).forEach(item -> {
            rateLimiter.acquire();
            System.out.println("等待:"+rateLimiter.acquire());
            System.out.println("等待结束"+item);
            //多线程模拟请求
            CompletableFuture<Void> cFeature = CompletableFuture.runAsync(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(200);
                    System.out.println("输出" + item + ": 当前秒数:" + LocalDateTime.now().getSecond());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, executorService);
            objects.add(cFeature);
        });
        CompletableFuture.allOf(objects.toArray(new CompletableFuture[objects.size()])).join();
        System.out.println("执行完毕");
        started.stop();
        long elapsed = started.elapsed(TimeUnit.MILLISECONDS);
        System.out.println("耗时:" + elapsed);

    }