会话存储/持久化: Shiro提供SessionDAO用于会话的CRUD,即DAO(Data Access Object)模式实现: Serializable create(Session session):如DefaultSessionManager在创建完session后会调用该方法;如保存到关系数据库/文件系统/NoSQL数据库;即可以实现会话的持久化;返回会话ID;主要此处返回的ID.equals(session.getId()); Session readSession(Serializable sessionId):根据会话ID获取会话 update(Session session)://更新会话;如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用 void delete(Session session):删除会话;当会话过期/会话停止(如用户退出时)会调用 Collection<Session> getActiveSessions():获取当前所有活跃用户,如果用户量多此方法影响性能
Subject subject = SecurityUtils.getSubject();
//得到一个身份集合,其包含了Realm验证成功的身份信息
PrincipalCollection principalCollection = subject.getPrincipals();
Assert.assertEquals(2, principalCollection.asList().size());
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();//获取的可以是字符串,也可以是一个user对象
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.findRoles(username));
authorizationInfo.setStringPermissions(userService.findPermissions(username));
return authorizationInfo;
}
PrincipalCollection是一个身份集合,因为我们可以在Shiro中同时配置多个Realm,所以呢身份信息可能就有多个;因此其提供了PrincipalCollection用于聚合这些身份信息:
public interface PrincipalCollection extends Iterable, Serializable {
Object getPrimaryPrincipal(); //得到主要的身份
<T> T oneByType(Class<T> type); //根据身份类型获取第一个
<T> Collection<T> byType(Class<T> type); //根据身份类型获取一组
List asList(); //转换为List
Set asSet(); //转换为Set
Collection fromRealm(String realmName); //根据Realm名字获取
Set<String> getRealmNames(); //获取所有身份验证通过的Realm名字
boolean isEmpty(); //判断是否为空
}
因为PrincipalCollection聚合了多个,此处最需要注意的是getPrimaryPrincipal,如果只有一个Principal 那么直接返回即可,如果有多个Principal,则返回第一个(因为内部使用Map存储,所以可以认为是返回任意一个);oneByType / byType根据凭据的类型返回相应的Principal;fromRealm 根据Realm 名字(每个Principal 都与一个Realm 关联)获取相应的Principal。
MutablePrincipalCollection是一个可变的PrincipalCollection接口,即提供了如下可变方法:
public interface MutablePrincipalCollection extends PrincipalCollection {
void add(Object principal, String realmName); //添加Realm-Principal的关联
void addAll(Collection principals, String realmName); //添加一组Realm-Principal的关联
void addAll(PrincipalCollection principals);//添加PrincipalCollection
void clear();//清空
}
目前Shiro只提供了一个实现SimplePrincipalCollection,还记得之前的AuthenticationStrategy实现嘛,用于在多Realm 时判断是否满足条件的,在大多数实现中(继承了
AbstractAuthenticationStrategy)afterAttempt 方法会进行AuthenticationInfo(实现了MergableAuthenticationInfo)的merge,比如SimpleAuthenticationInfo 会合并多个Principal为一个PrincipalCollection。
public class PrincialCollectionTest {
@Test
public void test(){
//因为Realm里没有进行验证,所以相当于每个Realm都身份验证成功了
login("classpath:chapter6/ini/shiro-multirealm.ini","zhang","123");
Subject subject = SecurityUtils.getSubject();
/*
* 我们可以直接调用subject.getPrincipal获取PrimaryPrincipal(即所谓的第一个);
* 或者通过subject.getPrincipals获取PrincipalCollection;然后通过其getPrimaryPrincipal获取PrimaryPrincipal。
*/
Object primaryPrincipal1 = subject.getPrincipal();
PrincipalCollection principalCollection = subject.getPrincipals();
/*
* 返回第一个,但内部是Map存储,所以可以理解为随机返回
*/
Object primaryPrincipal2 = principalCollection.getPrimaryPrincipal();
//但是因为多个Realm都返回了Principal,所以此处到底是哪个是不确定的
Assert.assertEquals(primaryPrincipal1, primaryPrincipal2);
/*
* 获取所有身份验证成功的Realm名字。返回a b c
*/
Set<String> realmNames = principalCollection.getRealmNames();
System.out.println(realmNames);
//==>[a, b, c]
/*
* 因为MyRealm1和MyRealm2返回的凭据都是zhang,所以排重了
*/
//asList和asSet的结果是一样的,因为将身份信息转换为Set/List,即使转换为List,也是先转换为Set再完成的。
Set<Object> principals = principalCollection.asSet();
System.out.println(principals);
//==>[zhang, User {id=null, username=zhang, password=123, salt=null, locked=false}]
/*
* 根据Realm名字获取身份,因为Realm名字可以重复,所以可能多个身份,建议Realm名字尽量不要重复。
*/
Collection<User> users = principalCollection.fromRealm("c");
System.out.println(users);
//==>[User {id=null, username=zhang, password=123, salt=null, locked=false}]
}
public void login(String configFile,String username,String password){
//1. 获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory(configFile);
//2. 得到SecurityManager实例,并绑定给SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3. 得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
subject.login(token);
}
}