写在前面

在上一篇文章《Shiro入门学习---使用自定义Realm完成认证|练气中期》当中,我们学会了使用自定义Realm实现shiro数据源的切换,我们可以切换成从关系数据库如MySQL中读取用户认证信息进行认证,亦可从非关系型数据库例如mongodb中读取用户认证信息进行认证。这是一个伟大的进度,这使得我们可以使用shiro来提升我们应用程序的安全度了,

那么,请大家思考一个问题,我们的应用程序真的安全了吗?

我把咱么上一篇文章当中的认证方法代码摘抄在下面给大家看看

/**认证
* @author 赖柄沣 bingfengdev@aliyun.com
* @date 2020-10-04 11:01:50
* @param authenticationToken
* @return org.apache.shiro.authz.AuthorizationInfo
* @throws AuthenticationException
* @version 1.0
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1. 从token中获取用户名
String principal = (String) authenticationToken.getPrincipal(); //2. 根据用户名查询数据库并封装成authenticationinfo对象返回(模拟)
if (principal == "xiangbei") {
AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei","123",this.getName());
return authInfo;
} return null;
}

在16行当中,我们模拟从数据库当中查询出了用户的注册信息,包括账户和密码,并且这里的密码是明文的。这意味着如果我们的用户密码被泄露了(这里用户原因导致的泄露除外),那么一些不友好的朋友将可以随意的进出我们的系统。这不但让我们的应用程序变得不安全,而且还会让我们面临法律风险。

以下内容摘自《网络安全法》

第三十四条 网络运营者应当建立健全用户信息保护制 度,加强对用户个人信息、隐私和商业秘密的保护

第三十五条 网络运营者收集、使用公民个人信息,应当 遵循合法、正当、必要的原则,明示收集、使用信息的目 的、方式和范围,并经被收集者同意。 网络运营者不得收集与其提供的服务无关的公民个人 信息,不得违反法律、行政法规的规定和双方的约定收 集、使用公民个人信息,并应当依照法律、行政法规的规 定或者与用户的约定,处理其保存的公民个人信息。 网络运营者收集、使用公民个人信息,应当公开其收 集、使用规则。

第三十六条 网络运营者对其收集的公民个人信息必须严 格保密,不得泄露、篡改、毁损,不得出售或者非法向他 人提供。 网络运营者应当采取技术措施和其他必要措施,确保 公民个人信息安全,防止其收集的公民个人信息泄露、毁 损、丢失。在发生或者可能发生信息泄露、毁损、丢失的 情况时,应当立即采取补救措施,告知可能受到影响的用 户,并按照规定向有关主管部门报告。

所以,我们需要对用户信息进行加密保护。对于账户密码信息,我们应该采取不可逆的加密方式。也就是说,我们对密码进行加密存储后,哪怕其获取了我们的密文,他也不能得到我们的密码明文。这样就对我们的用户信息起到了一个很好的保护作用。

MD5加密算法和salt盐值加密

MD5加密算法

什么是MD5加密

MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

特点

  1. 不可逆,也就是说其本身上不能由密文推出明文,

    但是,如果明文比较简单常见,还是存在泄露风险,例如先生成好简单明文的密文,然后使用穷举法进行破解;

  2. 对于同一个明文,无论加密多少次其密文都是一样的;

  3. 生成的结果始终是一个16进制的32位字符串。

作用

  1. 数字签名(校验和)

    例如对于一份文件,为了保证网络传输当中不发生改变,我提前对其用md5加密算法进行加密,得到一段密文。我将这份文件和密文分别发给你。你在收到文件后也对其使用md5加密一次,得到一个密文。这时,你就可以比较两个密文是否一致,如果一致,则文件没有被篡改,反之,文件已经被篡改。

  2. 加密

  3. 垃圾邮件筛选

    原理和作用1一样

salt盐值加密策略

在上面的介绍md5加密算法时我们讲到,虽然MD5算法本身不可逆,但是如果用户采用简单的字符串作为密码的话,仍然有被暴力破解的风险。因此,为了解决这个问题,我们需要在对密码加密之前使其变得复杂化。

而加盐就是其中的一种方式。所谓的加盐就是在原密码的基础上,加上一段随机字符串。然后再加密。

当然,如果盐值随着密码一起被泄露出去,也是存在着密码被破解的风险的,我们只能做到相对安全。

为了增加破解难度,可以在加盐时采取一定的策略,例如哈希加盐、加密后多次哈希。

当然,这要在安全跟性能直接做个平衡。

shiro使用MD5+salt加密

分析

在进行编码之前,我们需要理一下流程:

  1. 用户注册或系统分配账户时,服务层在接收到账号和凭证信息后,先对凭证信息采用md5+salt进行加密处理,然后将账号、加密后的密码还有盐值存入数据库;

  2. 用户登录请求接收后,先根据请求中的账号查询数据库:

    2.1 如果没有查到,直接返回“用户名或密码错误”的类似提示

    2.2 如果查到了账户信息,就执行步骤3;

  3. 将账号和加盐后凭证封装成AuthenticationInfo对象返回给shiro,shiro执行步骤4

  4. 对请求中的凭证进行加盐处理并执行步骤5

  5. 对加盐后的凭证进行md5加密,并将密文跟数据库当中的存储的密文进行比对:

    5.1 如果匹配成功,则认证通过

    5.2 如果匹配失败,则返回“用户名或密码错误”的类似提示

实现

编写自定义Realm并切换掉默认的凭证匹配器

/**自定义Realm对象
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/4 11:00
*/
public class MySqlRealm extends AuthorizingRealm { public MySqlRealm() {
//设置凭证匹配器,修改为hash凭证匹配器
HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();
//设置算法
myCredentialsMatcher.setHashAlgorithmName("md5");
//散列次数
myCredentialsMatcher.setHashIterations(1024);
this.setCredentialsMatcher(myCredentialsMatcher);
} /**授权
* @author 赖柄沣 bingfengdev@aliyun.com
* @date 2020-10-04 11:01:50
* @param principalCollection
* @return org.apache.shiro.authz.AuthorizationInfo
* @throws AuthenticationException
* @version 1.0
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null;
} /**认证
* @author 赖柄沣 bingfengdev@aliyun.com
* @date 2020-10-04 11:01:50
* @param authenticationToken
* @return org.apache.shiro.authz.AuthorizationInfo
* @throws AuthenticationException
* @version 1.0
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1. 从token中获取用户名
String principal = (String) authenticationToken.getPrincipal(); //2. 根据用户名查询数据库并封装成authenticationinfo对象返回(模拟)
if (principal == "xiangbei") {
//四个参数分别是数据库中的账号、加密后的密码、盐值、realm名字
AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei",
"ff595c47b51b4cf70fddce090f68879e",
ByteSource.Util.bytes("ee575f62-0dda-44f2-b75e-4efef795018f"),
this.getName());
return authInfo;
} return null;
}
}

编写认证器

/**认证管理器
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/4 11:11
*/
public class CurrentSystemAuthenticator {
private DefaultSecurityManager securityManager;
public CurrentSystemAuthenticator() {
//创建安全管理器
securityManager = new DefaultSecurityManager(); //设置自定义realm
this.securityManager.setRealm(new MySqlRealm()); //将安全管理器设置到安全工具类中
SecurityUtils.setSecurityManager(securityManager); } public void authenticate(String username,String password) {
//获取当前登录主题
Subject subject = SecurityUtils.getSubject(); //生成toeken
UsernamePasswordToken token = new UsernamePasswordToken(username, password); //进行认证
try {
subject.login(token);
}catch (UnknownAccountException | IncorrectCredentialsException e) {
System.out.println("用户名或密码不正确");
} //打印认证状态
if (subject.isAuthenticated()){
System.out.println(token.getPrincipal()+" 认证通过!");
}else {
System.out.println(token.getPrincipal()+" 认证未通过!");
} }
}

测试

生成加密后的密码
/**
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/4 21:37
*/
public class Md5Test { @Test
public void testMd5(){
//三个参数分别对应密码明文、盐值、散列次数
String salt = UUID.randomUUID().toString();
Md5Hash md5Hash = new Md5Hash("123", salt,1024);
System.out.println("密文:"+md5Hash.toHex());
System.out.println("盐值:"+salt);
}
}

输出

密文:ff595c47b51b4cf70fddce090f68879e
盐值:ee575f62-0dda-44f2-b75e-4efef795018f
进行认证测试
/**
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/4 11:20
*/
public class AuthcTest {
private CurrentSystemAuthenticator authenticator;
@Before
public void init() {
this.authenticator = new CurrentSystemAuthenticator();
} @Test
public void testAuthc(){
this.authenticator.authenticate("xiangbei","123");
} }

输出

xiangbei 认证通过!

写在最后

在这篇文章当中,我们主要是简单了解了shiro中的加密策略以及如何使用MD5+salt对密码进行加密。大家可以尝试着将MD5换成SHA-256加密算法再测一下。

在下一篇文章当中,作者将介绍SpringBoot整合Shiro的相关内容,文章可能有点长,会考虑分两次写。请大家多多关注。

欢迎大家点赞、转发、分享。转载注明出处时要带有原文链接。

最新文章

  1. 交易系统使用storm,在消息高可靠情况下,如何避免消息重复
  2. 用ant组建测试框架
  3. LINQ to Entities不识别方法***,因此该方法无法转换为存储表达式
  4. asp.net项目下的web service返回json数据问题
  5. SQL面试积累
  6. 解决服务器复制中SID冲突问题
  7. 微软开放技术(中国)携 CKAN 和 OData 技术引入基于 Azure 的开放数据平台
  8. Android百度地图
  9. uestc 1725 吴神数
  10. 使用vagrant作为开发环境后,js报错
  11. linux下实现tomcat定时自动重启
  12. lnmp 60秒的服务器缓存时间
  13. python练习linux下创建路径
  14. cloud computing platform,virtual authentication encryption
  15. mysql ifnull if
  16. poj 1979 Red and Black(dfs)
  17. poj2826(线段相交)
  18. 全国各大 oj 分类题集...
  19. ajax 注册
  20. eclipse导入源码

热门文章

  1. Spring Security如何优雅的增加OAuth2协议授权模式
  2. Mc小冰总结的Android开发工程师面试题以及答案,android程序员必备,详解
  3. Java拷贝——深拷贝与浅拷贝
  4. pybind11: C++ 工程如何提供 Python 接口
  5. 【学习中】Fitness Schedule
  6. Python模拟保护和私有成员
  7. C. News Distribution(并查集)
  8. linux的五种IO模型
  9. uniapp 获取元素高度 距离顶部高度等
  10. 柱状图bar