一、Shiro的一些经验与rememberMe实现原理
Shiro的登录(Authorization)和验权(Authentication)。默认都是依据usernameUserName来做验证和授权的。
登录用的是UsernamePasswordTokenusernamepassword对,验权用的是PrincipalCollection身份集合(默认是username和Realm名称),有一个统一的入口就是Subject(表示一个人或其它什么的要登录进来的对象)。
关系是登陆时subject.login(token),然后登陆成功了subject里就有了subject.getPrincipal()。拿到此用户的username了。
不论什么地方都能够直接使用工具方法拿到subject: Subject subject = SecurityUtils.getSubject();
能够通过subject.isAuthenticated()为true能够推断当前用户已经登录过了;此时能够直接通过subject.getSession()去获取我们放入session的信息了。
假设subject.isRemembered()为true,则登录后,Shiro会将principal对象序列化为Byte[],再base64。加入到client的cookie里。默认过期时间为1年。用户下次訪问登录时,直接将cookie里的rememberMe数据拿出来,传到server端。Shiro就可以反序列化。还原出来原用户的身份信息。在浏览器的设置里能够查看这个base64的cookie值:
二、实现机构加username的验证
Shiro默认这一套机制都是依照username来做的。如今我们的系统里存在多个机构,不同机构能够有同样的username,那么我们怎么来实现基于机构+username的验证呢?
两个思路能够解决问题:
- 把机构+username拼接到一起来作为UsernamePasswordToken里的UserName
- 扩展UsernamePasswordToken与Principal的实现
这两个思路各有利弊。第一个比較简单粗暴,第二个比較规范通用。
因为我们是先做好基于UserName的实现以后再修改代码实现把机构扩展进去,所以第一种方式修改最小:
- 系统登录页面表单加上机构类型与机构名称选择,
- 后台把机构类型+机构id+username拼接成admin:1:3的形式,作为UsernamePasswordToken的Username,
- Realm实现使用自己定义的或者我写的这个ServiceRealm(仅仅有3个类),SQL改成依据机构id+username获取password
- 然后使用subject.login登录就可以实现基于机构+用户的验证。
- 登录后查看subject.getPrincipal已经是admin:1:3的形式了。
另外一种以后再分析。
三、如上改动UserName格式+rememberMe造成的一个小问题
假设浏览器之前使用了rememberMe选型以后安装上面的方式改动了UserName格式(比方admin:1:3)。则由于其cookie有效期为1年。每次再自己主动登录诗、从client提交上来的序列化的cookie数据里都是旧的数据。还原出来的principal还是老的格式(比方admin),造成了数据不一致,程序运行出错。
解决的方法:清空浏览器的cookie,或者直接强制client使用一次login登录就可以。