震惊!只有两岁的宝宝,造就了神级编程大师?!!

导读:网络安全知识:网络安全的预防与应对

图片来源 / 网络

现在两三岁的宝宝,经常接触电脑、手机进行上网浏览各种类型的内容。上图已经很直观表现了青少年上网的各种威胁。以此警醒广大家长,加强针对儿童的上网的监督管理,控制孩子上网时间。

小编也是一位三岁小公主的dady, 小公主每天吵着要看小猪佩奇、光头强,抱着好dady的态度,照顾到宝宝的每一个需求,于是就细心研究一番儿童们使用网络的情况。

      查阅相关资料如下

保护我们明亮的眼睛

儿童上网,家长们总是担心宝宝上网时间,浏览内容,损害眼睛的问题。如何正确使用,爸爸、妈妈、小朋友们,要仔细听好,认真执行哦!

  1. 切勿长时间连续上网,尤其不要熬夜上网;
  2. 操作过程中常闭上眼睛休息片刻,或经常眨眼,增加泪液分泌,以缓解眼睛疲劳;
  3. 每隔一小时,要将视线彻底离开电脑休息10分钟;
  4. 室内光照要适中,不可过亮或过暗,一般来说,室内光线应为屏幕明亮度的3倍;
  5. 避免灯光、日光等光源直接照射屏幕,以免产生干扰光线;
  6. 注意调节电脑屏幕的亮度和对比度,屏幕不要太亮,对比度不要太强;
  7. 屏幕刷新频率不小于60赫兹;
  8. 电脑屏幕的中心位置应与操作者胸部在同一水平线上,使屏幕中心在眼睛下方7-10°之间;
  9. 双眼直视屏幕,不要斜视或扭转,眼睛距离35厘米大的屏幕应不少于60厘米,距离38厘米大的屏幕应不少于70厘米;
  10. 要经常擦拭屏幕,避免屏幕过脏使图像模糊,造成眼睛疲劳。

要确保我们的肌肉和骨骼健壮

  1. 正面面对电脑,不要歪坐或扭转;
  2. 坐姿正确,背部要有完全的支撑,膝盖约弯曲90度,应有足够的空间伸放双脚,不要交叉双脚,以免影响血液循环;
  3. 身体不要与桌子靠得太近,肘部保持自然弯曲;
  4. 敲击键盘不要过分用力,肌肉尽量放松;
  5. 不要连续操作,每隔一小时,起来活动全身;
  6. 上网过程中也要时不时伸伸懒腰,舒展筋骨或仰靠在椅子上,双手用力向后伸展;
  7. 还可做抖手指运动,这是完全放松手指最简单的方法。

保护呼吸系统,防止病从“鼻”入

  1. 上网时要注意屋内空气流通,不要在密闭环境中使用电脑;
  2. 有空调的房间应定期进行室内空气消毒;
  3. 在冬季等气候寒冷不利于开窗通风的季节,更要注意更换室内空气。
保持心理健康,快乐上网
  1. 生活要规律,不要因为上网耽误吃饭、睡觉;
  2. 不要在睡前玩过于刺激、紧张的游戏,以免影响入睡速度和睡眠质量;
  3. 要牢记网络世界与现实世界的区别,不要把网络世界当成逃避现实困难的避风港;
  4. 注意培养多种兴趣爱好,丰富业余生活;
  5. 积极参加社交活动,培养良好的人际关系;
  6. 经常进行户外活动,确保脑力消耗与体力消耗的平衡;
  7. 生活中遇到不开心的事或者有不愉快的经历,要及时疏导,不要在网上发泄不良情绪。
网络安全知识:网络安全的预防与应对

其实要让儿童健康上网,也没那么复杂,小编专门针对家里小公主开发了360度上网控制软件《绿盾安全桌面》限制孩子上网时间,守护小朋友健康成长。儿童桌面分享给各位宝爸、宝妈们~~!

 (绿盾安全桌面,守护宝宝健康成长)

绿盾安全桌面-一款保护儿童健康上网的桌面软件

绿盾安全桌面简介

绿盾安全桌面是一款实用的儿童上网教育辅助软件,该软件的主要功能是帮助家长对儿童上网进行良好的疏导,通过自动屏蔽部分网站来保证青少年上网浏览的网站的安全,并逐步养成良好的安全上网习惯。是家长帮助孩子正确利用网络资源的好帮手。现在的网络行业越来越普及大部分青少年都接触到了电脑这款绿盾安全桌面使用后能够开启独有的儿童上网防护功能,能够自动拦截浏览器中不适合儿童浏览的网页内容,使青少年健康上网,文明上网,有效的控制孩子上网时间。

绿盾安全桌面

  绿盾安全桌面专注儿童成长教育,家长可以设置定时锁屏,控制儿童使用电脑上网时间,保护视力。创建健康上网环境。可自助添加需要的内容。

特性介绍

绿盾安全桌面

  1.  浮动顶栏,方便快捷切换到儿童桌面模式
  2. 自助为小朋友添加感兴趣的内容
  3. 启动自动锁定,起到电脑的防护
  4. 类似电视单屏幕独占模式,专为儿童设计,简化操作
  5. 定时锁屏,控制儿童使用时间,保护视力

绿盾安全桌面下载

绿盾安全桌面下载概要

  • 资源大小:2.3 MB
  • 月下载量:40,765次
  • 软件属性:简体中文 免费软件
  • 更新时间:2019-03-15
  • 系统平台:Win10/Win7/WinVista/WinXP/Win2000/Win8兼容软件
  • 官方网站:ByWei.Cn
  • 软件说明:锁屏默认密码   123456

绿盾安全桌面下载地址

  1. 推荐百度网盘下载:  链接: https://pan.baidu.com/s/1AowRio3u26Iul4onKVF85Q 提取码:  g7r3
  2. 百味博客官方下载

 

背景小插曲

我家里有一位两岁多的小公主,最近特别的喜欢看小猪佩奇、光头强,只要一看电视就不吃饭、不愿意喝水。特别是在我上班去了的时候,家里的奶奶也总是会惯着她。完全控制不住。想了很多办法都不凑效,自己本职是做软件研发的,就想着肯定有相关优秀的软件能控制上网软件或者限制孩子上网时间,找了一圈下来,出名的就是《360安全桌面》还有儿童桌面模式能限制小孩上网时间,赶紧下载下来试用一番,虽然到点有提示眼保健操,能控制孩子上网时间,可能电脑端的没落、移动端的迅速崛起。360安全桌面竟然都不更新了,很多东西打开都是找不到资源。基本不可用了。另外还有另外一个《奇未安全桌面》专门做青少年的安全上网防护的,本以为找到了救星,没想到他们和360安全桌面是一家的,也已经断了维护。

绝望之下,借鉴了他们的模式,干脆自己来开发一个儿童安全桌面吧。安装在家里的电脑上,用了两三个星期,家里的小公主总算看电视有所节制了。现在回到家,听到她奶奶说:看一会儿动画片,发现电脑就睡觉了,于是她也跟着电脑睡觉了。   哈哈哈,听到这话,心里也算有点儿安慰…..

绿盾安全桌面作为上网控制软件当前只实现了最核心也是最基本的功能,后期再观察小公主的需求,继续优化升级吧,特分享出来,有相关需求的人可直接下载。

 

欢迎大家留言提出宝贵的建议!!

Nginx配置正向代理http/https案例

Nginx正向代理配置案例如下:

编辑nginx.conf文件,配置正向代理

#HTTP
server {
    #定义域名解析,可配置多个
    resolver 8.8.8.8 114.114.114.114;
    #用于指定dns解析的超时时间
    resolver_timeout 20s;

    listen 0.0.0.0:8888;

    access_log  /data/log/nginx/nginx.proxy.log;

    location / {
        #此处代理访问
        proxy_pass $scheme://$host$request_uri;
        proxy_set_header Host $http_host;

        proxy_buffers 256 4k;
        proxy_max_temp_file_size 0;

        proxy_connect_timeout 50;
        proxy_send_timeout 60;
        proxy_read_timeout 60;

        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 301 1h;
        proxy_cache_valid any 1m;
   }
}

#HTTPS
server{
         #定义域名解析,可配置多个
         resolver 8.8.8.8 114.114.114.114;
         #用于指定dns解析的超时时间
        resolver_timeout 20s;
        access_log /data/log/nginx/nginx.proxy.log main;
        listen 443;
        location / {
                root html;
                index index.html index.htm;
                proxy_pass https://$host$request_uri;
                proxy_buffers 256 4k;
                proxy_max_temp_file_size 0k;
                proxy_connect_timeout 50;
                proxy_send_timeout 60;
                proxy_read_timeout 60;
                proxy_next_upstream error timeout invalid_header http_502;
        }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
                root html;
        }
}

客户端增加代理访问

如上正向代理配置完成后,在浏览器或者程序中配置代理,其中浏览器配置代理如下:
代理配置

验证代理访问

查看Nginx访问日志 /data/log/nginx/nginx.proxy.log 验证是否正常代理,例子如下表示已经过代理服务器转发

 - -[18/Jan/2019:14:01:31 +0800] "CONNECT fonts.googleapis.com:443 HTTP/1.1" 200 598 "-" "-"
 - - [18/Jan/2019:14:01:31 +0800] "CONNECT static.cnblogs.com:443 HTTP/1.1" 200 598 "-" "-"
 - - [18/Jan/2019:14:01:31 +0800] "CONNECT pic.cnblogs.com:443 HTTP/1.1" 200 598 "-" "-"
.......

参考地址:http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver

如何利用爬虫和小程序模板消息实现早报提醒

网络爬虫

当今社会通讯发达,大量资讯信息汇集,如何精准有效的获取最有价值的信息,也成为一项令人苦恼的事情。由此引发出通过大数据算法提取出最热点的信息的应用。百味博客致力于软件研发管理、互联网科技资讯、企业管理等领域研究学习。应博主及广大网友需求,特定制了“百味博客科技早报提醒”的功能。
百味博客使用的是经典的PHP程序 wordpress. 在开发早报功能前,需先安装wordpress插件“wp-json”, 该插件提供了rest的接口形式,以便直接对接早报功能。

一、抓取新闻数据

//待补充

二、分析热点、提炼

//待补充

三、WordPress存储

//待补充

四、小程序展示

//待补充

五、对接小程序模板消息

//待补充

六、定时发送早报提醒

//待补充

最终演示效果可以扫描下面二维码进入早报查看,或者在微信小程序栏搜索“百味博客”进入即可。

微信订阅号

某聚合支付平台行业解决方案

聚合支付

第1章 关于本解决方案

本解决方案涵盖项目整个流程需求、设计、开发、问题售后、扩展以及安全性措施等。

第2章 概述

2.1 项目背景

支付行业的高速发展,面对国家对第三方平台的要求日益提高,聚合支付平台应运而生,作为第三方支付平台服务的拓展,介于第三方支付机构和商户之间。商家出于时间成本、精力上的考虑,不再需要一家家去对接支付宝、财付通、百度钱包等各个支付通道,而是直接由聚合支付服务商通过统一的开发系统实现一次性对接,同时支持市面上主流的多家支付通道、多个支付场景、多种支付方式。形成了自己的特色,软件产品服务于国内百余家支付公司。在为客户创造价值的同时,产品本身也获得了丰富与完善。公司战略是立足于软件研发,通过服务延伸软件价值,潜心深耕支付软件产品,与客户创建互利共赢是我们的愿景。

2.2 项目设计原则

项目以安全性、稳定性、健壮性为初衷进行设计。

第3章 需求描述及分析

3.1 概述

3.1.1 需求分析组成

业务需求、接口需求、安全需求;系统涉众的分析、总体功能需求、技术架构需求。

3.1.2 业务原理和逻辑

国付聚合支付系统,可以将市面上主流的支付渠道整合为一个付款渠道接口,发放给下级商户用户使用。商户(各类型站点或者线下商店以及各种付款场景使用者)只向平台申请一次就可以接入全部的主流支付 ,不用再去一个个的申请,节约站点支付开发的时间,解决收款混乱以及网站支付问题。

商户根据平台接口文档接入平台,平台进行审核/下号,平台之间产生资金流向并生成订单流水,支付担保平台提交给上游接口,等待上游接口返回订单状态;根据上游接口返回的订单状态,支付担保平台内部做具体业务逻辑处理(数据安全校验MD5值比对、非法值过滤、单号过滤等),将处理后的结果中转给下级商户平台。

3.1.3 接口需求

虚拟接口包括:业务逻辑接口、数据对象接口、模型抽象接口、全局接口等。

实体接口包括:网银支付接口、手机快捷接口、支付宝、财付通、微信等。

3.1.4 基于Java框架的安全性

Java框架提供了一个强健的安全系统,该系统能够限制代码在严格约束的、管理员定义的安全上下文中运行。

3.2 项目分析

3.3 应用领域与使用对象

主要用于线上收款,一些游戏类站点或者一些商城,如果需要各种支付方式(比如微信支付,支付宝支付,银联快捷支付)就必须分别向这些支付渠道商申请账号,不仅耗时耗力而且不便于统一维护和财务统计。通过国付API聚合支付系统对接后,只需要向平台申请一次支付,就可以同时拥有多种支付通道,而且这些支付通道不受上级通道限制。可以解决一些行业的支付问题,是一种创新的支付方式。

3.3.1 技术架构

使用Java三层体系架构:

表示层:主要使用Struts技术,逻辑层强大和完善,无论表现层如何定义和更改,各司其职,逻辑层都能完善地提供服务。

业务逻辑层:主要针对具体问题操作,也是对数据Data层的操作,对数据业务进行逻辑处理,实现积木拼接式搭建。

抽象接口层:对数据访问层抽象出接口,业务逻辑层经过抽象接口层去调用,保证调用分离,扩展分离。

数据访问层:主要对原始数据(数据库或XML、文本文件等存放数据形式)进行加工和提取,为业务逻辑层提供数据服务。

工厂模式(简单工厂、抽象工厂)。

设计模式:单例模式、适配器模式、模版方法模式、职责链模式等。

3.4 业务模式

 

第4章 总体设计

4.1 总体设计目标

高并发、大数据承载量、安全、稳定为最终实现目标。

4.2 总体设计原则

依据七大原则:开-闭原则(OCP)、里氏替换原则(LSP)、依赖倒转原则(DIP)、接口隔离原则(ISP)、迪米法特原则(LKP)、单一职责原则(SRP)、合成/复用聚合原则(CARP)。

4.2.1 服务器

系统采用分布式的部署方案,所以对服务器的要求比较高。并发能支撑到每秒500笔交易请求。服务器的搭配一般有3个方案:

方案一:

1台8核32G的服务器,带宽选10M。

方案二:

2台4核16G的服务器,带宽选5M。

方案三:

4台服务器。一台作为数据库服务器,4H8G,内网传输;两台作为应用服务器,4H16G,用于处理后台、交易和清算数据;一台作为查询服务器,4H8G,专门用来处理下游的查询请求。

4.3 平台选择

WEB服务器操作系统环境:CentOS 6.8 Linux 64位

WEB服务器数据库环境:MySQL

第5章 详细设计

5.1 技术架构设计

5.1.1 设计原则

可靠性、健壮性、可修改性、便捷性、可测试性、效率性、标准化性、先进性、可扩展性。

5.2 功能设计 

5.3 安全设计

信息内部传输采用MD5不可逆加密算法,涉及到金额、银行卡、结算等敏感信息系统会自动过滤恶意请求,保证用户资金安全。

5.3.1 界面设计原则

操作简单,布局舒适,颜色美观,效果出色。

5.3.2 易用性设计

用最简单的操作达成最复杂的需求,一站式服务的用户体验。

5.3.3 项目测试方案

5.3.3.1 测试阶段

基本单元测试,压力测试,性能测试必不可少。

第7章 项目预算

现阶段国付聚合支付平台的项目费用提供两套方案:

方案一:我司收取系统的费用,分三阶段付款。具体方案联系销售人员。

方案二:我司免费提供系统进行使用。前期收取一定的系统搭建费用;后期按照交易量比例进行收取。

 

 

欢迎留言联系

API接口防恶意调用盗链或攻击方案

Web前后分离开发,防止API接口被恶意调用或攻击。度娘分享了很多种应对场景的方法。总结得比较好的见文章《前后端分离,如何防止API接口被恶意调用或攻击》、《如何防止别人恶意调用API接口》。
网络安全
本文选用了另外一种常见的配置下载防盗链机制的方法,使用该方式前请仔细阅读《彻底搞清referrer和origin》后,理解应用场景。具体操作记录如下:

  1. 增加Nginx端配置,使其能够获取到Http头信息Origin、Referer。
    proxy_set_header Origin $http_origin;
    proxy_set_header Referer $http_referer;
    
  2. 后端Java来源验证
    public static boolean originAuth(HttpServletRequest request) {
            String referer = request.getHeader("Referer");
            String origin = request.getHeader("Origin");
            String sitePart = request.getServerName();
            if(referer == null || origin == null) {
                return false;
            }
            if (referer != null && !referer.contains(sitePart)) {
                return false;
            }
            if (origin != null && !origin.contains(sitePart)) {
                return false;
            }
            return true;
        }
    

因Http协议是无状态的,针对上面的方法也不是完全无破解方案,只是提高了攻击者的难度。在这条接口网络安全的道路上还得持续的斗智斗勇。

【刷爆微信朋友圈】短视频营销微信传播分享系统

什么是短视频裂变

视频裂变也叫视频强制裂变,是一种强制分享浏览广告吸粉的模式。首先挑选一个极有吸引力的视频,把您的产品和联系方式(微信,QQ)以H5落地页的形式插入到视频底部,然后由大量的转发人员曝光到朋友圈,观看这个视频到高潮时,会强制分享到5个群或者朋友圈才能继续观看!

短视频裂变演示

短视频裂变流程

①做一条网页链接,链接分享出去可以显示标题和图片(最好标题可以自动识别用户手机的地址)
②用户点进去之后可以点击视频可以播放
③视频播放到高潮的时,提示分享到三个微信群和朋友圈才能继续看(一般显示网速太慢,分享了可以视频加速)
④用户分享到微信群和朋友圈之后可以,返回继续播放视频
⑤视频播放完之后,立刻自动弹出广告落地页

短视频裂变注意事项

裂变的链接无论是视频播放页面链接还是广告文案页面链接都容易被封,所以一般需要做跳转。推广的链接(第一链接)自动跳到落地链接(第二链接)。只要封第一链接就行了,一般第一链接买抗封性比较好的域名(比如阿里云备案的域名)。第二链接短时间流量大了之后被封可能性非常大,所以第二链接一般多准备几个域名(最好做域名池)。

短视频营销系统架构设计

考虑并发十几万情况,服务器需要集群部署形式.

  • 服务器架构

服务器使用百度云/阿里云等在线云服务器。保障其稳定和扩展性。服务器架构如下:

  • 数据架构
    数据结构更经过精心的设计,从字段到表的分配,索引的构建,都经过缜密的考虑,能最大限度的发挥 MySQL快速数据库的效能。
    短视频架构网络图

短视频系统服务器配置

以下服务器配置预估建立在并发十几万的基础上。实际运行操作中依据情况进行拓展部署

项目 数量 说明
负载均衡服务器Nginx 1 负载集群服务和任务调度分发
H5服务端集群 3 考虑大访问量使用集群节点
管理后台应用 1 管理后台为内部人员使用,可使用单台即可
缓存服务器Redis 3 为提升H5响应效率和性能增加缓存服务器
数据存储服务MySql 1 数据核心存储持久化
工信部认证域名池 N N/A

系统演示

该项目为Java项目,可联系博主 索要 演示地址 或者 系统源码

微信:jiaivr

 

基于Nginx+Lua模块校验的Token认证

nginxlua

前言

在使用Nginx实现访问时的Token验证,将会使用到一个基于 Nginx 与 Lua 模块来实现Token的验证。在请求Nginx配置的地址时,将使用Headers传递Token参数,并在Nginx端做网关校验。

环境说明

有两种方式可选择,推荐使用OpenResty简单快捷:

1)使用nginx的lua模块配置环境

  • LuaJIT (下载地址:http://luajit.org/download.html)
  • lua-nginx-module (下载地址:https://github.com/openresty/lua-nginx-module/releases)
  • nginx (下载地址:http://nginx.org/en/download.html)

2)封装过后的高性能 Web 平台 OpenResty

OpenResty 其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

其官网做了更多详细的简介描述:

OpenResty 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

OpenResty 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。

以上信息参考OpenResty官方中文网站 http://openresty.org/cn/

软件安装

Nginx及模块安装可参阅官方的安装介绍,再此不再重述。
附赠Centos7的简单安装脚本

yum install -y yum-utils
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
yum install -y openresty

另Centos7 OpenResty Dockerfile见GitHub: https://github.com/bywei/Centos_OpenResty_Dockerfile

Nginx配置

Nginx的Token校验实现在nginx.conf中配置相应的Lua脚本,编辑Nginx配置文件即可。

vi nginx/conf/nginx.conf

完整的nginx.conf配置文件演示如下,在实际业务场景中通过扩展Lua脚本功能逻辑来实现即可。比如可通过增加Redis来实现Nginx Token权限校验Api网关,具体通过Redis实现权限校验网关,有兴趣的网友可在文章下方评论留言,将附赠Lua实现源码。

#user  nobody;
worker_processes  1;
 
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
 
#pid        logs/nginx.pid;
 
 
events {
    worker_connections  1024;
}
 
 
http {
    include       mime.types;
    default_type  application/octet-stream;
 
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
 
    access_log  logs/access.log  main;
 
    sendfile        on;
    #tcp_nopush     on;
 
    #keepalive_timeout  0;
    keepalive_timeout  65;
 
    #gzip  on;
 
    server {
        listen       80;
        server_name  localhost;
 
        #charset koi8-r;
 
        access_log  logs/access.log;
 
        location / {
            autoindex on;
                autoindex_exact_size off;
                autoindex_localtime on;
                root   /fileroot;
 
            index  index.html index.htm;
            access_by_lua '
              --获取请求的Header参数列表
              local args   = ngx.req.get_headers();
              --提取Header中的token参数,该参数在请求地址时必须加上
              local token1 = args["token"];
              --配置验证的token,此处可增加Redis做存储,然后提取
              local token2 = "f7781231c6f2ea3161sd244453580f73a";
              --token不一致则相应403无权限访问该地址
              if token1 ~= token2 then
                  ngx.exit(ngx.HTTP_FORBIDDEN);
              end
          ';
        }
 
        #通过该地址可测试Lua脚本是否安装成功
        location /lua {
            default_type 'text/html';
            content_by_lua 'ngx.say("hello world");';
        }
 
        #error_page  404              /404.html;
  }
}

 

SpringBoot使用Druid数据库加密链接完整方案

Druid介绍,Druid链接加密

网上的坑

springboot使用Druid数据库加密链接方案,不建议采用网上的一篇文章《springboot结合Druid加密数据库密码遇到的坑!》介绍的方式来进行加密链接实现。以下方式兼容SpringBoot1和SpringBoot2.0及以上。本文章下文分享Druid源码后就知道为什么不建议采用该方式的原因了。

加密准备

首先使用CMD生成数据库加密字符串,该命令会产生三个值 privateKey=公钥、publicKey=密码加密后的结果、password=密码加密串

java -cp druid-1.0.28.jar com.alibaba.druid.filter.config.ConfigTools pcds123123456

使用Durid的工具类ConfigTools验证加密字符串

@Test
public void db_decrypt_test() throws Exception {
   String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJggkaRJ+bqLMF6pefubEDLViboxYKGTdGe+78DziIta8Nv8crOA83M0tFG8y8CqHcFYIbG89q9zcnNvL+E2/CECAwEAAQ==";
   String password = "AgDRyKJ81Ku3o0HSyalDgCTtGsWcKz3fC0iM5pLur2QJnIF+fKWKFZ6c6e36M06tF2uCadvS/EodWxmRDWwvIA==";
   System.out.println(ConfigTools.decrypt(publicKey, password));
}

实现方案

SpringBoot集成Druid数据库链接加密的方式,推荐使用配置注入方式初始化DataSource。方法如下:

一、增加配置注入Bean

import java.sql.SQLException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
 
@Configuration
@ConfigurationProperties(prefix = "spring.datasource")
public class DbConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(DbConfig.class);
 
    private String url;
    private String driverClassName;
    private String username;
    private String password;
    private Integer initialSize;
    private Integer minIdle;
    private Integer maxActive;
    private Integer maxWait;
    private Integer timeBetweenEvictionRunsMillis;
    private Integer minEvictableIdleTimeMillis;
    private String validationQuery;
    private Boolean testWhileIdle;
    private Boolean testOnBorrow;
    private Boolean testOnReturn;
    private Boolean poolPreparedStatements;
    private Integer maxOpenPreparedStatements;
    private Integer maxPoolPreparedStatementPerConnectionSize;
    private String filters;
    private String publicKey;
    private String connectionProperties;
 
    @Primary
    @Bean(name = "dataSource")
    public DataSource dataSource() {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(url);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setValidationQuery(validationQuery);
        datasource.setTestWhileIdle(testWhileIdle);
        datasource.setTestOnBorrow(testOnBorrow);
        datasource.setTestOnReturn(testOnReturn);
        datasource.setPoolPreparedStatements(poolPreparedStatements);
        datasource.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        datasource.setConnectionProperties(connectionProperties.replace("${publicKey}", publicKey));
        try {
            datasource.setFilters(filters);
        } catch (SQLException e) {
            LOGGER.error("druid configuration initialization filter", e);
        }
        return datasource;
    }
 
    public String getUrl() {
        return url;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }
 
    public String getDriverClassName() {
        return driverClassName;
    }
 
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
 
    public Integer getInitialSize() {
        return initialSize;
    }
 
    public void setInitialSize(Integer initialSize) {
        this.initialSize = initialSize;
    }
 
    public Integer getMinIdle() {
        return minIdle;
    }
 
    public void setMinIdle(Integer minIdle) {
        this.minIdle = minIdle;
    }
 
    public Integer getMaxActive() {
        return maxActive;
    }
 
    public void setMaxActive(Integer maxActive) {
        this.maxActive = maxActive;
    }
 
    public Integer getMaxWait() {
        return maxWait;
    }
 
    public void setMaxWait(Integer maxWait) {
        this.maxWait = maxWait;
    }
 
    public Integer getTimeBetweenEvictionRunsMillis() {
        return timeBetweenEvictionRunsMillis;
    }
 
    public void setTimeBetweenEvictionRunsMillis(Integer timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    }
 
    public Integer getMinEvictableIdleTimeMillis() {
        return minEvictableIdleTimeMillis;
    }
 
    public void setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis) {
        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
    }
 
    public String getValidationQuery() {
        return validationQuery;
    }
 
    public void setValidationQuery(String validationQuery) {
        this.validationQuery = validationQuery;
    }
 
    public Boolean getTestWhileIdle() {
        return testWhileIdle;
    }
 
    public void setTestWhileIdle(Boolean testWhileIdle) {
        this.testWhileIdle = testWhileIdle;
    }
 
    public Boolean getTestOnBorrow() {
        return testOnBorrow;
    }
 
    public void setTestOnBorrow(Boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }
 
    public Boolean getTestOnReturn() {
        return testOnReturn;
    }
 
    public void setTestOnReturn(Boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }
 
    public Boolean getPoolPreparedStatements() {
        return poolPreparedStatements;
    }
 
    public void setPoolPreparedStatements(Boolean poolPreparedStatements) {
        this.poolPreparedStatements = poolPreparedStatements;
    }
 
    public Integer getMaxOpenPreparedStatements() {
        return maxOpenPreparedStatements;
    }
 
    public void setMaxOpenPreparedStatements(Integer maxOpenPreparedStatements) {
        this.maxOpenPreparedStatements = maxOpenPreparedStatements;
    }
 
    public Integer getMaxPoolPreparedStatementPerConnectionSize() {
        return maxPoolPreparedStatementPerConnectionSize;
    }
 
    public void setMaxPoolPreparedStatementPerConnectionSize(Integer maxPoolPreparedStatementPerConnectionSize) {
        this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
    }
 
    public String getFilters() {
        return filters;
    }
 
    public void setFilters(String filters) {
        this.filters = filters;
    }
 
    public String getPublicKey() {
        return publicKey;
    }
 
    public void setPublicKey(String publicKey) {
        this.publicKey = publicKey;
    }
 
    public String getConnectionProperties() {
        return connectionProperties;
    }
 
    public void setConnectionProperties(String connectionProperties) {
        this.connectionProperties = connectionProperties;
    }
}

二、增加配置文件

# mysql config
spring.datasource.name=pcdsdata
spring.datasource.url=jdbc:mysql://192.168.1.1/mydb
spring.datasource.username=root
spring.datasource.password=AgDRyKJ81Ku3o0HSyalDgCTtGsWcKz3fC0iM5pLur2QJnIF+fKWKFZ6c6e36M06tF2uCadvS/EodWxmRDWwvIA==
 
# druid datasource config
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.maxActive=20
spring.datasource.initialSize=1
spring.datasource.minIdle=1
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 1 from dual
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=config,stat,wall,log4j
spring.datasource.publicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJggkaRJ+bqLMF6pefubEDLViboxYKGTdGe+78DziIta8Nv8crOA83M0tFG8y8CqHcFYIbG89q9zcnNvL+E2/CECAwEAAQ==
spring.datasource.connectionProperties=config.decrypt=true;config.decrypt.key=${spring.datasource.publicKey}

注意事项

1)config.decrypt=true;config.decrypt.key=  该两项配置为Druid内部固定Key。网上很多案例配置为publicKey,这是有问题的。
2)其他教程会配置DbType为 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 实际在Druid源码中会判断DbType为 MySql 或者Db2等类型。可删除此项 原逻辑将从  this.dbType = JdbcUtils.getDbType(jdbcUrl, null);//数据库链接字符串 获取DbType值

经过以上配置,SpringBoot使用Druid加密链接完成。如果过程中遇到其他问题可在下方留言,方便更多人解决类似问题。

经测试以上配置兼容SpringBoot2.0以上2.0.3/2.06等. 升级测试时需要注意jar包版本问题:
1)Gson升级到最新版本,如gson-2.8.5.jar. 否则报错 com.google.gson.GsonBuilder.setLenient()Lcom/google/gson/GsonBuilder; but it does not exist. Its class, com.google.gson.GsonBuilder, is available from the following locations:

2)log4j升级到log4j-1.2.17以上, 否则报错 Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Priority


源码分析

1) 根据源码逻辑,如果不自定义注入Bean, 在SpringBoot初始化时只会读取系统配置参数,如下

public DruidDataSource(){
        this(false);
    }
 
    public DruidDataSource(boolean fairLock){
        super(fairLock);
 
        configFromPropety(System.getProperties());
    }
 
    public void configFromPropety(Properties properties) {
        {
            Boolean value = getBoolean(properties, "druid.testWhileIdle");
            if (value != null) {
                this.setTestWhileIdle(value);
            }
        }
        {
            Boolean value = getBoolean(properties, "druid.testOnBorrow");
            if (value != null) {
                this.setTestOnBorrow(value);
            }
        }
 
 
...

2) 所有的配置在类DruidAbstractDataSource中,自定义注入Bean,将在这个类中设置相关属性

3) 在首次初始化数据库链接时将调用DruidDataSource.init(),并进入到ConfigFilter.init ,初始化建立链接。根据配置config.decrypt决定是否要进行解密动作,如需解密则加载config.decrypt.key和password(首先加载connectionProperties链接字符串中的password,没有再加载默认的spring.datasource.password)  ConfigFilter.decrypt执行解密动作。

4) 文章《springboot结合Druid加密数据库密码遇到的坑!》中其实是绕过了是否要进行加密等配置,自己实现了解密动作再把数据库链接密码替换掉,这违背了Druid设计的初衷了。​

参考资料

参考资料 https://github.com/alibaba/druid/wiki