Shiro自定义验证器——使用国密sm3
Shiro自定义验证器——使用国密sm3
背景
在搞一个设计类的比赛,要求用国密,网上抄了抄,给Shiro改装一下,我本来Shiro验证用的是md5,因为sm3对标的是md5,所以现在就换成sm3
maven依赖
我用的是hutool的工具类,官网上说不需要导sm3那个依赖,但是我试了是不行的,所以还要导bcprov-jdk15on
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.0</version>
</dependency>
<!-- sm3-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<!-- Hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.21</version>
</dependency>
登录的原理就是比对密码是否相等,我这里是最简单的——比较加完salt和sm3进行hash后的密文是否和数据库中用户的密码密文相同
修改登录注册
//Json是我自定义的结果类
//用户注册,用hutool里的SM3和自建的盐工具加密,具体可以看点进去看源码
@Override
public Json register(String username, String password) {
if(this.getOne(new QueryWrapper<User>().eq("user_name",username))!=null){
return Json.fail(ResponseUtil.CREATE_CONFLICT,"用户名重复");
}
//处理业务调用dao
User user=new User();
user.setId(UUIDUtil.generateRandomUUID());
user.setUserName(username);
user.setPassword(password);
//1. 生成随机盐
String salt = SaltUtil.getSalt(8);
//2. 将生成的随机盐放入数据库
user.setSalt(salt);
//3. 明文密码进行sm3+salt+hash散列
SM3 sm3 = new SM3(salt.getBytes(StandardCharsets.UTF_8), 1024);
String digest = sm3.digest(user.getPassword()).toString();
user.setPassword(digest);
userMapper.insert(user);
return Json.success("注册成功");
}
//用户登录,这里和之前比没有区别,因为它们都是调用subject.login()方法,最后会进入realm里执行doGetAuthenticationInfo方法
@Override
public Json login(String username, String password) {
String USER_LOGIN_TYPE = LoginType.USER.toString();
Subject subject = SecurityUtils.getSubject(); //主体
UserToken token = new UserToken(username,password,USER_LOGIN_TYPE);
try {
// 会进入到doGetAuthenticationInfo,进行身份验证
subject.login(token);
} catch (UnknownAccountException e) {
// 账号不存在
return Json.fail(ResponseUtil.LOGIN_FAILURE);
} catch (IncorrectCredentialsException e) {
// 密码错误
return Json.fail(ResponseUtil.LOGIN_FAILURE);
}
// 向token中写入username
Map<String, String> claims = new HashMap<>();
claims.put("username", username);
// 回传token
Map<String, Object> map = new HashMap<>();
map.put("token", TokenUtil.generateToken(claims));
map.put("user", username);
return Json.result(ResponseUtil.LOGIN_SUCCESS,map);
}
修改自建Realm类
我这里只放认证相关,认证和原来比没有区别,重点是重写setCredentialsMatcher(设置认证的加密方式)
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取身份信息(用户名)
String principal = (String) authenticationToken.getPrincipal();
//根据数据库查询用户名信息
User user = userService
.getOne(new QueryWrapper<User>().eq("user_name", principal));
if (user == null) {
return null;
}
return new SimpleAuthenticationInfo(
principal, // 数据库的账号
user.getPassword(), // 加密后的密码
ByteSource.Util.bytes(user.getSalt()), // 加上盐值
getName());
}
//设置认证加密方式,登录密码校验的时候就会调用设置好的这个验证类里的验证方法,之前新建验证器里已经写好了
@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
SM3CredentialsMatcher sm3CredentialsMatcher = new SM3CredentialsMatcher();
super.setCredentialsMatcher(sm3CredentialsMatcher);
}
到这边就可以成功注册完,就可以登录了,两次生成的密文是一样的就登陆成功
新建验证器
首先新建自己的验证器类
@Component
public class SM3CredentialsMatcher extends SimpleCredentialsMatcher {
//登录的时候回调用这个方法进行密码比对
@Override
public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
SimpleAuthenticationInfo simpleAuthenticationInfo = (SimpleAuthenticationInfo) info;
//获取salt
byte[] salt = simpleAuthenticationInfo.getCredentialsSalt().getBytes();
SM3 sm3 = new SM3(salt,0, 1024);
//加密完的密码
Object tokenCredentials = sm3.digestHex(String.valueOf(token.getPassword()));
Object accountCredentials = getCredentials(info);
// 将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false
return equals(tokenCredentials, accountCredentials);
}
}
一个小问题
我使用Digest.digest()生成的byte[]输出是不一样的,但是调用Arrays.equals()他们是相同的,调用Digest.digestHex()以及把byte[]转16进制,它们又是相同的,不太明白为什么,,希望有大佬解答一下,感觉是一个字符编码的问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!