Bootstrap

Java研学-Shiro安全框架(三)

五 Shiro 加密

1 介绍

  如何实现项目中密码加密(这里我们采用的是 MD5 加密)的功能:1.添加用户的时候,对用户的密码进行加密;2. 登录时,按照相同的算法对表单提交的密码进行加密然后再和数据库中的加密过的数据进行匹配

  MD5 加密特点:属于不可逆的加密算法(只能从明文→密文,Shiro 中已经集成了MD5,直接使用即可),如果MD5 加密的数据一样,那么无论在什么时候加密的结果都是一样的,所以,相对来说还是不够安全,因此需要加盐。

  盐一般要求是固定长度的字符串,且每个用户的盐不同。可以选择用户的唯一的数据来作为盐(账号名,身份证等等)。

// demo
public class Md5Demo{
    @Test
    public void testMd5(){
    	//(密码,盐,加密次数)
        Md5Hash hash = new Md5Hash("123456","abc",3);
        System.out.println(hash);
        Md5Hash hash2 = new Md5Hash("123456","123",3);
        System.out.println(hash2);
    }
}

流程图

2 Realm加盐加密

  ① 实体类加盐

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
    private String username;//用户名
    private String password;//密码
    private String salt;//盐
}

  ② mapper 加盐

public class DataMapper {
    //用户集合
    private static Map<String, User> userData = new HashMap<String, User>();
    //角色集合
    private static Map<String, List<String>> roleData = new HashMap<String, List<String>>();
    //权限集合
    private static Map<String, List<String>> permissionData = new HashMap<String, List<String>>();
    static{
        //初始化用户数据
        User u1 = new User("dahuang","da22e1674b0cfc8eccf66e7414a1080f","abc");
        userData.put(u1.getUsername(),u1);
        roleData.put(u1.getUsername(), Arrays.asList("seller"));
        permissionData.put(u1.getUsername(),
                Arrays.asList("customer:list","customer:save"));

        User u2 = new User("xiaohuang","9f19b93f43eb266a13b1f261cf1f6bb1","123");
        userData.put(u2.getUsername(),u2);
        roleData.put(u2.getUsername(), Arrays.asList("seller","hr"));
        permissionData.put(u2.getUsername(),
                Arrays.asList("customer:list","customer:save","user:list","user:delete"));
    }
    //提供静态方法,模拟数据库返回数据
    public static User getUserByName(String username){
        return userData.get(username);
    }
    public static List<String> getRoleByName(String username){
        return roleData.get(username);
    }
    public static List<String> getPermissionByName(String username){
        return permissionData.get(username);
    }
}

  ③ UserRealm 加盐

public class UserRealm extends AuthorizingRealm {
    // 授权功能
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取当前登录用户
        User user = (User) principals.getPrimaryPrincipal(); //获取在 doGetAuthenticationInfo 中存放在上下文中的对象。
        String username = user.getUsername();
        // 当前登录用户的角色集合
        List<String> roles = DataMapper.getRoleByName(username);
        // 当前登录用户的权限集合
        List<String> permissions = DataMapper.getPermissionByName(username);
        // 需要将当前登录用户的权限和角色集合与返回的SimpleAuthorizationInfo 绑定关系
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;
    }
    // 认证功能
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 如何获取数据?  返回当前用户名(map的key)
        // String username = (String) token.getPrincipal();
        // 此处使用它的子类获取token
        UsernamePasswordToken token1 = (UsernamePasswordToken) token;
        // 通过token获取用户名
        String username = token1.getUsername();
        // 根据用户名去mapper中查询数据
        User user = DataMapper.getUserByName(username);
        if(user == null){
            return null;
        }
        // 需要返回一个  SimpleAuthenticationInfo 对象
        // 4个参数 (需要存储到上下文环境中的对象(登录成功后向session中存对象),密码信息,盐,Realm名字(这是父类的方法))
        // 若编码字符集不同则对中文的编码也不同,所以盐不允许传字符串,需要传byte数组(调工具类的方法转ByteSource对象)
        return new SimpleAuthenticationInfo(user,user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),getName());
    }
}

  ④ 测试

public class ShiroDemo {
	@Test
    public void testLoginByUserRealmMd5(){
        // 1. 创建安全管理器
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        // 2. 创建自定义 UserRealm 对象。
        UserRealm realm = new UserRealm();
        // 设置凭证匹配器(加密算法和加密次数)
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("md5");// 加密算法
        matcher.setHashIterations(3);// 加密次数
        // 关联 realm 和 凭证匹配器
        realm.setCredentialsMatcher(matcher);
        // 3. 将 Realm 对象设置给安全管理器
        securityManager.setRealm(realm);
        // 4. 将安全管理器设置到上下文对象中
        SecurityUtils.setSecurityManager(securityManager);
        // 5. 从上下文对象中获取 Subject 对象
        Subject subject = SecurityUtils.getSubject();
        // 6. 封装账号密码
        UsernamePasswordToken token = new UsernamePasswordToken("dahuang","123456");
        // 7. 进行登录操作
        subject.login(token);
        // *****验证目前是否已经登录*****
        System.out.println("当前用户状态:" + subject.isAuthenticated());
        // 获取当前登录对象
        System.out.println("当前登录用户为:" + subject.getPrincipal());
    }
}
;