35 | SpringBoot整合Shiro之MD5盐值加密

tech2024-06-07  60

前言

这篇文章是对上一篇 34 | Spring Boot整合Shiro框架(Shiro简介+实现登录拦截、用户认证、请求授权并整合Mybatis和Thymeleaf)的扩展

1. MD5盐值加密和MD5加密的区别

当两个用户的密码相同时,单纯使用不加盐的MD5加密方式,会发现数据库中存在相同结构的密码,这样是不安全的。为了实现两个人的原始密码一样,但加密后的结果是不一样的效果,就要使用加了盐的MD5加密方式。其实就好像炒菜一样,两道一样的鱼香肉丝,加的盐不一样,炒出来的味道就不一样。

2. 使用步骤

2.1 在控制层的注册用户方法中使用MD5盐值加密

@PostMapping("/register") public String register(User user) { // 设置盐值 Object salt = ByteSource.Util.bytes(user.getName()); // 加密后的密码 String newPassword = new SimpleHash("MD5", user.getPassword(), salt, 1000).toHex(); user.setPassword(newPassword); if (userService.registerUser(user)) { return "redirect:/toLogin"; } return "register"; }

2.2 在UserRealm的认证方法中加入盐值校验

@Override // 认证 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("执行了=>认证doGetAuthorizationInfo"); // 封装用户的登录数据 UsernamePasswordToken userToken = (UsernamePasswordToken) token; // 连接数据库 User user = userService.getUserByName(userToken.getUsername()); // 没有此人,抛出 UnkownAccountException if (user == null) { return null; } //获取当前登录的用户对象 Subject subject = SecurityUtils.getSubject(); //存放到Session中 Session session = subject.getSession(); session.setAttribute("user", user); // realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可 String realmName = getName(); System.out.println(realmName); // 盐值。使用 username作为盐值,盐值必须是唯一的 ByteSource credentialsSalt = ByteSource.Util.bytes(user.getName()); /*三个参数: principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。 一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。 credentials: 传递的密码对象 realmName: 认证名(指定当前 Realm 的类名) */ // 密码认证, shiro自己做 // 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo return new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, realmName); }

2.3 在ShiroConfig中配置认证匹配器

即创建HashedCredentialsMatcher对象,并设置加密算法即可 注意: 如果你没有配置此类,你会发现尽管密码被加密,但你输入原始密码是等不进去的,因为这个HashedCredentialsMatcher类处理密码校验

package com.nsx.shiro.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * Created by NXS * 2020/9/1 17:05 */ @Configuration public class ShiroConfig { //shiroFilterFactoryBean 3 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); // 设置安全管理器 factoryBean.setSecurityManager(securityManager); /* anon:无需认证就可以访问 authc:必须认证了才能访问 user:必须拥有 记住我功能才能用 perms:拥有对某个资源的权限才能访问 role:拥有某个角色权限才能访问 */ //添加shiro的内置过滤器 拦截 Map<String, String> fiterMap = new LinkedHashMap<>(); // 设置哪些路径授予哪些权限 fiterMap.put("/user/add", "perms[user:add]"); fiterMap.put("/user/update", "perms[user:update]"); // /user/* 指的是路径 必须认证了才能访问 fiterMap.put("/user/*", "authc"); factoryBean.setFilterChainDefinitionMap(fiterMap); // 设置登录的请求 factoryBean.setLoginUrl("/toLogin"); // 跳转至未授权页面 factoryBean.setUnauthorizedUrl("/unauthorize"); return factoryBean; } //DefaultWebSecurityManager 2 @Bean(name = "securityManager") public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 关联userRealm securityManager.setRealm(userRealm); return securityManager; } //创建realm对象,需要自定义类 1 @Bean public UserRealm userRealm(@Qualifier("credentialsMatcher") HashedCredentialsMatcher matcher) { UserRealm userRealm = new UserRealm(); userRealm.setCredentialsMatcher(matcher); return userRealm; } // ShiroDialect 用来整合shiro和thymleaf @Bean public ShiroDialect getShiroDialect() { return new ShiroDialect(); } /** * 替换当前 Realm 的 credentialsMatcher 属性. * 直接使用 HashedCredentialsMatcher 对象, 并设置加密算法即可. * 密码校验规则HashedCredentialsMatcher * 这个类是为了对密码进行编码的 * 防止密码在数据库中明码表示,当然在登录认证的时候, * 这个类也负责对form里输入的密码进行编码 * 处理认证匹配处理器 */ @Bean("credentialsMatcher") public HashedCredentialsMatcher credentialsMatcher() { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); //设置加密算法 matcher.setHashAlgorithmName("MD5"); //设置加密次数 matcher.setHashIterations(1000); //是否存储为16进制 //将setStoredCredentialsHexEncoded设置为true,则需要使用toHex()进行转换成字符串,默认使用的是toBase64() matcher.setStoredCredentialsHexEncoded(true); return matcher; } }

重点加入什么,参见下面图片

3. 效果

分享视频啦啦啦啦啦啦

4. 总结:

最后总结一下Shiro的MD5加盐加密: 1)在doGetAuthenticationInfo方法返回值创建SimpleAuthenticationInfo对象的时候,需要使用 SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)构造器。 2)使用ByteSource.Util.bytes()来计算盐值 3)盐值需要唯一,一般使用随机字符串或者userid 4)使用new SimpleHash(hashAlgorithmName,crdentials,salt,hashIterations)来进行MD5盐值加密,并且hashIterations一定要与HashedCredentialsMatcher设置的加密次数相同 参考:http://blog.csdn.net/acmman/article/details/78585662

最新回复(0)