Shiro的加密管理(六)
戒色诗: 二八佳人体似酥,腰间仗剑斩凡夫。虽然不见人头落,暗里教君骨髓枯。
一. 密码为什么要加密?
密码为什么要加密,当然是为了密码的安全性。 由于MD5加密算法是不可逆的,即不能由密文推算出明文,
所以即使数据库管理员或者入侵者知道了你存储在数据库里面的密码,也不会知道你真实的密码,
这样就保证了你账户的安全性。
然而,单纯的只使用 MD5加密,就安全吗?
第一, MD5虽然不能加密,但是却可以碰撞,从而得出原来的明文密码。 如 你的密码是 1234,
或者 abc 这样的简单密码,即使通过 MD5进行加密,也同样是可以被破解出来的。
第二, 同一个程序,所使用的加密算法是一致的。注册者所拥有的初始密码是一致的。
那么,当两个人都没有改变密码的时候,两者存储在数据库里面的密码是一致的。
即 A存储在数据库里面的密码是 AiTsc3dGCn7L6+GJmB7IuQ, B存储在数据库里面的密码也是 AiTsc3dGCn7L6+GJmB7IuQ,那么A,B的密码都没有改变, 其他人使用A,B的账户+初始密码,
也是可以登录进去的。
这样,也是不安全的。 希望能有一个标识,md5在加密的时候,可以考虑到这个标识, 这个标识应该是唯一的, 如用户的id,用户的唯一编号。
这个标识,专业上讲叫做 盐(salt)。
用户在设置自己的密码时,为了方便记忆和输入,通常都比较好记, 即使加上了盐,有时候,也很容易破解。 如 用户的密码是1234, 盐是abc,
那么 1234abc 这个密码很有可能也会被碰撞出来。 有人就想,能不能把 1234abc 这个密码加密后,再加密呢?
多加密几次不就行了吗?
1234 不加密,很容易破解。 1234加盐abc 变成 1234abc 时,也可能很容易破解, 但如果把MD5加密 1234abc 之后的那个密码再加密,是否就不容易破解呢?
如果将MD5加密1234abc后的再加密的字符串再进行加密,是否就更不容易被破解呢? 如果这个次数,足够大呢?
其实,这也是MD5 加密的三个阶段。
二. MD5 加密的简单小例子
package com.yjl.md5;
import org.apache.shiro.crypto.hash.Md5Hash;
public class MD5Demo {
public static void main(String[] args) {
//普通md5加密 md5(明文)
Md5Hash md51=new Md5Hash("1234");
System.out.println("1234加密:"+md51.toBase64());
//加盐, 是 md5(明文+盐)
Md5Hash md52=new Md5Hash("1234","yuejl");
System.out.println("1234加密 盐是yuejl:"+md52.toBase64());
//加密10次, 是md5^10(明文+盐)
Md5Hash md53=new Md5Hash("1234","yuejl",10);
System.out.println("加密10次:"+md53.toBase64());
//项目中,通过使用第三种
Md5Hash md54=new Md5Hash("1234","admin",10);
System.out.println("加密10次:"+md54.toBase64());
}
}
控制台打印输出:
接下来,将 MD5 加密 实际运用到项目里面。
运用的是 上一章节的例子, 自定义 Realm.
三. MD5 加密进行认证
三.一 数据库准备
在 user 表里面添加一列, salt 。 (数据库中加盐即可,加密的次数,一般是固化的)
salt 的值 是 用户的编号值, 密码password 需要改写, 通过 上面的例子运行,得到相应的结果,进行更新密码。
//加密10次, 是md5^10(明文+盐)
Md5Hash md53=new Md5Hash("1234","admin",10);
System.out.println("admin 加密10次:"+md53.toBase64());
//项目中,通过使用第三种
Md5Hash md54=new Md5Hash("1234","yuejl",10);
System.out.println("yuejl 加密10次:"+md54.toBase64());
Md5Hash md55=new Md5Hash("1234","yuezl",10);
System.out.println("yuezl 加密10次:"+md55.toBase64());
这是以前的密码:
内容是: 81dc9bdb52d04dc20036dbd8313ed055
这是现在的密码:
完全不是一个级别的。
三.二 pojo包下的 user 实体添加 一个 salt 字段
private String salt;
添加这个字段,并实现 setter 和getter 方法。
三.三 编写自定义 Realm, 实现认证和授权
MyMd5Realm.java
package com.yjl.md5;
import java.util.List;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import com.yjl.dao.PrivilegeDao;
import com.yjl.dao.RoleDao;
import com.yjl.dao.UserDao;
import com.yjl.dao.impl.PrivilegeDaoImpl;
import com.yjl.dao.impl.RoleDaoImpl;
import com.yjl.dao.impl.UserDaoImpl;
import com.yjl.pojo.Privilege;
import com.yjl.pojo.Role;
import com.yjl.pojo.User;
public class MyMd5Realm extends AuthorizingRealm{
private UserDao userDao=new UserDaoImpl();
private PrivilegeDao privilegeDao=new PrivilegeDaoImpl();
private RoleDao roleDao=new RoleDaoImpl();
@Override
public String getName() {
return "MyMd5Realm";
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) {
System.out.println("进入授权");
User user=(User)paramPrincipalCollection.getPrimaryPrincipal();
System.out.println("输出登录的用户编号:"+user.getCode());
//查询权限
String priSql="select a.* from privilege a where a.id in ( select rp.pid from user_role ur "
+ " left join role_privilege rp "
+"on ur.rid=rp.rid where ur.uid=? ) and a.type=?";
List<Privilege> privilegeList= privilegeDao.findInfosBySql(priSql,user.getId(),2);
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
for(Privilege pri:privilegeList){
if(pri.getPercode()!=null&&!("".equals(pri.getPercode()))){
simpleAuthorizationInfo.addStringPermission(pri.getPercode());
}
}
//查询角色
String roleSql="select a.* from role a left join user_role b on a.id=b.rid "
+" where b.uid=?";
List<Role> roleList=roleDao.findInfosBySql(roleSql, user.getId());
for(Role role:roleList){
simpleAuthorizationInfo.addRole(role.getId()+"");
}
return simpleAuthorizationInfo;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken paramAuthenticationToken)
throws AuthenticationException {
System.out.println("进入认证");
String code=(String)paramAuthenticationToken.getPrincipal();
//根据用户名,去查询相应的数据
User user=userDao.getInfoByNameAndValue("select * from user","code", code);
if(user==null){
//没有查询出来
return null;
}
SimpleAuthenticationInfo simpleAuthenticationInfo=
new SimpleAuthenticationInfo(user,user.getPassword(),
//传入转换后的盐
ByteSource.Util.bytes(user.getSalt()),getName());
return simpleAuthenticationInfo;
}
}
三.四 创建配置文件 customermd5.ini
里面配置加密算法和自定义Realm
[main]
#加密类
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#加密方式
credentialsMatcher.hashAlgorithmName=md5
#加密次数
credentialsMatcher.hashIterations=10
#存储散列后的密码是否为16进制
credentialsMatcher.storedCredentialsHexEncoded=false
#配置自定义realm
myRealm=com.yjl.md5.MyMd5Realm
#配置加密
myRealm.credentialsMatcher=$credentialsMatcher
#注入自定义的realm
securityManager.realm=$myRealm
三.五 编写测试类 MyRealmMd5Test
package com.yjl.md5;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import com.yjl.pojo.User;
public class MyRealmMd5Test {
public static void main(String[] args) throws Exception{
//1. 创建factory
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:customermd5.ini");
//2 获取实例
SecurityManager securityManager=factory.getInstance();
//3 设置
SecurityUtils.setSecurityManager(securityManager);
//4. 获取 Subject
Subject subject=SecurityUtils.getSubject();
//5. 设置token
//直接输入明文 1234 密码即可,不需要手动加密
UsernamePasswordToken token=new UsernamePasswordToken("yuejl","1234");
//6. 执行登录操作
try{
subject.login(token);
User user=(User)subject.getPrincipal();
//可以将这个user 设置到session 里面
System.out.println(user.toString()+"登录成功");
//admin 是管理员
boolean flag=subject.hasRole("1");
System.out.println("是否拥有角色 管理员:"+flag);
//关于角色验证的那些方法,都可以使用
flag=subject.hasRole("2");
System.out.println("是否拥有角色 经理:"+flag);
//admin有删除权限,没有添加权限
flag=subject.isPermitted("dept:add");
System.out.println("是否拥有权限 dept:add "+flag);
flag=subject.isPermitted("dept:delete");
System.out.println("是否拥有权限 dept:delete "+flag);
}catch(Exception e){
e.printStackTrace();
System.out.println("用户名或者密码不正确");
}
}
}
控制台打印输出:
输入传入的密码是 12345, 错误的密码
UsernamePasswordToken token=new UsernamePasswordToken("yuejl","12345");
密码加密是成功的。
本章节代码链接为:
链接:https://pan.baidu.com/s/1clhY9jW0qKyE3ZFA0gp3uA
提取码:9bjw
谢谢您的观看,我是两个蝴蝶飞, 如果喜欢,请关注我,再次感谢 !!!