Shiro的登录(Authorization)和验权(Authentication),默认都是根据用户名UserName来做验证和授权的。
登录用的是UsernamePasswordToken用户名密码对,验权用的是PrincipalCollection身份集合(默认是用户名和Realm名称),有一个统一的入口就是Subject(表示一个人或其他什么的要登录进来的对象)。
关系是登陆时subject.login(token),然后登陆成功了subject里就有了subject.getPrincipal(),拿到此用户的用户名了。
任何地方都可以直接使用工具方法拿到subject: Subject subject = SecurityUtils.getSubject();
可以通过subject.isAuthenticated()为true可以判断当前用户已经登录过了;此时可以直接通过subject.getSession()去获取我们放入session的信息了。
如果subject.isRemembered()为true,则登录后,Shiro会将principal对象序列化为Byte[],再base64,添加到客户端的cookie里,默认过期时间为1年。用户下次访问登录时,直接将cookie里的rememberMe数据拿出来,传到服务器端,Shiro即可反序列化,还原出来原用户的身份信息。在浏览器的设置里可以查看这个base64的cookie值:
Shiro默认这一套机制都是按照用户名来做的。现在我们的系统里存在多个机构,不同机构可以有相同的用户名,那么我们怎么来实现基于机构+用户名的验证呢?
两个思路可以解决这个问题:
这两个思路各有利弊,第一个比较简单粗暴,第二个比较规范通用。
由于我们是先做好基于UserName的实现以后再修改代码实现把机构扩展进去,所以第一种方式改动最小:
第二种以后再分析。
如果浏览器之前使用了rememberMe选型以后安装上面的方式修改了UserName格式(比如admin:1:3),则因为其cookie有效期为1年,每次再自动登录诗、从客户端提交上来的序列化的cookie数据里都是旧的数据,还原出来的principal还是老的格式(比如admin),造成了数据不一致,程序执行出错。
解决办法:清空浏览器的cookie,或者直接强制客户端使用一次login登录即可。