之前使用过的(来点印象)
login("classpath:shiro-authenticator-all-success.ini");
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();
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。
示例:
1. 准备三个Realm
public class MyRealm1 implements Realm{
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
"zhang", //身份 字符串类型
"123", //凭据
getName() //Realm Name
);
return simpleAuthenticationInfo;
}
public String getName() {
return "a"; //realm name为"a"
}
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
}
2. MyRealm2
和MyRealm1完全一样,只是Realm名字为b
3. MyRealm3
public class MyRealm3 implements Realm{
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
User user = new User("zhang","123");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
user, //身份User类型
"123", //凭据
getName() //Realm Name
);
return simpleAuthenticationInfo;
}
public String getName() {
return "c"; //realm name为"c"
}
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
}
和MyRealm1同名,但返回的Principal是User类型
4. ini配置(shiro-multirealm.ini)
[main]
realm1=chapter6.realm.MyRealm1
realm2=chapter6.realm.MyRealm2
realm3=chapter6.realm.MyRealm3
securityManager.realms=$realm1,$realm2,$realm3
5. 测试用例
因为我们的Realm 中没有进行身份及凭据验证,所以相当于身份验证都是成功的,都将返回.
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);
}
}