以前使用过的(来点印象)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); } }