开头一段废话
加盐加密会使用户的密码受到相对安全的保护,比普通的MD5加密要好。用户数据暴露给了用户,因为有盐值的存在,也很难拿到用户的原始密码。即时拿到了,也会增加破解难度。
Shiro权限框架有加盐加密的实现,下面就简单描述下自己实现的过程。
盐值
我使用的盐值是32的随机字符串,盐值越长,保密性相对越好。不过,有看到其他开发者说24位的盐值是考虑加密速度等因素后比较合适的。
工具类
public class ShiroUtil {
/**
* 生成32的随机盐值
*/
public static String createSalt(){
return UUID.randomUUID().toString().replaceAll("-", "");
}
/**
* 加盐加密
* @param srcPwd 原始密码
* @param saltValue 盐值
*/
public static String salt(Object srcPwd, String saltValue){
return new SimpleHash("MD5", srcPwd, saltValue, 1024).toString();
}
}
方法作用见注释,此处不做说明。
后端加密
本人采用的方案是在后端加密。即在创建用户时,生成随机盐值,然后将加密后的密码存入数据库。如:
public class User {
/**
* ID
*/
private String id;
/**
* 登录用户
*/
@NotEmpty(message = "用户名:用户名不能为空")
private String username;
/**
* 登录密码
*/
@Length(min = 6, message = "密码:密码长度不能低于6位")
@NotEmpty(message = "密码:密码不能为空")
private String password;
/**
* 盐值
*/
private String saltValue;
/**
* 手机号
*/
private String mobile;
/**
* 昵称
*/
private String nickname;
/**
* 是否冻结
*/
private Integer isFrozen;
/**
* 创建时间
*/
private Date createTime;
public User() {
}
public User(String id, String username, String password, String saltValue, String mobile, String nickname,
Integer isFrozen, Date createTime) {
this.id = id;
this.username = username;
this.password = password;
this.saltValue = saltValue;
this.mobile = mobile;
this.nickname = nickname;
this.isFrozen = isFrozen;
this.createTime = createTime;
}
/**
* 创建新的用户
* @param username 用户名
* @param password 密码
* @param nickname 昵称
* @param mobile 手机号
*/
public static User createUser(String username, String password, String nickname, String mobile){
String saleValue = ShiroUtil.createSalt();
return new User(ShiroUtil.createSalt(), username, ShiroUtil.salt(password, ByteSource.Util.bytes(saleValue).toString()),
saleValue, mobile, nickname, 0, new Date());
}
//省略set/get方法
}
@NotEmpty等注解,采用的thymeleaf的表单校验,如果报错了,请删除该注解,或者在POM中引入依赖即可,如:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Shiro认证
用户加密完成后,在Shiro中认证时,加盐加密用户输入的密码,然后和库中的对比是否一致即可。如:
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//todo:获取用户的权限
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = this.userMapper.selectByPrimaryKey(token.getUsername());
if (user == null){
throw new AuthenticationException("用户不存在!");
}
//盐值
ByteSource salt = ByteSource.Util.bytes(user.getSaltValue());
String saltPassword = ShiroUtil.salt(token.getPassword(), salt.toString());
if (!user.getPassword().equals(saltPassword)){
throw new AuthenticationException("输入密码不正确!");
}
if (user.getIsFrozen() == 0){
throw new AuthenticationException("用户已冻结!");
}
//第4个参数是realm名称
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getPassword(), salt, getName());
}
}
网友评论