Data Masking安装和使用指南
Data Masking安装和使用指南
1. Data masking 简介
软件开发的最后阶段,是需要尽量真实的数据来作为基础测试软件的一系列功能。尤其是企业信息化系统,ERP、BI、EPM等这些大型系统实施或开发的时候,对于基础数据的要求很严格,很多时候都是直接克隆生产环境的数据来进行软件系统的测试,但是随之而来的影响却是深远的,生产数据中,首先它是一个真实的数据,透过数据基本上掌握了整个企业的资料。其次,在这当中包含很多敏感数据,不光是敏感数据,而且还是真实的敏感数据。如果在测试环境中发生了信息泄露问题,那么对于企业数据安全将造成致命的后果。
ORACLRE在OEM 10g之后提供Data Masking pack组件,解决从生产环境的数据向测试环境(或者开发环境)导入时可能会产生的数据内容安全问题,data masking特点是:按照指定的规则进行数据转换,可以通过规则控制最终生成的数据格式;维护数据表间的引用关系;通过EM图形界面来定义Data Masking,容易理解和使用。流程如下图所示:
2. 安装Data Masking Pack组件
OEM10g版本是10.2.0.4,提供了新6个pack功能,Data Masking Pack就是其中之一。因此,要使用安装,升级至最新的版本。使用Grid Control+OEM方便得使用管理Data Masking。
注:使用Data Masking的前提条件,安装Grid Control,升级OEM10g到10.2.0.4版本。
安装Grid Control软件。
下载介质:
从oracle官方网站下载gc_102011_linux.zip,补丁包GridControl_10.2.0.4_LINUX.zip。
具体安装步骤详见文档:Oracle Grid Control 10.2.0.4 for Linux 安装和配置指南。
3. 通过Grid Control使用Data Masking
1. 进入管理界面
使用SYSMAN用户登录 Oracle Enterprise Manger 10g(Grid Control)。
进入Grid Control主界面后,点击“Targets”
进入Targets管理界面,点击“Database”
选择要进行管理的数据库,如图,进入“emrep”(数据库名)进行配置。
进入要管理的数据库之后,点击“Administration”。
进入管理界面后,看见Data Masking pack组件选项,点击“Definition”进入Data Masking进行管理与配置。
2. 定义/配置Data Masking
1. 添加一个定义
- 1. 选择需要mask的数据库,点击“Mask”,进入配置界面
- 2. 选择要mask的表,以及对应的列,并对格式进行定义。
- 3. 选择列转换规则(Array List,Fixed Number,Fixed string等转化规则,根据实际需求选择)
注:对定义TT用户的Mask表的列选择“随机字符串”转化规则
注:指定随机字符串的长度
2. 生成列转化规则脚本
脚本的内容为:
set echo off set feedback off set serveroutput on set pagesize 0 spool /u02/app/oragrid/OracleHomes/db10g/dbs/masking14.log -- Script Header Section -- ============================================== -- functions and procedures CREATE OR REPLACE PROCEDURE mgmt$mask_sendMsg (msg IN VARCHAR2) IS msg1 VARCHAR2(1020); len INTEGER := length(msg); i INTEGER := 1; BEGIN dbms_output.enable (1000000); LOOP msg1 := SUBSTR (msg, i, 255); dbms_output.put_line (msg1); len := len - 255; i := i + 255; EXIT WHEN len <= 0; END LOOP; END mgmt$mask_sendMsg; / CREATE OR REPLACE PROCEDURE mgmt$mask_errorExit (msg IN VARCHAR2) IS BEGIN mgmt$mask_sendMsg (msg); mgmt$mask_sendMsg ('errorExit!'); END mgmt$mask_errorExit; / CREATE OR REPLACE PROCEDURE mgmt$mask_errorExitOraError (msg IN VARCHAR2, errMsg IN VARCHAR2) IS BEGIN mgmt$mask_sendMsg (msg); mgmt$mask_sendMsg (errMsg); mgmt$mask_sendMsg ('errorExitOraError!'); END mgmt$mask_errorExitOraError; / CREATE OR REPLACE PROCEDURE mgmt$mask_checkDBAPrivs (user_name IN VARCHAR2) AUTHID CURRENT_USER IS granted_role REAL := 0; BEGIN EXECUTE IMMEDIATE 'SELECT 1 FROM SYS.DBA_ROLE_PRIVS WHERE GRANTED_ROLE = ''DBA'' AND GRANTEE = (SELECT USER FROM DUAL)' INTO granted_role; EXCEPTION WHEN NO_DATA_FOUND THEN mgmt$mask_sendMsg ( 'WARNING checking privileges... User Name: ' || user_name); mgmt$mask_sendMsg ( 'User does not have DBA privs. ' ); mgmt$mask_sendMsg ( 'The script will fail if it tries to perform operations for which the user lacks the appropriate privilege. ' ); END mgmt$mask_checkDBAPrivs; / CREATE OR REPLACE PROCEDURE mgmt$mask_setUpJobTable (script_id IN INTEGER, job_table IN VARCHAR2, step_num OUT INTEGER) AUTHID CURRENT_USER IS ctsql_text VARCHAR2(200) := 'CREATE TABLE ' || job_table || '(SCRIPT_ID NUMBER, LAST_STEP NUMBER, unique (SCRIPT_ID))'; itsql_text VARCHAR2(200) := 'INSERT INTO ' || job_table || ' (SCRIPT_ID, LAST_STEP) values (:1, :2)'; stsql_text VARCHAR2(200) := 'SELECT last_step FROM ' || job_table || ' WHERE script_id = :1'; TYPE CurTyp IS REF CURSOR; -- define weak REF CURSOR type stsql_cur CurTyp; -- declare cursor variable BEGIN step_num := 0; BEGIN EXECUTE IMMEDIATE ctsql_text; EXCEPTION WHEN OTHERS THEN NULL; END; BEGIN OPEN stsql_cur FOR -- open cursor variable stsql_text USING script_id; FETCH stsql_cur INTO step_num; IF stsql_cur%FOUND THEN NULL; ELSE EXECUTE IMMEDIATE itsql_text USING script_id, step_num; COMMIT; step_num := 1; END IF; CLOSE stsql_cur; EXCEPTION WHEN OTHERS THEN mgmt$mask_errorExit ('ERROR selecting or inserting from table: ' || job_table); return; END; return; EXCEPTION WHEN OTHERS THEN mgmt$mask_errorExit ('ERROR accessing table: ' || job_table); return; END mgmt$mask_setUpJobTable; / CREATE OR REPLACE PROCEDURE mgmt$mask_deleteJobTableEntry(script_id IN INTEGER, job_table IN VARCHAR2, step_num IN INTEGER, highest_step IN INTEGER) AUTHID CURRENT_USER IS delete_text VARCHAR2(200) := 'DELETE FROM ' || job_table || ' WHERE SCRIPT_ID = :1'; BEGIN IF step_num <= highest_step THEN return; END IF; BEGIN EXECUTE IMMEDIATE delete_text USING script_id; IF SQL%NOTFOUND THEN mgmt$mask_errorExit ('ERROR deleting entry from table: ' || job_table); return; END IF; EXCEPTION WHEN OTHERS THEN mgmt$mask_errorExit ('ERROR deleting entry from table: ' || job_table); return; END; COMMIT; END mgmt$mask_deleteJobTableEntry; / CREATE OR REPLACE PROCEDURE mgmt$mask_setStep (script_id IN INTEGER, job_table IN VARCHAR2, step_num IN INTEGER) AUTHID CURRENT_USER IS update_text VARCHAR2(200) := 'UPDATE ' || job_table || ' SET last_step = :1 WHERE script_id = :2'; BEGIN -- update job table EXECUTE IMMEDIATE update_text USING step_num, script_id; IF SQL%NOTFOUND THEN mgmt$mask_sendMsg ('NOTFOUND EXCEPTION of sql_text: ' || update_text); mgmt$mask_errorExit ('ERROR accessing table: ' || job_table); return; END IF; COMMIT; EXCEPTION WHEN OTHERS THEN mgmt$mask_errorExit ('ERROR accessing table: ' || job_table); return; END mgmt$mask_setStep; / CREATE OR REPLACE FUNCTION mgmt$mask_randomencode (i_input VARCHAR2, pad_length NUMBER) RETURN VARCHAR2 AUTHID CURRENT_USER IS TYPE charmap IS TABLE OF VARCHAR2(1) INDEX BY BINARY_INTEGER; l_input NUMBER; l_mod NUMBER; l_retCode VARCHAR2(100); l_map charmap; l_base number := 25; BEGIN if (i_input is null) then return lpad('a',pad_length,'a'); end if; l_map(0) := 'a'; l_map(1) := 'b'; l_map(2) := 'c'; l_map(3) := 'd'; l_map(4) := 'e'; l_map(5) := 'f'; l_map(6) := 'g'; l_map(7) := 'h'; l_map(8) := 'i'; l_map(9) := 'j'; l_map(10) := 'k'; l_map(11) := 'l'; l_map(12) := 'm'; l_map(13) := 'n'; l_map(14) := 'o'; l_map(15) := 'p'; l_map(16) := 'q'; l_map(17) := 'r'; l_map(18) := 's'; l_map(19) := 't'; l_map(20) := 'u'; l_map(21) := 'v'; l_map(22) := 'w'; l_map(23) := 'x'; l_map(24) := 'y'; l_map(25) := 'z'; l_input := i_input; l_retCode := ''; LOOP -- skip 'a' for padding l_mod := l_input mod l_base + 1; l_retCode := l_map(l_mod) || l_retCode ; IF (l_input >= l_base) THEN l_input := round(l_input / l_base); ELSE l_input := 0; END IF; -- dbms_output.put_line('left ' || l_input || ' mod ' || l_mod ); EXIT WHEN l_input = 0; END LOOP; return lpad(l_retCode, pad_length, 'a'); END ; / create or replace type mgmt$mask_array_list as table of varchar2(128) / CREATE OR REPLACE PROCEDURE mgmt$step_1_14(script_id IN INTEGER, job_table IN VARCHAR2, step_num IN OUT INTEGER) AUTHID CURRENT_USER IS sqlerr_msg VARCHAR2(100); BEGIN IF step_num <> 1 THEN return; END IF; mgmt$mask_setStep (14, 'MGMT$MASK_CHECKPOINT', step_num); step_num := step_num + 1; BEGIN mgmt$mask_sendMsg ('declare adj number:=0; begin select length(count(distinct NAME)) into adj from "TT"."MASK"; adj := greatest(adj - 1, 0); execute immediate ''create table MGMT_DM_TT_15 (orig_val, new_val) NOLOGGING as select s.orig_val, CAST(lpad(lower(dbms_random.string(''''l'''', trunc(dbms_random.value(1, 7 - least(7, greatest(''||adj||'' - 0, 0)))))), 7 - least(7, greatest(''||adj||'' - 0, 0)),''''a'''') || mgmt$mask_randomencode(nvl(substr(s.new_num, 1, 1 + least(7, greatest(''||adj||'' - 0, 0))), ''''0''''),1 + least(7, greatest(''||adj||'' - 0, 0))) AS VARCHAR2(10)) new_val from (select rownum rn , orig_val , lpad(rownum - 1, ''||adj||'' + 1, 0) new_num from (select distinct NAME orig_val from "TT"."MASK" order by dbms_random.value)) s where 1=1 ''; DBMS_STATS.GATHER_TABLE_STATS(NULL, ''"MGMT_DM_TT_15"'', estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE, degree=>DBMS_STATS.DEFAULT_DEGREE); end; '); EXECUTE IMMEDIATE 'declare adj number:=0; begin select length(count(distinct NAME)) into adj from "TT"."MASK"; adj := greatest(adj - 1, 0); execute immediate ''create table MGMT_DM_TT_15 (orig_val, new_val) NOLOGGING as select s.orig_val, CAST(lpad(lower(dbms_random.string(''''l'''', trunc(dbms_random.value(1, 7 - least(7, greatest(''||adj||'' - 0, 0)))))), 7 - least(7, greatest(''||adj||'' - 0, 0)),''''a'''') || mgmt$mask_randomencode(nvl(substr(s.new_num, 1, 1 + least(7, greatest(''||adj||'' - 0, 0))), ''''0''''),1 + least(7, greatest(''||adj||'' - 0, 0))) AS VARCHAR2(10)) new_val from (select rownum rn , orig_val , lpad(rownum - 1, ''||adj||'' + 1, 0) new_num from (select distinct NAME orig_val from "TT"."MASK" order by dbms_random.value)) s where 1=1 ''; DBMS_STATS.GATHER_TABLE_STATS(NULL, ''"MGMT_DM_TT_15"'', estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE, degree=>DBMS_STATS.DEFAULT_DEGREE); end; '; EXCEPTION WHEN OTHERS THEN sqlerr_msg := SUBSTR(SQLERRM, 1, 100); mgmt$mask_errorExitOraError('ERROR executing steps ', sqlerr_msg); step_num := -1; return; END; END mgmt$step_1_14; / CREATE OR REPLACE PROCEDURE mgmt$step_2_14(script_id IN INTEGER, job_table IN VARCHAR2, step_num IN OUT INTEGER) AUTHID CURRENT_USER IS sqlerr_msg VARCHAR2(100); BEGIN IF step_num <> 2 THEN return; END IF; mgmt$mask_setStep (14, 'MGMT$MASK_CHECKPOINT', step_num); step_num := step_num + 1; BEGIN mgmt$mask_sendMsg ('ALTER TABLE "TT"."MASK" RENAME TO "MASK$DMASK"'); EXECUTE IMMEDIATE 'ALTER TABLE "TT"."MASK" RENAME TO "MASK$DMASK"'; EXCEPTION WHEN OTHERS THEN sqlerr_msg := SUBSTR(SQLERRM, 1, 100); mgmt$mask_errorExitOraError('ERROR executing steps ', sqlerr_msg); step_num := -1; return; END; END mgmt$step_2_14; / CREATE OR REPLACE PROCEDURE mgmt$step_3_14(script_id IN INTEGER, job_table IN VARCHAR2, step_num IN OUT INTEGER) AUTHID CURRENT_USER IS sqlerr_msg VARCHAR2(100); BEGIN IF step_num <> 3 THEN return; END IF; mgmt$mask_setStep (14, 'MGMT$MASK_CHECKPOINT', step_num); step_num := step_num + 1; BEGIN mgmt$mask_sendMsg ('CREATE TABLE "TT"."MASK" TABLESPACE "USERS" PCTFREE 10 INITRANS 1 MAXTRANS 255 STORAGE ( INITIAL 64K BUFFER_POOL DEFAULT) NOLOGGING AS SELECT s."ID", c0m15.NEW_VAL "NAME" FROM "TT"."MASK$DMASK" s , MGMT_DM_TT_15 c0m15 WHERE s."NAME" = c0m15.ORIG_VAL(+) '); EXECUTE IMMEDIATE 'CREATE TABLE "TT"."MASK" TABLESPACE "USERS" PCTFREE 10 INITRANS 1 MAXTRANS 255 STORAGE ( INITIAL 64K BUFFER_POOL DEFAULT) NOLOGGING AS SELECT s."ID", c0m15.NEW_VAL "NAME" FROM "TT"."MASK$DMASK" s , MGMT_DM_TT_15 c0m15 WHERE s."NAME" = c0m15.ORIG_VAL(+) '; EXCEPTION WHEN OTHERS THEN sqlerr_msg := SUBSTR(SQLERRM, 1, 100); mgmt$mask_errorExitOraError('ERROR executing steps ', sqlerr_msg); step_num := -1; return; END; END mgmt$step_3_14; / CREATE OR REPLACE PROCEDURE mgmt$step_4_14(script_id IN INTEGER, job_table IN VARCHAR2, step_num IN OUT INTEGER) AUTHID CURRENT_USER IS sqlerr_msg VARCHAR2(100); BEGIN IF step_num <> 4 THEN return; END IF; mgmt$mask_setStep (14, 'MGMT$MASK_CHECKPOINT', step_num); step_num := step_num + 1; BEGIN mgmt$mask_sendMsg ('ALTER TABLE "TT"."MASK" LOGGING '); EXECUTE IMMEDIATE 'ALTER TABLE "TT"."MASK" LOGGING '; EXCEPTION WHEN OTHERS THEN sqlerr_msg := SUBSTR(SQLERRM, 1, 100); mgmt$mask_errorExitOraError('ERROR executing steps ', sqlerr_msg); step_num := -1; return; END; END mgmt$step_4_14; / CREATE OR REPLACE PROCEDURE mgmt$step_5_14(script_id IN INTEGER, job_table IN VARCHAR2, step_num IN OUT INTEGER) AUTHID CURRENT_USER IS sqlerr_msg VARCHAR2(100); BEGIN IF step_num <> 5 THEN return; END IF; mgmt$mask_setStep (14, 'MGMT$MASK_CHECKPOINT', step_num); step_num := step_num + 1; BEGIN mgmt$mask_sendMsg ('DROP TABLE "TT"."MASK$DMASK" PURGE'); EXECUTE IMMEDIATE 'DROP TABLE "TT"."MASK$DMASK" PURGE'; EXCEPTION WHEN OTHERS THEN sqlerr_msg := SUBSTR(SQLERRM, 1, 100); mgmt$mask_errorExitOraError('ERROR executing steps ', sqlerr_msg); step_num := -1; return; END; END mgmt$step_5_14; / CREATE OR REPLACE PROCEDURE mgmt$step_6_14(script_id IN INTEGER, job_table IN VARCHAR2, step_num IN OUT INTEGER) AUTHID CURRENT_USER IS sqlerr_msg VARCHAR2(100); BEGIN IF step_num <> 6 THEN return; END IF; mgmt$mask_setStep (14, 'MGMT$MASK_CHECKPOINT', step_num); step_num := step_num + 1; BEGIN mgmt$mask_sendMsg ('BEGIN DBMS_STATS.GATHER_TABLE_STATS(''"TT"'', ''"MASK"'', estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE, cascade=>TRUE); END;'); EXECUTE IMMEDIATE 'BEGIN DBMS_STATS.GATHER_TABLE_STATS(''"TT"'', ''"MASK"'', estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE, cascade=>TRUE); END;'; EXCEPTION WHEN OTHERS THEN sqlerr_msg := SUBSTR(SQLERRM, 1, 100); mgmt$mask_errorExitOraError('ERROR executing steps ', sqlerr_msg); step_num := -1; return; END; END mgmt$step_6_14; / CREATE OR REPLACE PROCEDURE mgmt$mask_cleanup_14 (script_id IN INTEGER, job_table IN VARCHAR2, step_num IN INTEGER, highest_step IN INTEGER) AUTHID CURRENT_USER IS BEGIN IF step_num <= highest_step THEN return; END IF; mgmt$mask_sendMsg ('Starting cleanup of recovery tables'); mgmt$mask_deleteJobTableEntry(script_id, job_table, step_num, highest_step); mgmt$mask_sendMsg ('Completed cleanup of recovery tables'); END mgmt$mask_cleanup_14; / CREATE OR REPLACE PROCEDURE mgmt$mask_commentheader_14 IS BEGIN mgmt$mask_sendMsg ('-- Target database: emrep'); mgmt$mask_sendMsg ('-- Script generated at: 24-MAR-2012 23:52'); END mgmt$mask_commentheader_14; / -- Script Execution Controller -- ============================================== variable step_num number; exec mgmt$mask_commentheader_14; exec mgmt$mask_sendMsg ('Starting Data Masking'); exec mgmt$mask_sendMsg ('Executing as user: ' || 'system'); exec mgmt$mask_checkDBAPrivs ('system'); exec mgmt$mask_setupJobTable (14, 'MGMT$MASK_CHECKPOINT', :step_num); exec mgmt$step_1_14(14, 'MGMT$MASK_CHECKPOINT', :step_num); exec mgmt$step_2_14(14, 'MGMT$MASK_CHECKPOINT', :step_num); exec mgmt$step_3_14(14, 'MGMT$MASK_CHECKPOINT', :step_num); exec mgmt$step_4_14(14, 'MGMT$MASK_CHECKPOINT', :step_num); exec mgmt$step_5_14(14, 'MGMT$MASK_CHECKPOINT', :step_num); exec mgmt$step_6_14(14, 'MGMT$MASK_CHECKPOINT', :step_num); exec mgmt$mask_sendMsg ('Completed Data Masking. Starting cleanup phase.'); exec mgmt$mask_cleanup_14 (14, 'MGMT$MASK_CHECKPOINT', :step_num, 6); exec mgmt$mask_sendMsg ('Starting cleanup of generated procedures'); DROP PROCEDURE mgmt$step_1_14; DROP PROCEDURE mgmt$step_2_14; DROP PROCEDURE mgmt$step_3_14; DROP PROCEDURE mgmt$step_4_14; DROP PROCEDURE mgmt$step_5_14; DROP PROCEDURE mgmt$step_6_14; DROP PROCEDURE mgmt$mask_cleanup_14; DROP PROCEDURE mgmt$mask_commentheader_14; DROP TABLE MGMT_DM_TT_15; exec mgmt$mask_sendMsg ('Completed cleanup of generated procedures'); exec mgmt$mask_sendMsg ('Script execution complete'); spool off set pagesize 24 set serveroutput off set feedback on set echo on |
3. 生成排程计划
排程有两种运行方式:立即执行和指定时间执行。
3. 数据格式转化
查看数据库中TT用户的mask表的原数据。
执行Job之后:
查看执行之后mask表的数据:
前后结果进行对比之后发现,mask表里面的name列数据发生变化,Data Masking就是通过生成脚本的方式将原有敏感数据,进行处理保证机密资料不被外泄,同时也满足开发者能获得和生产库相近的资料。
转载于:https://www.cnblogs.com/buffercache/p/10836553.html
Data Masking安装和使用指南相关推荐
- APC UPS网络管理卡 (AP9606/9617/9618/9619)的快速安装及配置指南
APC UPS网络管理卡 (AP9606/9617/9618/9619)的快速安装及配置指南 本文说明如何配置APC UPS网络管理卡所需的基本网络参数 在正确使用网络管理卡之前,必须对其基本参数进行 ...
- keras安装与配置指南_Keras-快速指南
keras安装与配置指南 Keras-快速指南 (Keras - Quick Guide) Keras-简介 (Keras - Introduction ) Deep learning is one ...
- Oracle Grid Control 11g for linux安装和配置指南
2019独角兽企业重金招聘Python工程师标准>>> Oracle Grid Control 11g for linux安装和配置指南 原创 candon123 2010-07-1 ...
- 学习笔记:Windows 下Keras安装和配置指南
目录: 目录: Windows下Keras安装和配置指南 Keras 框架搭建 安装 本系列参考官方文档官方文档 这就是keras可以参考前篇:这就是keras 学习笔记 Keras:一些基本概念 一 ...
- Anaconda安装和使用指南教程:环境管理、包管理、离线安装第三方包
本文介绍anaconda的安装,和使用anaconda对Python进行环境管理. Anaconda安装和使用指南教程 1 概述 2 安装 3 环境管理 3.1 检查conda 3.2 创建环境 3. ...
- Azure SQL Database (23) Azure SQL Database Dynamic Data Masking动态数据掩码
<Windows Azure Platform 系列文章目录> 我们在使用关系型数据的时候,有时候希望: - 管理员admin,可以查看到所有的数据 - 普通用户,某些敏感字段,比如信用卡 ...
- Mentor PADS 9.5下载安装及破解指南
Pads,是一款用于设计.模拟电子线路及设计电路板的电脑软件,原由Innoveda公司开发,其后改名为PowerPCB,在2002年4月Innoveda被Mentor Graphics收购,近年再次改 ...
- P6 EPPM 16.1 安装和配置指南 1
安装和配置指南 下一topiccontents 这些指南解释如何安装和配置数据库服务器,和P6 EPPM,模块:他们还提供在P6 EPPM能够解决所有模块的概述. 标准指南帮助您配置和部署应用程序向导 ...
- 请检查virtualboxapi是否正确安装_MBR膜组件安装施工方案指南
在水处理系统建设时,往往要考虑到MBR膜组件安装施工方面的问题,而且很多公司对于MBR膜组件安装不够了解,在MBR膜分离池施工时没注意到后续安装问题,导致后面安装选型受限. MBR膜安装施工前的注意事 ...
最新文章
- mysql脚本下载_Mysql备份脚本
- NOIP2018 赛前集训总结反思
- C语言 内存分配 地址 指针 数组 参数 解析
- 【51Nod - 1001 】 数组中和等于K的数对 (排序+ 尺取)
- python爬pdf的曲线_科学网—Python爬PDF - 胡鹏程的博文
- 【maven】idea左侧External Libraries里,没有Maven的依赖包 代码飘红
- CentOS部署SpringBoot项目(二)
- document.all用法
- python网站开发实例 flask_Python Web开发之——构建基于Flask框架的web后端项目
- linux 下的下载管理工具
- Leetcode之机器人大冒险
- freeswitch呼叫中心之百度MRCP语音合成识别环境搭建
- 科目二倒车入库学车技巧_学车必看_保过。
- 网络是怎样连接的?从浏览器输入URL开始
- 2020-11-20
- vue中注释里@xxx是什么意思呢???
- 三招教你降低电脑屏幕蓝光对眼睛的危害
- Unity5.1 新的网络引擎UNET(十五) Networking 引用--中
- BootStrap初学者怎么使用?
- 图森计划裁员25%/ 特斯拉被曝将冻结招聘/ 天才黑客Geohot从推特辞职…今日更多新鲜事在此...
热门文章
- 简明扼要阐述synchronized和lock的区别
- 抖音一般多久能上热门 视频修改MD5工具
- Mongodb分组查询
- logo免费在线设计,给自己的logo寻找灵感
- 在Java中构建响应式微服务系统——第三章 构建响应式微服务
- LeetCode 544. 输出比赛匹配对(NBA季后赛对阵图)
- 【Android开发】wifi开关与wifi连接(密码连接)
- 解决chrome浏览器一打开就弹出hao123问题
- 01-基于C++的简易技能系统实现
- 机械祭天法力无边:练习3.5:编写一段程序从标准输入中读入多个字符串并将它们连接在一起,输出连接成的大字符串。然后修改上述程序,用空格把输入的多个字符串分隔开来。