shiro之Realm及相关对象 PrincipalCollection

Posted Firm陈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shiro之Realm及相关对象 PrincipalCollection相关的知识,希望对你有一定的参考价值。


之前使用过的(来点印象)

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

以上是关于shiro之Realm及相关对象 PrincipalCollection的主要内容,如果未能解决你的问题,请参考以下文章

shiro之Realm及相关对象AuthorizationInfo

第六章 Realm及相关对象——《跟我学Shiro》

shiro之 JdbcRealm及Authentication Strategy

shiro实战系列之Realm

shiro之自定义Realm之继承AuthorizingRealm

shiro入门 之 Realm