之前使用过
shiro
权限框架做过项目,但是一直没有机会系统 的了解一下shiro的认证机制
终于有机会好好研究一下这个轻量级安全框架🎄🎄🎁🎁
Shiro的身份认证
【1】基本流程
Shiro
把用户的数据封装成标识token
,token一般封装着用户名,密码等信息;- 使用
Subject
主体获取到封装着用户的数据的标识token; Subject
把标识token
交给SecurityManager
,在SecurityManager
安全中心,SecurityManager
把标识token
委托给认证器Authenticator
进行身份证。认证器的作用是一般用来指定如何验证,它规定本次认证用到那些Realm
- 认证器
Authenticator
将传入的标识token
,与数据源Realm
对比,验证token
是否合法
口说无凭,让我们代码上见 📢📢
为了操作简便,在这里我就不使用数据库,开始操作:
代码实现阶段🐳🐳
业务层代码🎊🎊
public interface UserService {
/**
* 按照用户名查找密码
* @param loginName
* @return
*/
String findPassWordByLoginName(String loginName);
}
//Service实现层
public class SecurityServiceImpl implements SecurityService {
@Override
public String findPassWordByLoginName(String loginName) {
return "123";
}
}
编写自定义CustomRealm 🎈🎈
public class CustomRealm extends AuthorizingRealm {
/**
* 授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取用户名
String loginName = (String) token.getPrincipal();
SecurityServiceImpl securityService = new SecurityServiceImpl();
String password = securityService.findPassWordByLoginName(loginName);
if ("".equals(password) || password == null){
throw new UnknownAccountException("账户不存在");
}
return new SimpleAuthenticationInfo(loginName,password,this.getName());
}
}
##声明自定义的Relam
customRealm=com.pdx.shiro.realm.CustomRealm
securityManager.realms=$customRealm
编写测试类 🎏🎏
@Test
public void shiroLogin(){
//导入权限的ini文件构建权限工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//工厂构建安全管理器
SecurityManager instance = factory.getInstance();
//使用SecurityUtils工具获取主体
SecurityUtils.setSecurityManager(instance);
Subject subject = SecurityUtils.getSubject();
//构建账号
UsernamePasswordToken token = new UsernamePasswordToken("jay", "123");
//登陆操作
subject.login(token);
System.out.println("是否登陆成功:"+subject.isAuthenticated());
}
测试结果⌛️⌛️
虽然代码运行成功,同样也返回了预期的结果,但是它的过程是如何实现的呢?
源码追踪阶段(开启疯狂Debug模式)🔎🔎
【1】通过debug
模式去进入subject.login(token)
下的默认实现类中发现, 正如认证流程图中显示的一样,subject
将用户的用户名密码委托给了SecurityManager
去做。接着往下看🔎
【2】通过这个方法的注释可以看出SecurityManager
又将用户的token
委托给内部认证组件Authenticator
去做。接着往下看🔎
【3】通过源码可以发现,其实SecurityManager
的内部组件都是相互推脱,认证组件Authenticator
又将传来的用户的token
交给Realm
去作比较。接着往下看🔎
【4】从图中可以看出,当前对象是Realm
类对象,而即将调用的方法就是第二个红色框框中的doGetAuthenticationInfo(token)
方法。当我看到这个方法的时候,总有一种似曾相识的感觉,疯狂思考,是不是在哪里见过呀,突然灵机乍现!!!猛拍大腿!这不就是我们在自定义Realm
中要重写的那个方法嘛!!!如果账号密码通过了,那么返回一个认证成功的info
凭证,如果认证失败,则抛出异常。接着往下看🔎
【5】接下来,就看到我自定义的Realm
类了,此类继承了AuthorizingRealm
,并实现了里面的doGetAuthenticationInfo()
方法,在这个方法里就可以进行一系列数据库的操作,并将查询到的数据库中存放的用户名和密码封装成一个AuthenticationInfo
对象返回。接着往下看🔎
【6】剩下的就很简单了,就是把输入的账号密码和数据库中的账号密码对比一下即可!如果没有报错,就说明此次登陆操作成功了!!!