第六章 Realm及相关对象(四) PrincipalCollection


以前使用过的(来点印象)java

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。spa

MutablePrincipalCollection是一个可变的PrincipalCollection接口,即提供了以下可变方法:code

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。blog


示例:继承

1. 准备三个Realmtoken

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. MyRealm3ip

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)ci

[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);
	}
}