要解决的业务问题
第三方平台跳转至集成了shiro的平台时,仅通过用户名就可以实现登录集成了shiro的平台,可以理解为免登录Shiro系统。
因此要实现这样的操作,必须了解shiro的登录认证流程是如何的。
ps:以下展示的代码均为测试例子,大家可根据自己项目找到对应的逻辑处理的地方。前7步为逻辑分析,若是想看最终修改地方,则直接查看第8步即可
shiro的认证流程
1、生成token
将登陆的用户名密码等信息封装成一个UsernamePasswordToken实体类得到token信息。(此处可以通过集成org.apache.shiro.authc.UsernamePasswordToken类封装一些额外信息,但是最重要的就是用户名和密码。)
UsernamePasswordToken token = new UsernamePasswordToken(name, password);
2、调用登录认证
通过得到的token信息执行subject.login(token)方法,这个方法可能是代码中自己写的代码进行调用,也有可能是通过Shiro实现的fitler自动调用。
subject.login(token);
3、生成info
调用login方法后,会执行AuthorizingRealm的doGetAuthenticationInfo方法,这个方法需要我们在项目中通过集成AuthorizingRealm类重写doGetAuthenticationInfo。(这里我们从数据库用户的信息,然后返回一个SimpleAuthenticationInfo对象,注意,这里很重要,这里很重要,这里很重要)
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
System.out.println("执行认证逻辑");
//1、从数据库得到用户名和密码
//2、判断用户名
UsernamePasswordToken token2 = (UsernamePasswordToken) token;
User user = new User(token2.getUsername());
user = userService.selectUser(user);
if (user==null) {
return null; //shiro底层帮助抛出异常
}
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
4、用户名认证
第一步中,我们得到了封装了前端输入的用户信息的token,第三步中,我们得到了从数据库中查询得到了真实的用户信息。如果第三步没有从数据库中查询到用户,说明用户不存在,直接终止验证流程即可。如果查询得到了用户,则接下来就是进行密码的检验即可。
5、密码认证
密码的检验最后会由HashedCredentialsMatcher的doCredentialsMatch方法进行执行,如下
具体如何调用到这里,可以参考这篇博客:https://blog.csdn.net/caoyang0105/article/details/82769293
6、密码加密逻辑
由于数据库中保存的密码是经过shriro加密处理且不可逆的,因此前端传输过来的密码在与数据库对比前同样需要经过加密,具体的加密流程参考第六步的doCredentialsMatch方法为根方法,可逐个分析源码,我这里直接说结论,在不加盐的情况下,前端传过来的密码将会被shiro采用SHA-1加密方法,加密1024次得到新的密码,如下:
//需要导入的包
import org.apache.shiro.crypto.hash.SimpleHash;
new SimpleHash("SHA-1","",null,1024).toHex();
参数一:为加密的算法;参数为:为要加密的字符串(即前端传输过来的密码);参数三:盐;参数四:加密的次数。
7、默认密码
由第七步可知,如果前端传输的密码固定为""时,那么经过shrio转化后的密码为
b8f31e91d52285a5d39a9a53da819e35fb04edc6,
因此我们如果要实现用户的免密登录,则默认数据库的密码为b8f31e91d52285a5d39a9a53da819e35fb04edc6即可。
8、修改登录认证逻辑
因此我们在第三步生成数据库的SimpleAuthenticationInfo对象时,如果判断是免密码登录,则返回对象如下。
即在我们项目中通过集成AuthorizingRealm类重写doGetAuthenticationInfo的方法中,返回对象做如下改变,如果你前端的密码固定是""的话,此处的秘钥是确定的,如果不是,则参考第七步的代码生成秘钥
if (token.isMis()){
//这是免密登录的认证逻辑
return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()),
"b8f31e91d52285a5d39a9a53da819e35fb04edc6", getName());
}else {
//这是原先的登录
return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()),
user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());
}
以上是基于免密登录的需求修改的代码,自己调试得到的shiro认证逻辑,可能也不十分准确,尤其是最后的密码再次加密认证地方,源码中调用多个方法,精力有限,没有仔细深究。若是想要简单处理,就请参考第8步的代码,不要进行加盐处理。如果大家比较了解最后的密码加盐加密逻辑,还请多多指教。