最新要闻
- 陕西省19个农产品区域公用品牌亮相第22届绿色食品博览会
- 环球微头条丨你为啥不升级Win11?数据:Win11份额历史新高 用过都说好?
- 每日热点:想薅羊毛?本轮汽车降价你需要知道事:10万以下车别想 理想蔚来等不跟定位豪华
- 今日最新!NVIDIA对RTX 4070定价有信心:3060的功耗输出3080性能
- 每日时讯!气温跳水!今年来范围最广最强雨雪上线:北方多地降温达10℃
- 时代终结!50年的大众经典神车高尔夫要停产了
- 送牙刷:纳美氨基酸牙膏4支/19.9元清仓
- 重磅!“笔吧”称将转行评测飞机杯?看了眼日期我悟了
- 【世界聚看点】速度可达600km/h!我国自研高温超导电动悬浮交通系统实现首次悬浮运行
- 天天精选!马斯克74岁母亲来武汉游玩:频繁晒照 称中国悠久的历史令人着迷
- 哥伦比亚发布橙色预警!这座火山,或在未来几天喷发?曾致超2.3万人死亡
- 今日聚焦!建议友商跟进!长安深蓝豪送大礼:已提车用户享终身整车质保
- 热点在线丨专家:发展电车一年可节约500亿美元石油进口、这钱拿来干什么不好
- 天天观点:送给老师的便宜小礼物
- 理想杀疯了!3月交付破2万辆 成新势力新老大 李想:30-50万SUV市场占比已接近20%
- 讯息:波兰蠢驴营收向好
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
快报:求求你别乱脱敏了!MyBatis 插件 + 注解轻松实现数据脱敏,So easy~!
问题
在项目中需要对用户敏感数据进行脱敏处理,例如身份号、手机号等信息进行加密再入库。
(资料图)
解决思路
- 就是:一种最简单直接的方式,在所有涉及数据敏感的查询到对插入时进行密码加解密
- 方法二:有方法一到出现对所有重大问题的影响,需要考虑到问题的出现,并且需要考虑可能出现的组员时添加数据的方法。
最后决定采用mybatis的插件在mybatis的SQL执行和结果填充操作上进行切入。上层业务调用不再需要考虑数据的加敏同时也保证了数据的加解密
Mybatis 插件原理
Mybatis 的是通过拦截器实现的,Mabatis 支持对当事人进行拦截
实现
- 设置对参数中带有敏感参数字段的数据时进行加密
- 对返回的结果进行解密处理
根据不同的要求,我们只需要对ParameterHandler
和ResultSetHandler
进行切入。
定义特定注解,在切入时需要检查字段中是否包含注解来是否加解密
加注解
定义SensitiveData
注解
import java.lang.annotation.*;/** * 该注解定义在类上 * 插件通过扫描类对象是否包含这个注解来决定是否继续扫描其中的字段注解 * 这个注解要配合EncryptTransaction注解 **/@Inherited@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface SensitiveData {}
定义EncryptTransaction
注解
import java.lang.annotation.*;/** * 该注解有两种使用方式 * ①:配合@SensitiveData加在类中的字段上 * ②:直接在Mapper中的方法参数上使用 **/@Documented@Inherited@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)public @interface EncryptTransaction {}
加解密工具类
解密
package sicnu.cs.ich.common.interceptor.transaction.service;public interface IDecryptUtil { /** * 解密 * * @param result resultType的实例 * @return T * @throws IllegalAccessException 字段不可访问异常 */ T decrypt(T result) throws IllegalAccessException;}
加密接口
package sicnu.cs.ich.common.interceptor.transaction.service;import java.lang.reflect.Field;public interface IEncryptUtil { /** * 加密 * * @param declaredFields 加密字段 * @param paramsObject 对象 * @param 入参类型 * @return 返回加密 * @throws IllegalAccessException 不可访问 */ T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException;}package sicnu.cs.ich.common.interceptor.transaction.service.impl;import org.springframework.stereotype.Component;import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;import sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil;import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;import java.lang.reflect.Field;import java.util.Objects;@Componentpublic class DecryptImpl implements IDecryptUtil { /** * 解密 * * @param result resultType的实例 */ @Override public T decrypt(T result) throws IllegalAccessException { //取出resultType的类 Class> resultClass = result.getClass(); Field[] declaredFields = resultClass.getDeclaredFields(); for (Field field : declaredFields) { //取出所有被DecryptTransaction注解的字段 EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class); if (!Objects.isNull(encryptTransaction)) { field.setAccessible(true); Object object = field.get(result); //String的解密 if (object instanceof String) { String value = (String) object; //对注解的字段进行逐一解密 try { field.set(result, DBAESUtil.decrypt(value)); } catch (Exception e) { e.printStackTrace(); } } } } return result; }}
加密实现类
package sicnu.cs.ich.common.interceptor.transaction.service.impl;import com.fasterxml.jackson.databind.ObjectReader;import org.springframework.stereotype.Component;import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;import sicnu.cs.ich.common.interceptor.transaction.service.IEncryptUtil;import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;import java.io.ObjectInputStream;import java.lang.reflect.Field;import java.util.Arrays;import java.util.Objects;import java.util.Random;@Componentpublic class EncryptUtilImpl implements IEncryptUtil { @Override public T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException { //取出所有被EncryptTransaction注解的字段 for (Field field : declaredFields) { EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class); if (!Objects.isNull(encryptTransaction)) { field.setAccessible(true); Object object = field.get(paramsObject); //暂时只实现String类型的加密 if (object instanceof String) { String value = (String) object; //加密 try { field.set(paramsObject, DBAESUtil.encrypt(value)); } catch (Exception e) { e.printStackTrace(); } } } } return paramsObject; }}
模拟类
package sicnu.cs.ich.common.interceptor.transaction.service.impl;import org.springframework.stereotype.Component;import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;import sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil;import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;import java.lang.reflect.Field;import java.util.Objects;@Componentpublic class DecryptImpl implements IDecryptUtil { /** * 解密 * * @param result resultType的实例 */ @Override public T decrypt(T result) throws IllegalAccessException { //取出resultType的类 Class> resultClass = result.getClass(); Field[] declaredFields = resultClass.getDeclaredFields(); for (Field field : declaredFields) { //取出所有被DecryptTransaction注解的字段 EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class); if (!Objects.isNull(encryptTransaction)) { field.setAccessible(true); Object object = field.get(result); //String的解密 if (object instanceof String) { String value = (String) object; //对注解的字段进行逐一解密 try { field.set(result, DBAESUtil.decrypt(value)); } catch (Exception e) { e.printStackTrace(); } } } } return result; }}
加解密工具类
package sicnu.cs.ich.common.util.keyCryptor;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.util.Base64;public class DBAESUtil { private static final String DEFAULT_V = "6859505890402435"; // 自己填写 private static final String KEY = "***"; private static final String ALGORITHM = "AES"; private static SecretKeySpec getKey() { byte[] arrBTmp = DBAESUtil.KEY.getBytes(); // 创建一个空的16位字节数组(默认值为0) byte[] arrB = new byte[16]; for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) { arrB[i] = arrBTmp[i]; } return new SecretKeySpec(arrB, ALGORITHM); } /** * 加密 */ public static String encrypt(String content) throws Exception { final Base64.Encoder encoder = Base64.getEncoder(); SecretKeySpec keySpec = getKey(); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec iv = new IvParameterSpec(DEFAULT_V.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); byte[] encrypted = cipher.doFinal(content.getBytes()); return encoder.encodeToString(encrypted); } /** * 解密 */ public static String decrypt(String content) throws Exception { final Base64.Decoder decoder = Base64.getDecoder(); SecretKeySpec keySpec = getKey(); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec iv = new IvParameterSpec(DEFAULT_V.getBytes()); cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); byte[] base64 = decoder.decode(content); byte[] original = cipher.doFinal(base64); return new String(original); }}
推荐一个开源免费的 Spring Boot 最全教程:
https://github.com/javastacks/spring-boot-best-practice
插件实现
参数插件ParameterInterceptor
切入mybatis设置参数时对敏感数据进行加密
Mybatis插件的使用就是通过实现Mybatis中的Interceptor
接口
再@Intercepts
注解
// 使用mybatis插件时需要定义签名// type标识需要切入的Handler// method表示要要切入的方法@Intercepts({@Signature(type = ParameterHandler.class, method = “setParameters”, args = PreparedStatement.class),})package sicnu.cs.ich.common.interceptor.transaction;import com.baomidou.mybatisplus.core.MybatisParameterHandler;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.executor.parameter.ParameterHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.plugin.*;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.stereotype.Component;import org.springframework.util.CollectionUtils;import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;import sicnu.cs.ich.common.interceptor.transaction.service.IEncryptUtil;import sicnu.cs.ich.common.util.keyCryptor.DBAESUtil;import java.lang.annotation.Annotation;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.sql.PreparedStatement;import java.util.*;@Slf4j// 注入Spring@Component@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),})public class ParameterInterceptor implements Interceptor { @Autowired private IEncryptUtil IEncryptUtil; @Override public Object intercept(Invocation invocation) throws Throwable { //@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler //若指定ResultSetHandler ,这里则能强转为ResultSetHandler MybatisParameterHandler parameterHandler = (MybatisParameterHandler) invocation.getTarget(); // 获取参数对像,即 mapper 中 paramsType 的实例 Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject"); parameterField.setAccessible(true); //取出实例 Object parameterObject = parameterField.get(parameterHandler); // 搜索该方法中是否有需要加密的普通字段 List paramNames = searchParamAnnotation(parameterHandler); if (parameterObject != null) { Class> parameterObjectClass = parameterObject.getClass(); //对类字段进行加密 //校验该实例的类是否被@SensitiveData所注解 SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class); if (Objects.nonNull(sensitiveData)) { //取出当前当前类所有字段,传入加密方法 Field[] declaredFields = parameterObjectClass.getDeclaredFields(); IEncryptUtil.encrypt(declaredFields, parameterObject); } // 对普通字段进行加密 if (!CollectionUtils.isEmpty(paramNames)) { // 反射获取 BoundSql 对象,此对象包含生成的sql和sql的参数map映射 Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql"); boundSqlField.setAccessible(true); BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler); PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0]; // 改写参数 processParam(parameterObject, paramNames); // 改写的参数设置到原parameterHandler对象 parameterField.set(parameterHandler, parameterObject); parameterHandler.setParameters(ps); } } return invocation.proceed(); } private void processParam(Object parameterObject, List params) throws Exception { // 处理参数对象 如果是 map 且map的key 中没有 tenantId,添加到参数map中 // 如果参数是bean,反射设置值 if (parameterObject instanceof Map) { @SuppressWarnings("unchecked") Map map = ((Map) parameterObject); for (String param : params) { String value = map.get(param); map.put(param, value==null?null:DBAESUtil.encrypt(value)); }// parameterObject = map; } } private List searchParamAnnotation(ParameterHandler parameterHandler) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException { Class handlerClass = MybatisParameterHandler.class; Field mappedStatementFiled = handlerClass.getDeclaredField("mappedStatement"); mappedStatementFiled.setAccessible(true); MappedStatement mappedStatement = (MappedStatement) mappedStatementFiled.get(parameterHandler); String methodName = mappedStatement.getId(); Class> mapperClass = Class.forName(methodName.substring(0, methodName.lastIndexOf("."))); methodName = methodName.substring(methodName.lastIndexOf(".") + 1); Method[] methods = mapperClass.getDeclaredMethods(); Method method = null; for (Method m : methods) { if (m.getName().equals(methodName)) { method = m; break; } } List paramNames = null; if (method != null) { Annotation[][] pa = method.getParameterAnnotations(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < pa.length; i++) { for (Annotation annotation : pa[i]) { if (annotation instanceof EncryptTransaction) { if (paramNames == null) { paramNames = new ArrayList<>(); } paramNames.add(parameters[i].getName()); } } } } return paramNames; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { }}
返回值插件ResultSetInterceptor
package sicnu.cs.ich.common.interceptor.transaction;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.executor.resultset.ResultSetHandler;import org.apache.ibatis.plugin.*;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.stereotype.Component;import org.springframework.util.CollectionUtils;import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;import java.sql.Statement;import java.util.ArrayList;import java.util.Objects;import java.util.Properties;@Slf4j@Component@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})public class ResultSetInterceptor implements Interceptor { @Autowired private sicnu.cs.ich.common.interceptor.transaction.service.IDecryptUtil IDecryptUtil; @Override public Object intercept(Invocation invocation) throws Throwable { //取出查询的结果 Object resultObject = invocation.proceed(); if (Objects.isNull(resultObject)) { return null; } //基于selectList if (resultObject instanceof ArrayList) { @SuppressWarnings("unchecked") ArrayList resultList = (ArrayList) resultObject; if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) { for (Object result : resultList) { //逐一解密 IDecryptUtil.decrypt(result); } } //基于selectOne } else { if (needToDecrypt(resultObject)) { IDecryptUtil.decrypt(resultObject); } } return resultObject; } private boolean needToDecrypt(Object object) { Class> objectClass = object.getClass(); SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class); return Objects.nonNull(sensitiveData); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { }}
使用
注意解在实体类上
import lombok.*;import org.springframework.security.core.userdetails.UserDetails;import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;import sicnu.cs.ich.api.common.annotations.transaction.SensitiveData;@With@Builder@Data@NoArgsConstructor@AllArgsConstructor@SensitiveData // 插件只对加了该注解的类进行扫描,只有加了这个注解的类才会生效public class User implements Serializable { private Integer id; private String username; private String openId; private String password; // 表明对该字段进行加密 @EncryptTransaction private String email; // 表明对该字段进行加密 @EncryptTransaction private String mobile; private Date createTime; private Date expireTime; private Boolean status = true;}
注解在参数上
import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;import sicnu.cs.ich.api.common.annotations.transaction.EncryptTransaction;@Mapperpublic interface UserMapper extends BaseMapper { // 只需要在参数前加上@EncryptTransaction 即可 long countByEmail(@EncryptTransaction @Param("email") String email); long countByMobile(@EncryptTransaction @Param("mobile") String mobile);}
版权声明:本文为CSDN博主「shenyang1026」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/relosy/article/details/123494036
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!
关键词:
-
快报:求求你别乱脱敏了!MyBatis 插件 + 注解轻松实现数据脱敏,So easy~!
问题在项目中需要对用户敏感数据进行脱敏处理,例如身份号、手机号等信息进行加密再入库。解决思路就是...
来源: 快报:求求你别乱脱敏了!MyBatis 插件 + 注解轻松实现数据脱敏,So easy~!
博客园添加打赏按钮
Pytorch 备忘清单_开发速查表分享
中金普洛斯REIT第一次扩募并新购入基础设施项目正式获批
吸收外资扩量更要提质
年内19只债基降低管理费 一级债基成主力
环球热点!当市场恐慌时,美联储会采取行动吗?
陕西省19个农产品区域公用品牌亮相第22届绿色食品博览会
环球微头条丨你为啥不升级Win11?数据:Win11份额历史新高 用过都说好?
每日热点:想薅羊毛?本轮汽车降价你需要知道事:10万以下车别想 理想蔚来等不跟定位豪华
今日最新!NVIDIA对RTX 4070定价有信心:3060的功耗输出3080性能
每日时讯!气温跳水!今年来范围最广最强雨雪上线:北方多地降温达10℃
时代终结!50年的大众经典神车高尔夫要停产了
送牙刷:纳美氨基酸牙膏4支/19.9元清仓
重磅!“笔吧”称将转行评测飞机杯?看了眼日期我悟了
【世界聚看点】速度可达600km/h!我国自研高温超导电动悬浮交通系统实现首次悬浮运行
天天精选!马斯克74岁母亲来武汉游玩:频繁晒照 称中国悠久的历史令人着迷
全球热资讯!基于zynq的OV5640摄像头的sobel算子边缘检测
天天新动态:小米路由器3C刷入Breed和OpenWrt
【世界播资讯】Microsoft Visual Studio 2022 透明背景设置
全球快播:相同基准点的多个rvt BIM模型数据配准后位置有错位偏差问题处理
哥伦比亚发布橙色预警!这座火山,或在未来几天喷发?曾致超2.3万人死亡
今日聚焦!建议友商跟进!长安深蓝豪送大礼:已提车用户享终身整车质保
热点在线丨专家:发展电车一年可节约500亿美元石油进口、这钱拿来干什么不好
天天观点:送给老师的便宜小礼物
头条焦点:Redis——内存淘汰策略
理想杀疯了!3月交付破2万辆 成新势力新老大 李想:30-50万SUV市场占比已接近20%
讯息:波兰蠢驴营收向好
世界今日讯!斗罗大陆游戏激活码有哪些(斗罗大陆游戏激活码是多少)
全球今日报丨Java 基础 -- NIO 多人聊天室
环球热推荐:四元数法
环球快资讯丨iPhone大面积bug:苹果自带天气崩了 无任何数据
shell 脚本之一键部署安装 Nginx
环球实时:国产PCSK9抑制剂 ,即将迎来商业爆发?
全球观速讯丨韩国主持人流鼻血仍淡定直播 本人回应没事:网友看笑但直呼真敬业
热文:钟南山亲自颁发!理想L9获颁“五星健康车”:383.4分创造历史
全球热头条丨文峰股份:3月31日融券卖出5300股,融资融券余额1.56亿元
全球动态:读SQL进阶教程笔记04_集合运算
加满一箱油少花13元!油价迎今年来最大降幅
天天热资讯!2020广州大佛寺中秋佳节拜月法会安排一览
Swift与OC混编
环球信息:知乎使用指南
汉唐元三车齐发!比亚迪乘用车在西班牙正式开售
【世界独家】不虚标!比亚迪汉EV冠军版实测光电续航达成率93.1%
世界今热点:中国最畅销手机出炉:“十三香”传奇上演 安卓高端难成
内存狂魔第一次冲上8200MHz!芝奇带来96GB DDR5
佛山企业标准“领跑者”数量连续三年全国第一
当前视点!腮腺炎初期有什么症状?_腮腺炎初期有什么症状
全球时讯:深度学习——用简单的线性模型构建识别鸟与飞机模型
Redhat/CentOS Linux 系统进入单用户模式
天天即时看!万恶的环境 二 java
今日要闻!验证码被攻破!谷歌AI已能精准识别 GPT-4则装瞎求人帮忙
美女博主在家拍擦边视频?穿瑜伽服涨粉百万!真相是...
全球今头条!3DMark加入AMD FSR专项测试:终于追上NVIDIA、Intel
全球报道:广汽埃安AION Y Plus 2023款上市:14万级唯一纯电610KM续航
天天信息:V观财报|科达制造股东广东联塑短线交易致歉
世界消息!Teamcenter_SOA开发:使用查询构建器查询数据
即时:flatMap
热讯:React Native 备忘清单_开发速查表分享
世界信息:5.98万起售还嫌贵?五菱缤果上市即热销:当天交付522台
每日热议!想买车的抓紧 广州新能源汽车也有补贴了:最高1万元
世界微头条丨《原神》开发商新作:米哈游《崩坏:星穹铁道》预约超2000万
环球热推荐:米香四溢:袁隆平品牌隆平农场东北晶米10斤39.9元发车
华硕ROG游戏手机7真机曝光:无挖孔全面屏、后置三摄
天天讯息:华天科技2022年营收119亿元 拟实施先进封测研发及产业化项目
当前焦点!Leetcode Practice --- 栈和队列
我的第一个项目(七):(解决问题)Vue中canvas无法绘制图片
观点:确认引进内地!《小美人鱼》新泄露镜头:爱丽儿深情抚摸王子的脸
全球首个商用海底数据中心在海南下水:算力高、散热不费电
今日关注:不挑路面、转得很稳 仰望U8如何实现原地掉头?官方详解
大学生应聘饭店洗碗工被HR婉拒 HR:第一份工作很重要 这会害了他
不等发布会!vivo X Fold2真机抢先看:素皮+玻璃设计惊艳
现场丨圆桌对话:抓住确定性——楼宇经济的大周期和小趋势
讯息:awk 处理 Git 提交信息生成 Release Note
最长上升子序列 II
视焦点讯!day3 函数的定义和调用,练习编写简单的程序(记录1)
天天热文:内存/SSD白菜价甩卖 美光芯片大减产:工厂停机率创纪录
任泽平:在中国做生意没任何理由抱怨 燃油车正迎来诺基亚时刻
世界新资讯:宏碁推出W系列4K电视:QLED面板、30W扬声器
油价“二连降” 今年来最大降幅!加满一箱油少花13元
【全球热闻】网络时钟同步设备(NTP时间同步服务器)技术设计应用方案
全球实时:记录--你可能忽略的10种JavaScript快乐写法
聚焦:无所畏惧的求和题解
今亮点!商品日报(3月31日):焦炭低位反弹菜油补涨 纸浆、玻璃主力合约跌超2%
保姆级教程!12306官方详解“免费坐高铁”
环球速递!RTX 40系显卡才能“撑得住”!《赛博朋克2077》实现路径光追
夏季必备 圈叉潮品纯棉印花T恤/短裤24.9元起
【新视野】编程神童立志写最棒的程序改变世界 严重偏科只能选职高 妈妈无奈
世界视讯!成龙自豪发声:不是我要去好莱坞 而是好莱坞要我
“19鑫苑01”到期日期将延期一年
Excel批量检查5列数据是否一致(存在不规则空值)
【密码管理器】上海道宁为您提供存储和使用强密码的简单方法工具软件——1Password
【环球报资讯】MQTT协议介绍
世界热资讯!开心档之Go 语言环境安装
DIM中的一些知识点(慢更)
新消息丨每日机构分析:3月31日
全球实时:国家发展改革委:国内汽、柴油价格每吨分别降低335元和320元
焦点简讯:吃日料、听京剧 库克时隔3年再访中国:6年前还去过ofo小黄车总部
全球观焦点:酷派新品发布会定档4月3日:三款新机待发
世界聚焦:2023增长最快的手机品牌!一加Ace 2首销日销量在第三方平台遥遥领先