`

APDPlat中的用户密码安全策略

阅读更多

互联网时代,安全是永恒的主题,威胁无处不在,哪怕是在企业内网。

 

APDPlat充分考虑到了安全的问题:

 

首先,在浏览器中对用户密码加入复杂字符({用户信息})之后进行加密(Secure Hash Algorithm,SHA-512,as defined in FIPS 180-2),在服务器端加入用户名和复杂字符之后再次加密,提高破解复杂度;

 

其次,在浏览器和服务器之间采用安全通道(HTTPS)传输用户信息,避免信息泄露。

 

再次安全易用相互矛盾,不同的应用需要的平衡点不一样,APDPlat充分考虑到了这个问题,提供了可配置的用户密码安全策略,以满足不同的需求。

 

下面详细介绍相关的设计和实现:

 

首先,在浏览器对用户的登陆密码进行加密,使用的sha512.js来源于http://pajhome.org.uk/crypt/md5/

 

j_password=hex_sha512(j_password+'{用户信息}');

 

在服务器端对新增用户修改密码重置密码时候的密码进行加密,使用类org.apdplat.module.security.service.PasswordEncoder的encode方法:

 

/**
 * 用户密码双重加密:
 * 1、使用SHA-512算法,salt为user.getMetaData(),即:用户信息
 * 2、使用SHA-256算法,salt为saltSource.getSalt(user),即:用户名+APDPlat应用级产品开发平台的作者是杨尚川,联系方式(邮件:ysc@apdplat.org)(QQ:281032878)
 * @author 杨尚川
 */
public class PasswordEncoder {
    public static String encode(String password,User user){
        password = new ShaPasswordEncoder(512).encodePassword(password,user.getMetaData());
        SaltSource saltSource = SpringContextUtils.getBean("saltSource");
        return new ShaPasswordEncoder(256).encodePassword(password,saltSource.getSalt(user));
    }
    public static void main(String[] args){
        User user = new User();
        user.setUsername("admin");
        user.setPassword("admin");
        String password = new ShaPasswordEncoder(512).encodePassword(user.getPassword(),user.getMetaData());
        System.out.println("Step 1 use SHA-512: "+password+" length:"+password.length());
        SaltSource saltSource = new APDPlatSaltSource();
        password = new ShaPasswordEncoder(256).encodePassword(password,saltSource.getSalt(user));
        System.out.println("Step 2 use SHA-256: "+password+" length:"+password.length());
    }
}

 

/**
 * 用户salt服务,salt为:
 * 用户名+APDPlat应用级产品开发平台的作者是杨尚川,联系方式(邮件:ysc@apdplat.org)(QQ:281032878)
 * @author 杨尚川
 */
@Service("saltSource")
public class APDPlatSaltSource implements SaltSource{
    @Override
    public Object getSalt(UserDetails user) {
        //变化的用户名+固定的字符串
        String text = user.getUsername()+"APDPlat应用级产品开发平台的作者是杨尚川,联系方式(邮件:ysc@apdplat.org)(QQ:281032878)";
        return text;
    }
}

 

 

用户在登陆的时候,浏览器进行一次加密,服务器进行二次加密,服务器加密的spring security配置如下:

 

<s:authentication-manager alias="authenticationManager">
	<s:authentication-provider   user-service-ref="userDetailsServiceImpl" >
		<s:password-encoder  hash="sha-256">
			<s:salt-source ref="saltSource"/>
		</s:password-encoder>
	</s:authentication-provider>
</s:authentication-manager>

  

 

其次,使用HTTPS安全通道,在配置文件config.local.properties中指定channel.type的值为https

 

#指定数据传输通道,可选值为http或https
channel.type=https
http.port=8080
https.port=8443

 

再次,提供了可配置的用户密码安全策略,在配置文件config.local.properties中指定user.password.strategy的值,可在安全易用之间进行平衡,这里的值为相应策略类的spring bean name:

 

#用户密码安全策略
user.password.strategy=passwordLengthStrategy;passwordComplexityStrategy

 

接下来看看跟用户密码安全策略相关的设计与实现:

 

用户密码安全策略接口

 

/**
 * 用户密码安全策略
 * @author 杨尚川
 */
public interface PasswordStrategy {
    /**
     * 检查用户的密码是否符合安全策略
     * @param password 用户密码
     * @throws PasswordInvalidException 不合法的原因包含在异常里面
     */
    public void check(String password) throws PasswordInvalidException;
}

 

APDPlat默认提供了2个安全策略,分别是密码长度安全策略密码复杂性安全策略

 

1、密码长度安全策略:

 

/**
 * 密码长度安全策略
 * 密码长度必须大于等于6
 * @author 杨尚川
 */
@Service
public class PasswordLengthStrategy implements PasswordStrategy{
    private static final APDPlatLogger LOG = APDPlatLoggerFactory.getAPDPlatLogger(PasswordLengthStrategy.class);

    @Override
    public void check(String password) throws PasswordInvalidException {
        if(StringUtils.isBlank(password) || password.length() < 6){
            String message = "密码长度必须大于等于6";
            LOG.error(message);
            throw new PasswordInvalidException(message);
        }
        LOG.info("密码符合安全策略");
    }
}

 

2、密码复杂性安全策略

 

/**
 * 密码复杂性安全策略:
 * 1、密码不能为空
 * 2、密码不能全是数字
 * 3、密码不能全是字符
 * @author 杨尚川
 */
@Service
public class PasswordComplexityStrategy implements PasswordStrategy{
    private static final APDPlatLogger LOG = APDPlatLoggerFactory.getAPDPlatLogger(PasswordComplexityStrategy.class);

    @Override
    public void check(String password) throws PasswordInvalidException {
        if(StringUtils.isBlank(password)){
            String message = "密码不能为空";
            LOG.error(message);
            throw new PasswordInvalidException(message);            
        }
        if(StringUtils.isNumeric(password)){
            String message = "密码不能全是数字";
            LOG.error(message);
            throw new PasswordInvalidException(message);            
        }
        if(StringUtils.isAlpha(password)){
            String message = "密码不能全是字符";
            LOG.error(message);
            throw new PasswordInvalidException(message);            
        }
        LOG.info("密码符合安全策略");
    }
}

 

有了不同的策略之后,还需要一个类来执行配置文件指定的策略

 

/**
 * 密码安全策略执行者
 * 根据配置项user.password.strategy
 * 指定的spring bean name
 * 分别执行指定的策略
 * @author 杨尚川
 */
@Service
public class PasswordStrategyExecuter implements PasswordStrategy, ApplicationListener {
    private static final APDPlatLogger LOG = APDPlatLoggerFactory.getAPDPlatLogger(ApplicationListener.class);
    private final List<PasswordStrategy> passwordStrategys = new LinkedList<>();

    @Override
    public void check(String password) throws PasswordInvalidException {
        for(PasswordStrategy passwordStrategy : passwordStrategys){
            passwordStrategy.check(password);
        }
    }
    
    @Override
    public void onApplicationEvent(ApplicationEvent event){
        if(event instanceof ContextRefreshedEvent){
            LOG.info("spring容器初始化完成,开始解析PasswordStrategy");
            String strategy = PropertyHolder.getProperty("user.password.strategy");
            if(StringUtils.isBlank(strategy)){
                LOG.info("未配置user.password.strategy");
                return;
            }
            LOG.info("user.password.strategy:"+strategy);
            String[] strategys = strategy.trim().split(";");
            for(String item : strategys){
                PasswordStrategy passwordStrategy = SpringContextUtils.getBean(item.trim());
                if(passwordStrategy != null){
                    passwordStrategys.add(passwordStrategy);
                    LOG.info("找到PasswordStrategy:"+passwordStrategy);
                }else{
                    LOG.info("未找到PasswordStrategy:"+passwordStrategy);
                }
            }
        }
    }
}

 

有了执行安全策略的服务之后,需要在新增用户修改密码重置密码的地方验证用户密码的安全用户服务类中进行验证APDPlat_Core/src/main/java/org/apdplat/module/security/service/UserService.java:

 

首先,注入密码安全策略执行者:

 

@Resource(name="passwordStrategyExecuter")
private PasswordStrategyExecuter passwordStrategyExecuter;

 

其次,在新增用户的时候验证密码是否符合安全策略:

 

//先对用户的密码策略进行验证
try{
	passwordStrategyExecuter.check(model.getPassword());
}catch(PasswordInvalidException e){
	throw new RuntimeException(e.getMessage());
}
LOG.debug("加密用户密码");
model.setPassword(PasswordEncoder.encode(model.getPassword(), model));

 

再次,在修改密码的时候验证密码是否符合安全策略:

 

for(Property property : properties){
	if("password".equals(property.getName().trim())){
		//先对用户的密码策略进行验证
		try{
			passwordStrategyExecuter.check(property.getValue().toString());
		}catch(PasswordInvalidException e){
			throw new RuntimeException(e.getMessage());
		}
		property.setValue(PasswordEncoder.encode(property.getValue().toString(),user));
		break;
	}
}

 

//先对用户的密码策略进行验证
try{
	passwordStrategyExecuter.check(newPassword);
}catch(PasswordInvalidException e){
	result.put("success", false);
	result.put("message", e.getMessage());
	LOG.error(e.getMessage());
	return result;
}            
oldPassword=PasswordEncoder.encode(oldPassword.trim(),user);
if(oldPassword.equals(user.getPassword())){
	user.setPassword(PasswordEncoder.encode(newPassword.trim(),user));
	serviceFacade.update(user);
	message = "修改成功";
	result.put("success", true);
	result.put("message", message);
	LOG.info(message);
}else{
	message = "修改失败,旧密码错误";
	result.put("success", false);
	result.put("message", message);
	LOG.error(message);
}

 

最后,在重置密码的时候验证密码是否符合安全策略:

 

//先对用户的密码策略进行验证
try{
	passwordStrategyExecuter.check(password);
}catch(PasswordInvalidException e){    
	LOG.error(e.getMessage());
	return e.getMessage();
}
int success = 0;
for(int id : ids){
	User user = serviceFacade.retrieve(User.class, id);
	if(user == null){
		LOG.error("ID为 "+id+" 的用户不存在,无法为其重置密码");
		continue;
	}
	if(PropertyHolder.getBooleanProperty("demo") && "admin".equals(user.getUsername())){
		LOG.error("演示版本不能重置admin用户的密码");
		continue;
	}
	//设置新密码
	user.setPassword(PasswordEncoder.encode(password, user));
	//同步到数据库
	serviceFacade.update(user);
	success++;
} 

 

后记(说明APDPlat中密码安全策略的重要性): 

 

假设用户密码明文为:123456,我们使用http://pajhome.org.uk/crypt/md5/的MD5计算功能进行密文的计算,使用http://www.cmd5.com/的解密功能进行明文的计算。

1、明文123456计算得到密文为:e10adc3949ba59abbe56e057f20f883e,然后对密文进行解密,立马得到明文:123456

2、我们加大密码长度,在密码后面填充3个0,把密码变为123456000,计算得到密文为3bc2fbdd89ef79f3dbfbaf1f2132baa1,然后对密文进行解密,解密页面提示:已查到,这是一条付费记录,密文类型:md5。说明还是能解密。

3、我们加大密码复杂度,在每两个密码之间填充更多的复杂字符,把密码变为{~$(!APDPlat)1(是)2(一个)3(应用级产品)4(开发平台)5(帮助您)6(快速开发企业级应用程序)$~},计算得到密文为:59f1cade9738167f3b4070e29da5af2e,然后对密文进行解密,解密页面提示:未查到,已加入本站后台解密。要想破解这么复杂的密码不是容易的事,何况APDPlat采用了比MD5更安全的SHA-512加密算法。

 

从上面的分析可以看到,保证密码安全并不容易,为了防止用户密码在网络传输中被监听泄露,我们使用HTTPS安全通道,为了避免用户的明文密码在网络上传输,先在客户端进行了加密,当然了,在客户端的加密规则很容易就暴露了,我们需要在服务器端再进行一次加密,也就是把客户端加密过后的密文当做明文,这样最终的用户密码密文就很难再破解出来了。

 

总结一下,用户密码主要面临两方面的威胁:一是在用户登陆的时候,需要将密码提交给服务器以验证身份,用户的密码有可能在网络传输过程中被监听导致泄露,用户在输入密码的时候也有可能被旁边的人看到,用户输入的密码也有可能被计算机中的病毒木马窃取;二是存储在服务器端数据库中的用户密码有可能被网站工作人员或黑客获取到,如果存储的用户密码未加密而是明文存储,那么就相当危险了,就算是加密了,如果采用的算法有缺陷(王小云为首的研发团队破译了MD5和SHA-1)且密码过于简单,也有可能被破解。

 

参考资料:

1、http://www.cmd5.com/

2、http://pajhome.org.uk/crypt/md5/

3、http://zh.wikipedia.org/wiki/%E7%8E%8B%E5%B0%8F%E9%9B%B2

 

APDPlat托管在Github

 

 

4
6
分享到:
评论

相关推荐

    应用级产品开发平台APDPlat.zip

    APDPlat提供了应用容器、多模块架构、代码生成、安装程序、认证授权、备份恢复、数据字典、web service、系统监控、操作审计、统计图、报表、机器绑定、防止破解、数据安全、内置搜索、数据转换、maven支持、WEB...

    开源项目案例访谈整理版:APDPlat1

    在2013年中国优秀开源项目评选中,APDPlat获得了高人气,目前有超过930名开发者在GitHub上对其进行跟踪、研究和使用。 APDPlat的核心成员数量相对较少,但拥有广泛的开发者社区支持。它基于JAVA开源框架构建,适用...

    APDPlat中数据库备份恢复的设计与实现

    在APDPlat中,数据库备份恢复的设计与实现是一个关键功能,它旨在简化数据库的维护工作,通过提供web接口支持手工操作和定时调度。以下是该系统的一些核心知识点: 1. **多数据库支持**:APDPlat设计了一个统一的`...

    APDPlat应用级产品开发平台_共27张UML设计图

    在APDPlat中,这些设计图可能包括类图、包图、用例图、序列图、状态图、活动图等,每种图都有其特定的用途。 1. **类图**:类图展示了系统的静态结构,包括类、接口以及它们之间的关系,如继承、实现和关联。在...

    基于Java和JavaScript的APDPlat应用级产品开发平台源码

    该项目为APDPlat应用级产品开发平台的设计源码,采用Java和JavaScript为主要编程语言,辅以CSS、HTML等前端技术。...APDPlat平台旨在提供高效的应用级产品开发环境,适用于各类软件项目的快速构建和迭代。

    基于Java的APDPlat应用级产品开发平台设计源码镜像站点

    该平台是一款基于Java的APDPlat应用级产品开发平台,由2902个文件组成,涵盖1750个GIF图像、282个Java源文件、269个PNG图像、187个JavaScript文件、156个CSS样式表、34个JSP页面、31个XML配置文件、30个PSD设计文件...

    中文分词工具word-1.0,Java实现的中文分词组件多种基于词典的分词算法

    保留停用词:List&lt;Word&gt; words = WordSegmenter.segWithStopWords("杨尚川是APDPlat应用级产品开发平台的作者"); System.out.println(words); 输出: 移除停用词:[杨尚川, apdplat, 应用级, 产品, 开发平台, 作者...

    中文分词词典-54w条词语

    中文分词词典 适合最大正向匹配算法使用 共计548389条词语

    一个非常好的检测lucene索引库的工具

    标题中的“一个非常好的检测lucene索引库的工具”指的是用于检查和验证Apache Lucene索引库完整性和功能的专用工具。Apache Lucene是一个开源的全文检索库,它提供了核心的搜索功能,广泛应用于各种信息检索系统。这...

    EXTJS产品级别管理后台源代码

    在压缩包文件`ysc-APDPlat-5e7a864`中,我们可以预期找到以下内容: - `app.js`或类似文件,这是EXTJS应用的主要入口点,包含了应用的配置和初始化逻辑。 - `controller`目录,存放了各种控制器文件,比如产品级别...

    word分词文本相似度计算.docx

    在给定的代码中,使用了`org.apdplat.word.segmentation.Word`类,表明使用了APDPlat分词库。 2. **构建词频矩阵**:对于每个文本,统计每个词的出现次数,生成一个词频向量。在代码中,`...

    2013年度中国优秀开源项目列表

    4. APDPlat:APDPlat是一个应用级产品开发平台,提供各种模块以加速基于B/S多层架构的信息管理系统开发。 5. ArduBlock:作为Arduino平台的图形化编程工具,ArduBlock降低了非编程人员使用硬件实现创新想法的门槛。...

    word-1.2.jar

    中文分词,word分词,附件是编译好的word-1.2.jar版本 maven依赖: &lt;groupId&gt;org.apdplat &lt;artifactId&gt;word &lt;version&gt;1.2 &lt;/dependency&gt;

    pageExtractor

    ##HtmlExtractor是一个Java实现的基于模板的网页结构化信息精准抽取组件,本身并不包含爬虫功能,但可被爬虫或其他程序调用...html-extractor是一个jar包,可通过maven引用:&lt;dependency&gt; &lt;groupId&gt;org.apdplat&lt;/gr

Global site tag (gtag.js) - Google Analytics