如何有效预防脱库

2023-10-31

ca5cba5dee66e1fd81aeb9b999872d5f.png

本篇不从DBA、网络架构层面来讲述数据安全,这部分有很专业的架构和云上产品来解决,本篇重点从开发人员角度讲述如何避免数据安全的漏洞。

相信大部分人都看到过这样的新闻,某某论坛泄漏了用户密码,某某物流公司泄漏了用户的手机号等等,我一直坚信大部分数据泄漏都是内部管理出现了问题,大部分都是内部团队有意或无意泄漏了数据,如果要从外界通过漏洞攻克不是没有可能但是成本是巨大的,所以人为的泄漏往往是需要更加关注的问题,那作为软件生产的主力军(程序员)如何来避免挖坑?那我们接下来就主要讲讲从开发角度如何避免数据泄漏的可能性。

从网上找了一些 数据安全治理 的相关资料,有兴趣可以参考一下。

我们从程序员角度来讲讲如何有效的预防数据安全问题。

数据的访问控制

我们先来看看哪些是经常访问数据库的用户?

  1. 软件程序(应用程序、数据库中间件)

  2. 人员:运维、开发、测试、产品、等

那接下来我们就来看看从这几点如何来控制数据库的访问。

软件程序层面

这里说的软件程序包含:应用程序、数据库中间件等,作为数据库的第一用户我们如何有效的规避数据安全的问题呢?

我们先来说说现在的软件开发用到的一些框架,无论是java、go、python或其他,已经有很丰富的ormdatasource框架或工具,我下面罗列一些java中常用jdbc连接池和orm框架以及数据库中间件

名称 说明 是否有加解密策略
druid 阿里巴巴开源的数据库连接池
dbcp Apache的开源数据库连接池
c3p0 一个开源的JDBC连接池
atomikos 一款分布式事务框架
mybatis 一款开源orm框架
hibernate 一款开源orm框架
mycat 开源分布式数据库中间件
shardingsphere Apache的开源分布式数据库中间件
cobar 阿里巴巴开源分片数据库和表的代理

我相信很多程序的数据库连接与密码都是通过配置文件来保存的,假如应用服务器被黑客利用软件漏洞拿下,我相信通过部署的软件可以翻出数据库连接的配置,那么针对这一点我们如何有效的避免呢?

数据库连接密码加密

一般我们为了做数据库高可用都会给数据库集群中间做一层代理,通过域名的方式来暴漏连接,这样做的好处就是数据库failover的时候应用程序可以不需要重启,只用重新创建连接即可。因此这层代理可以有效的防止数据库真实部署的机器被暴漏出去,起到了一定的安全作用。

虽然连接层面有一层代理来杜绝真实服务器被暴漏,但是我们在通过jdbc连接的时候往往是有密码访问的,我相信很多数据库的密码是明文的存储在配置文件中,虽然现在都用配置中心(configcenter)来统一管理应用的配置,如果使用明文来保存密码始终是无法规避泄漏的风险,因为应用程序始终要进行连接,在连接的时候要读取配置,不管配置是从云端同步下来还是从本地读取,只要是明文存储密码的就会存在安全问题。

其实大多的数据库连接池都有对数据库访问密码加解密的功能,因此我们可以通过把数据库访问的密码进行加密来解决安全问题。

下来我使用druid举个例子,具体的看看如何使用,也可以查看druid官方 示例

通过使用ConfigFilter为数据库密码提供加密功能

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
    .................
    <!-- 如果fitlers走的配置中心,请去配置中心修改 -->
    <property name="filters" value="${filters}" />
    <!-- 如果没有配置中心请直接修改 -->
    <property name="filters" value="stat,config" /> 
    <!-- 以上两种filters配置2选一 -->

    <property name="connectionProperties" value="config.decrypt=true;config.decrypt.key=${publickey}" />
    .................
</bean>
filters=stat
改为
filters=stat,config

jdbc.xxxx.password=123456
改为
jdbc.xxxx.password=加密后的值

增加
publickey=公钥

ps. 如果使用的是配置中心那么创建对应的配置项即可。

非对称秘钥对的生成方式有很多种,这里我给个在线生成的链接 加密后的密文是一段比较长的字符,例如下面这段示例

jdbc.password=p9i+fChqlaYnfhI+NoJqmrGwTyWwlFZ1W7Vi7i2MGZ8agFkGxGr/kWU//yDvPyXZ6YwJwnMKQ4zXpTZnfxWaRjfqWIRG+JzxSdSYEMp/bRCiIvzF6y8FdVCqN/0m0eQeZFvMCdIf4wqhKF0QRCEOTysZ3oGg7t5o35CIMpV1A5Y=

其他的jdbc连接池也都有类似的功能,但是不排除有一些没有这个功能的就需要我们自己动手开发来增强这部分功能。

首先我们需要了解数据库连接加解密的思路,只要有思路实现都是很简单的,其实数据库连接加解密思路很简单,在真正创建数据库连接的时候读取加密的密码进行解密后再进行数据库连接,那接下来我们给dbcp扩展这个功能。

import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.logging.Logger;

import org.apache.commons.dbcp.BasicDataSource;
import org.slf4j.LoggerFactory;

public class SecurityBasicDataSource extends BasicDataSource {

    private final org.slf4j.Logger logger = LoggerFactory.getLogger(SecurityBasicDataSource.class);

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setPassword(String password) {
        try {
            //这里可以从任意地方读取数据库配置
            Properties p = ConfigLoaderUtils.loadConfig("jdbc.properties");
            String publickey = p.getProperty("publickey");
            //ConfigTools是实现私钥、公钥对加解密实现
            password = ConfigTools.decrypt(publickey, password);
            super.setPassword(password);
        } catch(Exception e) {
            logger.error("解密password出错", e);
        }
    }

}
  • 首先我们继承dbcp数据源org.apache.commons.dbcp.BasicDataSource

  • 重写setPassword

  • 设置密码的时候通过公钥和密文进行解密

这样我们就给dbcp扩展了数据库连接加解密的功能,是不是很简单。

到这里我们就对数据库连接密码加密的方法介绍完毕,这样做的好处有什么呢?假设当应用服务器被坏人俘虏后,他想通过应用的配置信息轻松的获取数据库访问密码是不太可能,采用 公开密钥加密 安全性还是很高的,它是一种非对称加密算法想要了解更多的可以点开维基百科的连接查看。

敏感数据加解密

前面我们介绍完了数据库连接上的安全问题以及如何解决,接下来我们继续介绍数据库中存储的敏感数据应该如何处理。

我相信很多人都接触过导出生产数据需要经过 数据脱敏 ,需要经过数据脱敏的大多都是存储的明文数据,比如说用户的手机号、详细地址、银行卡号、信用卡验证码、用户密码、等。

如果我们将这些敏感数据在存储入库的时候进行加密,数据库中存储的是密文数据,这样及时被脱库我相信也没有那么容易破解,有人可能说密码破解外界有 彩虹表 ,彩虹表是一个用于加密散列函数逆运算的预先计算好的表,常用于破解加密过的密码散列,针对于用户详细地址、银行卡号我相信彩虹表是无能为力的,如果使用暴力破解时间上的成本也是难以想象的,可能需要xxxxxx亿年,哈哈哈。

我们对数据加解密使用对称加解密算法AESDES,为什么不使用非对称的公开密钥加密 ?

虽然非对称加解密算法安全性高,但是非对称加解密算法加密后的值太长不利于存储,所以我们需要使用固定长度或者可控长度的加解密算法,刚好对称加解密算法符合要求,这里使用DES作为示例,当然可以替换成任意的加解密算法。

先说说数据落库时的加解密实现思路,假设我们需要存储用户详情,其中有姓名、电话、联系地址、银行卡号等信息,我们在持久化用户详情的时候对敏感字段进行加密计算出密文,再将密文存入数据库,当查询用户详情的时候,先从数据库查询出密文,通过对密文的解密和脱敏再返回给前台,这样我们就可以达到我们想要的效果,这里需要特殊说明一下,密文对模糊查询不是很友好,但是也可以实现模糊查询,具体的实现思路有很多种,这里我们就不多做介绍,感兴趣的话我后面可以单独出一篇支持模糊查询的加解密算法,回到主题我们就以这个案例进行实现。

首先我们需要对敏感字段进行打标记,javaannotation可以帮助我们实现打标

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Cipher {
}

我们可以给用户对象的敏感字段:手机号、银行卡、详细地址 上标注@Cipher注释,说明这几个字段我们在保存、修改的时候需要加密,在查询的时候需要解密。

下来我们就要找一个公共的地方来统一的进行加解密处理,作为一名合格的程序员需要想尽一切办法来偷懒,并不是在所有CRUD的地方进行加解密调用这样会很傻很天真,作为被广泛使用的orm框架之一的mybatis这里我使用它作为示例讲解实现思路。

mybatis提供拦截器机制,可以对执行的CRUD进行拦截处理操作,pagehelper 是一个分页的mybatis插件,就是利用拦截的机制来扩展分页功能。

我们刚才有说过我们需要对insertupdate操作进行加密,对select操作进行解密,在mybatis的底层保存和修改都是update方法,查询都是query方法,刚好我们就对这两个方法进行拦截处理。

import java.lang.reflect.Field;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
        @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class }) })
public class CipherHelper implements Interceptor {

    private final Logger logger = LoggerFactory.getLogger(CipherHelper.class);

    /**
     * 加密密钥</br> 为null,使用默认密钥进行加解密</br>
     */
    private String secureKey = null;
    /**
     * 是否允许宽容处理</br> 宽容处理的话,使用原值,反之throw {@link CipherException}</br>
     */
    private boolean lenient = false;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        if (methodName.equals("update") || methodName.equals("query")) {
             Object parameter = invocation.getArgs()[1];
            if (parameter instanceof List) {
                List<?> list = (List<?>) parameter;
                for (Object obj : list) {
                    encrypt(obj);
                }
            } else if(parameter instanceof StrictMap) {
                StrictMap<?> strictMap = (StrictMap<?>) parameter;
                if (strictMap.containsKey("list")) {
                    List<?> list = (List<?>) strictMap.get("list");
                    for (Object obj : list) {
                        encrypt(obj);
                    }
                } else if (strictMap.containsKey("array")) {
                    Object[] objects = (Object[]) strictMap.get("array");
                    for (Object obj : objects) {
                        encrypt(obj);
                    }
                }
            } else {
                encrypt(parameter);
            }
        }
        Object returnValue = invocation.proceed();
        if (methodName.equals("query")) {
            if (returnValue instanceof List) {
                List<?> list = (List<?>) returnValue;
                for (Object obj : list) {
                    decrypt(obj);
                }
            } else {
                decrypt(returnValue);
            }
        }
        return returnValue;
    }

    /**
     * 加密处理
     * 
     * @param parameter
     * @throws IllegalAccessException
     */
    private void encrypt(Object parameter) throws IllegalAccessException {
        if (parameter == null) return;
        Class<?> clazz = parameter.getClass();
        if (!clazz.getSimpleName().endsWith("Entity")) {
            return;
        }
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                if (!fields[i].isAnnotationPresent(Cipher.class)) {
                    continue;
                }
                if (!fields[i].getType().equals(String.class)) {
                    logger.debug("加密字段只支持String类型,当前类型非String,跳过!");
                    continue;
                }
                fields[i].setAccessible(true);
                String v = (String) fields[i].get(parameter);
                if (StringUtils.isBlank(v)) {
                    logger.debug("加密字段值为null,跳过!");
                    continue;
                }
                try {
                    String crypt = DESTools.encrypt(secureKey, v);
                    fields[i].set(parameter, crypt);
                    logger.debug("加密处理字段,{}", fields[i].getName());
                } catch (Exception e) {
                    if (lenient) {
                        logger.warn("加密处理失败,宽容处理使用原值");
                    } else {
                        throw new CipherException("加密处理失败,不允许宽容处理["+v+"]", e);
                    }
                }
            }
        }
    }

    /**
     * 解密处理
     * 
     * @param obj
     * @throws IllegalAccessException
     * @throws Exception
     */
    private void decrypt(Object obj) throws IllegalAccessException, Exception {
        if (obj == null) return;
        Class<?> clazz = obj.getClass();
        if (!clazz.getSimpleName().endsWith("Entity")) {
            return;
        }
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                if (!fields[i].isAnnotationPresent(Cipher.class)) {
                    continue;
                }
                if (!fields[i].getType().equals(String.class)) {
                    logger.debug("解密字段只支持String类型,当前类型非String,跳过!");
                    continue;
                }
                fields[i].setAccessible(true);
                String v = (String) fields[i].get(obj);
                if (StringUtils.isBlank(v)) {
                    logger.debug("解密字段值为null,跳过!");
                    continue;
                }
                try {
                    String crypt = DESTools.decrypt(secureKey, v);
                    fields[i].set(obj, crypt);
                    logger.info("解密处理字段,{}", fields[i].getName());
                } catch (Exception e) {
                    if (lenient) {
                        logger.warn("解密处理失败,宽容处理使用原值");
                    } else {
                        throw new CipherException("解密处理失败,不允许宽容处理["+v+"]", e);
                    }
                }
            }
        }

    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        if (properties != null && StringUtils.isNotBlank(properties.getProperty("secureKey"))) {
            this.secureKey = properties.getProperty("secureKey");
        }
        if (properties != null && StringUtils.isNoneBlank(properties.getProperty("lenient"))) {
            this.lenient = Boolean.parseBoolean(properties.getProperty("lenient"));
        }

    }

}

通过mybatis的插件扩展机制在执行过程进行拦截处理,plugin方法是插件的装载方法,setProperties方法设置关键属性,比如说密钥串。

接下来我们对每个方法进行讲解:

encrypt:是加密方法,这里加密方法需要注意的是,mybatis参数支持Pojo 、MapStrictMapListArray,我们使用注解@Cipher是用在类上的所以只对Pojo生效,如果是Map它天生的key,value格式无法支持打标,我们这里对Map类型进行跳过不处理,如果非要处理Map也是有办法的,需要固定加解密的key值,对特定的key进行识别并加解密替换value,加密方法通过查找有注解@Cipher的字段进行加密并且回填值。

decrypt:是解密方法,主要用在查询时的解密,这里需要注意的是查询有可能返回特定的Pojo也可能返回List,所以这里解密的时候需要根据类型来分别处理,如果是List需要进行很层次查找,如果是Pojo那就查找使用注解@Cipher的字段进行解密并且回填值。

intercept:是拦截方法,在updatequery前后进行拦截处理,在这个方法里需要进行如下步骤:

  • 识别当前执行的methodupdate还是query

    • 如果是update那就进行加密

    • 如果是query那就进行解密

  • 识别参数类型是ListStrictMapPojo

    • 如果StrictMap里是list那就循环调用encrypt方法

    • 如果StrictMap里是array那就循环调用encrypt方法

    • 如果List里是Pojo那就循环调用encrypt方法

    • 如果List里是Map跳过处理,或者使用上面我们说的识别某些固定key进行加密处理

    • 如果是List需要再深层次看一下List里是什么类型,这里建议使用递归方式

    • 如果是StrictMap需要再深层次看一下StrictMap里是什么类型,这里建议使用递归方式

    • 如果是Pojo那就调用encrypt方法

  • 执行sql处理获取返回值

  • 获取返回值并且执行的方法是query时,进行解密处理

    • 如果是List深层次查找内部类型,这里建议使用递归方式

    • 如果是Pojo那就调用decrypt方法

    • 如果List里是Pojo那就循环调用decrypt方法

    • 如果List里是Map跳过处理,或者使用上面我们说的识别某些固定key进行解密处理

    • 识别返回的类型是List还是Pojo

到这里数据加解密的核心逻辑就介绍完了。

这里我们回顾一下,我们先是对数据库连接密码进行加解密,然后又对敏感数据落库和查询时进行加解密,第一步连接密码加密预防坏人即使攻击拿到了应用服务器的操作权限他也无法轻易的攻克我们的数据库访问密码,第二步敏感数据加解密预防坏人即使攻克了我们的数据库(俗称脱库)他也无法获取用户的隐私数据,这样就有效的保证了用户隐私数据的安全性。

到这里我们就对软件程序层面的数据安全防护手段介绍完毕,接下来我们再从人员访问控制方面来看看有什么有效的手段。

人员层面

前面我们说了绝大多数的数据泄密都不是技术问题而是人员管理问题,我们要对人员进行有效的管理与控制。

开发或测试人员

这类人一般对数据是有CRUD的诉求,针对这类人员的控制有如下几个方面

  • 开发人员只能连接测试环境数据库,不允许连接生产数据库,即使连接vpn也不行。

  • 开发人员申请数据库需要走运维工单流程,运维提供数据库连接密码时应直接提供密文,或者运维直接给配置到配置中心。

  • 配置文件或配置中心禁止存储明文密码,需要对jdbc等其他敏感密码进行脱敏处理。

  • 生产服务器需要通过跳板机访问,禁止开发使用root直接操作,如果看应用日志可以走日志平台,实在没有日志平台可以给跳板机开通app用户只给查看固定目录日志的权限,如果要发布走devops平台,如果没有可以提供给运维进行发布。

  • 查询生产数据走dms平台,对敏感信息进行脱敏或隐藏,对上线的sql和日常的查询日志做到dms可管控。

  • 提交到开放环境时需要注意以下几点

    • 提交到开放的仓库(githubgitlabgitee等),需要对代码进行审核,避免有hardcode的公司服务器密码、ip、端口、密钥等。

    • 提交到开放的论坛(csdnoschina知乎公众号社区分享等),需要对文章进行审核,避免有不允许公开的技术细节或敏感信息。

运维或DBA人员

这类人一般操作权限都很高,出问题概率最高的人员,有很多删库跑路或误操作rm -rf的例子哈哈哈!所以这类人更要重点管控。

  • 需要搭建和处理运维工单平台,用于开发提出的运维资源申请,尤其是数据库密码,直接提供加密后的密文和公钥。

  • 需要搭建和处理数据库管理工具dms,用于开发日常生产数据查询和发版时SQL升级。

  • 需要提供跳板机和给跳板机提供不同等级的用户,提供给特别需要的人访问生产环境机器。

  • 需要提供devops平台或者自动化发版工具,避免手动操作失误带来问题,对开发提供升级发布的流水线。

  • 对服务器密码需要进行加密存储,可以借助密码管理工具。

  • 运维最好也不要使用root用户操作服务器,使用特定权限的用户操作。

  • dba最好也不要使用root用户操作数据库,使用特定权限的用户操作。

  • 制定责任人机制,对应的责任项必须到具体人,具体可以参考 责任分配矩阵RAM 。

  • 关键重要的操作需要至少两个人在场,具体可以参考 责任分配矩阵RAM 。

产品或业务人员

这类人一般对数据有查询和分析的诉求,有分析诉求就需要导出数据,所以分析诉求统一走公司BI工具,也有少部分有修改的诉求。

  • 查询分析数据,统一接入BI工具,并且BI工具需要有功能和数据权限,并对敏感数据导出加以控制,导出走审批并脱敏。

  • 提交数据变更,统一接入dms平台。

  • 产品的分析文件(word、excel、ppt)应该进行加密,这种一般依赖公司引入文档安全的解决方案,要花钱的,如果不想花钱那就没啥好办法。

总结

到这里整篇也就差不多都介绍完了,我们现在回顾一下,首先我们介绍了数据安全的问题,并且说明了安全问题一般发生在两个方面,一个是软件程序,一个是人员管理。

我们在软件程序方面介绍了两种预防数据安全的手段,一个是数据库连接密码加解密,一个是数据加解密,数据库连接加密可以有效预防服务器被攻击后通过翻找程序来进一步攻击数据库,数据加解密可以有效预防数据库被攻击或脱库后泄漏用户及公司隐私数据。

我们在人员方面首先对人员进行了分类,针对每一类人的诉求谈了管控的手段,说了这么多的控制手段,并不是说对员工不信任,这里说的控制并不等于限制,说了这么多的终极目标是搭建一套有序的安全的管理机制,在安全的范围内给员工提供最大化的发挥空间,规避有心或无心的泄密。

source: //ningyu1.github.io/20201229/datasource-security.html

70603296352e40caef1278653126cac9.png

喜欢,在看

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何有效预防脱库 的相关文章

  • 在 Java 中连接和使用 Cassandra

    我已经阅读了一些关于 Cassandra 是什么以及它可以做什么的教程 但我的问题是如何在 Java 中与 Cassandra 交互 教程会很好 如果可能的话 有人可以告诉我是否应该使用 Thrift 还是 Hector 哪一个更好以及为什
  • Java中反射是如何实现的?

    Java 7 语言规范很早就指出 本规范没有详细描述反射 我只是想知道 反射在Java中是如何实现的 我不是问它是如何使用的 我知道可能没有我正在寻找的具体答案 但任何信息将不胜感激 我在 Stackoverflow 上发现了这个 关于 C
  • Java EE:如何获取我的应用程序的 URL?

    在 Java EE 中 如何动态检索应用程序的完整 URL 例如 如果 URL 是 localhost 8080 myapplication 我想要一个可以简单地将其作为字符串或其他形式返回给我的方法 我正在运行 GlassFish 作为应
  • Java - 将节点添加到列表的末尾?

    这是我所拥有的 public class Node Object data Node next Node Object data Node next this data data this next next public Object g
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • 制作一个交互式Windows服务

    我希望我的 Java 应用程序成为交互式 Windows 服务 用户登录时具有 GUI 的 Windows 服务 我搜索了这个 我发现这样做的方法是有两个程序 第一个是服务 第二个是 GUI 程序并使它们进行通信 服务将从 GUI 程序获取
  • JAXb、Hibernate 和 beans

    目前我正在开发一个使用 Spring Web 服务 hibernate 和 JAXb 的项目 1 我已经使用IDE hibernate代码生成 生成了hibernate bean 2 另外 我已经使用maven编译器生成了jaxb bean
  • Spark 1.3.1 上的 Apache Phoenix(4.3.1 和 4.4.0-HBase-0.98)ClassNotFoundException

    我正在尝试通过 Spark 连接到 Phoenix 并且在通过 JDBC 驱动程序打开连接时不断收到以下异常 为简洁起见 下面是完整的堆栈跟踪 Caused by java lang ClassNotFoundException org a
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • 无法捆绑适用于 Mac 的 Java 应用程序 1.8

    我正在尝试将我的 Java 应用程序导出到 Mac 该应用程序基于编译器合规级别 1 7 我尝试了不同的方法来捆绑应用程序 1 日食 我可以用来在 Eclipse 上导出的最新 JVM 版本是 1 6 2 马文 看来Maven上也存在同样的
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 捕获的图像分辨率太大

    我在做什么 我允许用户捕获图像 将其存储到 SD 卡中并上传到服务器 但捕获图像的分辨率为宽度 4608 像素和高度 2592 像素 现在我想要什么 如何在不影响质量的情况下获得小分辨率图像 例如我可以获取或设置捕获的图像分辨率为原始图像分
  • JGit 检查分支是否已签出

    我正在使用 JGit 开发一个项目 我设法删除了一个分支 但我还想检查该分支是否已签出 我发现了一个变量CheckoutCommand但它是私有的 private boolean isCheckoutIndex return startCo
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List
  • 如何实现仅当可用内存较低时才将数据交换到磁盘的写缓存

    我想将应用程序生成的数据缓存在内存中 但如果内存变得稀缺 我想将数据交换到磁盘 理想情况下 我希望虚拟机通知它需要内存并将我的数据写入磁盘并以这种方式释放一些内存 但我没有看到任何方法以通知我的方式将自己挂接到虚拟机中before an O
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐

  • HTTP 错误 404.3 – Not Found 由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。

    今天 在vs2013中新建了一个placard json文件 当我用jq读取它的时候 去提示404 直接在浏览器访问这个文件 提示 HTTP 错误 404 3 Not Found 由于扩展配置问题而无法提供您请求的页面 如果该页面是脚本 请
  • session 和 cookie 有什么区别?

    Session 和 Cookie 都是Web开发中非常重要的概念 它们用于保存Web应用程序状态和用户信息 但是它们有一些重要的区别 1 存储位置不同 Cookie 存储于客户端 浏览器 而 Session 存储于服务器端 对于服务器端存储
  • python训练模型、如何得到模型训练总时长_模型训练时间的估算

    模型训练时间的估算 昨天群里一个朋友训练一个BERT句子对模型 使用的是CPU来进行训练 由于代码是BERT官方代码 并没有显示训练需要的总时间 所以训练的时候只能等待 他截图发了基本的信息 想知道训练完整个模型需要多久 最开始跑BERT模
  • 如何做一个人工智能聊天机器人的毕业设计

    毕业设计是大学生的必修课程之一 许多学生在毕业设计中选择了人工智能方向的课题 人工智能聊天机器人是一个很好的毕业设计课题 它涉及到自然语言处理 机器学习 深度学习等人工智能的核心技术 做好一个聊天机器人的毕业设计需要考虑好聊天机器人的功能
  • 树莓派3 有线静态路由设置_配置树莓派为wifi热点(AP模式)

    该功能主要用于搭建一个小型的的网络 使得连接至热点的各个设备可以进行通信 用于构建物联网系统 如智能家居 或是无线控制指定设备 另外这一网络也是独立的 并未启用NAT连接至互联网 具有一定的安全性也为研究提供了一定的便利 目前已经有很多方案
  • 删除rabbitmq的队列和队列中的数据

    欢迎访问本人博客查看原文 http wangnan tech 访问http rabbitmq安装IP 15672 帐号guest 密码guest 也可以使用自己创建的帐号 登录后访问http rabbitmq安装IP 15672 queue
  • Error:() java: 程序包org.springframework.beans.factory.annotation不存在

    写在前面 很重要 idea的2019 2020版本确实是存在很多bug的 我也踩过几个坑 我推荐使用idea2018 1 8版本 这个版本比较稳定 我暂时没遇到什么bug 其实遇到这个bug我很高兴 因为之前就出现过这个bug 当时公司前辈
  • 华为30道Python面试题总结

    Python是目前编程领域最受欢迎的语言 在本文中 我将总结华为 阿里巴巴等互联网公司Python面试中最常见的30个问题 每道题都提供参考答案 希望能够帮助你在求职面试中脱颖而出 找到一份高薪工作 这些面试题涉及Python基础知识 Py
  • hutool json转map_记一个Jackson与Hutool混用的坑

    技术公众号 Java In Mind Java In Mind 欢迎关注 问题出现 最近遇到一个问题 Hutool从4 1 7升级到4 6 8之后 使用feign调用出现错误 Caused by feign codec EncodeExce
  • CXF java.lang.RuntimeException: Cannot create a secure XMLInputFactory

    刚开始接触cxf 照着网上的例子写了一个demo 在测试 编写客户端访问服务运行的时候后台报了 CXF java lang RuntimeException Cannot create a secure XMLInputFactory 的错
  • Android gradle配置抽取合并

    一 为什么要合并 当项目中model或library变多过后 比如用到组件化或者引入第三方库需要配置多个build gradle文件 一旦需要统一其SDK或者其他组件版本就需要同时修改多个文件 这确实很麻烦 所以抽取gradle配置非常有必
  • JAVA单元测试框架-9-testng.xml管理依赖

    在testng xml里配置依赖管理 先写个测试用例 Test description 测试分组 groups operation public void TestGroupAdd System out print String value
  • 对七牛云的简单了解

    一 初识七牛云 1 概述 七牛云是国内领先的企业级公有云服务商 致力于打造以数据为核心的场景化PaaS服务 围绕富媒体场景 七牛先后推出了对象存储 融合CDN加速 数据通用处理 内容反垃圾服务 以及直播云服务等 通俗来讲七牛云就是一个服务器
  • UE4基础学习笔记——— 材质编辑器04

    材质实例 原理 不用在原父级材质编辑器中去调节材质 我们把重要的调节值设置为 转换为参数 将材质实例化 要修改只要修改参数即可 选择父级材质右键 创建材质实例 注意标识颜色是 深绿 在实例编辑界面中 出现了之前设置的可变参数 材质实例化方便
  • 《Java Web程序设计——开发环境的搭建》

    Java Web程序设计 开发环境的搭建 一 前言 这里主要分享一下我搭建开发环境的过程以及遇到的问题 其中涉及到的软件都可以从官网获取 若官方访问过慢也可到镜像网站或者下面分享的网盘链接中下载 软件安装路径尽量不要有中文 否则可能会报错
  • 试题 算法训练 拿金币-蓝桥杯

    这里的关键字仍然是动态规划 动态规划核心 拆分子 记住过往 减少重复计算 计算结果 1 不难发现 对于某个确定的路径上的特定位置上的金币总数 总是由该位置的上方的值或左边的值确定的 所以遍历数组位置的上方和左边的 再 比较 递加 就能计算出
  • K8S之资源管理

    文章目录 一 K8S中的资源 二 YAML语言 三 资源管理方式 一 命令式对象管理 二 命令式对象配置 三 声明式对象配置 一 K8S中的资源 在kuberbnetes中 所有的内容都抽象为资源 用户需要通过操作资源来管理kubernet
  • 可视化埋点方案和实践-PC-WEB端(一)

    目录 一 什么是可视化埋点 1 圈选 点选 即标记页面元素 的逻辑代码 2 捕获监听标记的元素的逻辑代码 二 遇到的坑 1 标记元素兼容性难 2 监听难 三 优点 1 方便了测试人员和运营人员 2 埋点的变更是即时的 不需要更新系统代码 3
  • 【Graph Neural Network】 GraphSAGE 基本原理与tensorflow2.0实现

    文章目录 GraphSAGE 前向传播算法 采样算法 聚合 aggragator 操作 参数学习 基于tensorflow2 0实现Graph SAGE GCN是一种利用图结构和邻居顶点属性信息学习顶点Embedding表示的方法 GCN是
  • 如何有效预防脱库

    本篇不从DBA 网络架构层面来讲述数据安全 这部分有很专业的架构和云上产品来解决 本篇重点从开发人员角度讲述如何避免数据安全的漏洞 相信大部分人都看到过这样的新闻 某某论坛泄漏了用户密码 某某物流公司泄漏了用户的手机号等等 我一直坚信大部分