Realm
- doGetAuthenticationInfo()和getAuthenticationInfo()一样,都是做用户验证和返回身份凭证的。
- 从调用链上,getAuthorizationInfo()会调用doGetAuthorizationInfo(),所以大部分时候是重写后者。
AuthenticationToken
收集用户提交的身份信息(如用户名和凭据(如密码))的接口。
public interface AuthenticationToken extends Serializable { Object getPrincipal(); //身份 Object getCredentials(); //凭据 };
- 扩展接口RememberMeAuthenticationToken:提供boolean isRememberMe()实现记住我功能。
- 扩展接口HostAuthenticationToken:提供String getHost()获取用户主机。
- 内置实现类UsernamePasswordToken:仅保存用户名、密码,并实现了以上两个接口,可以实现记住我和主机验证的支持。
AuthenticationInfo
- 封装验证通过的身份信息,主要包括Object属性principal(一般存储用户名)和credentials(密码)。
- MergableAuthenticationInfo子接口:在多Realm时合并AuthenticationInfo,主要合并Principal,如果是其他信息如credentialsSalt,则会后合并进来的AuthenticationInfo覆盖。
- SaltedAuthenticationInfo子接口:比如HashedCredentialsMatcher,在验证时会判断AuthenticationInfo是否是SaltedAuthenticationInfo的子类,是则获取其盐。
- Account子接口:相当于我们之前的[users],SimpleAccount是其实现。在IniRealm、PropertiesRealm这种静态创建账号的场景中使用,它们继承了SimpleAccountRealm,其中就有API用于增删查改SimpleAccount。适用于账号不是特别多的情况。
- SimpleAuthenticationInfo:一般都是返回这个类型。
PincipalCollection
- Principal前缀:应该是上面AuthenticationInfo的属性principal。
- PincipalCollection:是一个身份集合,保存登录成功的用户的身份信息。因为我们可以在Shiro中同时配置多个Realm,所以身份信息就有多个。可以传给doGetAuthorizationInfo()方法为登录成功的用户授权。
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(); //判断是否为空
};
- 其中,getPrimaryPrincipal()时,如果只有一个Principal,那么直接返回即可,但若有多个则返回第一个,由于其底层由Map存储,所以第一个也就是任意一个。
- oneByType() 、 byType():根据凭据的类型返回对应的Principal。
- fromRealm():根据Realm名字返回对应的Principal。
- 因为此例中就一个Realm,所以直接调用getPrimaryPrincipal得到之前传入的用户名即可。
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//PrincipalCollection是一个身份集合,因为我们现在就一个Realm,
//所以直接调用getPrimaryPrincipal得到之前传入的用户名即可
String username=(String)principals.getPrimaryPrincipal();
...
}
- 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();//清空
}
- SimplePrincipalCollection:MutablePrincipalCollection唯一实现类。在继承了AbstractAuthenticationStrategy的验证策略中,afterAttemp()会调用SimplePrincipalCollection的merge()将多个Principal合并到一个PrincipalCollection中。
PrincipalCollection示例
- 准备三个Realm,命名分别为a,b,c,身份凭证只有细微差别。
public class MyRealm1 implements Realm {
@Override
public String getName() {
return "a"; //realm name 为 “a”
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
return new SimpleAuthenticationInfo(
"zhang", //身份 字符串类型
"123", //凭据
getName() //Realm Name
);
}
}
//和1完全一样,只是命名为b
public class MyRealm2 implements Realm {
@Override
public String getName() {
return "b"; //realm name 为 “b”
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
return new SimpleAuthenticationInfo(
"zhang", //身份 字符串类型
"123", //凭据
getName() //Realm Name
);
}
}
//除了命名不同,只是Principal类型为User,而不是简单的String
public class MyRealm3 implements Realm {
@Override
public String getName() {
return "c"; //realm name 为 “c”
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
User user=new User("zhang","123");
return new SimpleAuthenticationInfo(
user, //身份 User类型
"123", //凭据
getName() //Realm Name
);
}
}
- shiro-multirealm.ini
[main]
realm1=com.haien.shiroHelloWorld.principalTest.realm.MyRealm1
realm2=com.haien.shiroHelloWorld.principalTest.realm.MyRealm2
realm3=com.haien.shiroHelloWorld.principalTest.realm.MyRealm3
securityManager.realms=$realm1,$realm2,$realm3
- PrincipalCollectionTest:测试方法
public class PrincipalCollectionTest extends BaseTest {
@Test
public void testPrincipalCollection(){
login("classpath:config/shiro-multirealm.ini",
"zhang","123");
Subject subject=subject();
//获取Map中第一个Principal,即PrimaryPrincipal
Object primaryPrincipal1=subject.getPrincipal();
//获取PrincipalCollection
PrincipalCollection principalCollection=subject.getPrincipals();
//也是获取PrimaryPrincipal
Object primaryPrincipal2=principalCollection.getPrimaryPrincipal();
//获取所有身份验证成功的Realm名字
Set<String> realmNames=principalCollection.getRealmNames();
for(String realmName:realmNames)
System.out.println(realmName);
//将身份信息转换为Set/List(实际转换为List也是先转为Set)
List<Object> principals=principalCollection.asList();
/*返回集合包含两个String类、一个User类,但由于两个String类都是"zhang",
所以只只剩下一个,转为List结果也是一样*/
for(Object principal:principals)
System.out.println("set:"+principal);
//根据realm名字获取身份,因为realm名字可以重复,
//所以可能有多个身份,建议尽量不要重复
Collection<User> users=principalCollection.fromRealm("c");
for(User user:users)
System.out.println("c:user="+user.getUsername()+user.getPassword());
Collection<String> usernames=principalCollection.fromRealm("b");
for(String username:usernames)
System.out.println("b:username="+username);
}
}
- 代码实例:ideaProjects/shiroHelloWorld/PrincipalTest
AuthorizationInfo
- 封装权限信息,主要是doGetAuthorizationInfo()时封装授权信息然后返回的。
public interface AuthorizationInfo extends Serializable {
Collection<String> getRoles(); //获取角色字符串信息
Collection<String> getStringPermissions(); //获取权限字符串信息
Collection<Permission> getObjectPermissions(); //获取Permission对象信息
}
- SimpleAuthorizationInfo:实现类,大多数时候使用这个。主要增加了以下方法:
authorizationInfo.addRole("role1"); //添加角色到内部维护的role集合;
添加角色后调用MyRolePermissionResolver解析出权限
authorizationInfo.setRoles(Set<String> roles); //将内部维护的role集合设置为入参
authorizationInfo.addObjectPermission(new BitPermission("+user1+10")); //添加对象型权限
authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
authorizationInfo.addStringPermission("+user2+10"); //字符串型权限
authorizationInfo.addStringPermission("user2:*");
authorizationInfo.setStringPermissions(Set<String> permissions);
Subject
- Shiro核心对象,基本所有身份验证、授权都是通过Subject完成的。
//获取身份信息
Object getPrincipal(); //Primary Principal
PrincipalCollection getPrincipals(); // PrincipalCollection
//身份验证
void login(AuthenticationToken token) throws AuthenticationException; //调用各种方法;
登录失败抛AuthenticationException,成功则调用isAuthenticated()返回true
boolean isAuthenticated(); //与isRemembered()一个为true一个为false
boolean isRemembered(); //返回true表示是通过记住我登录到额而不是调用login方法
//角色验证
boolean hasRole(String roleIdentifier); //返回true或false表示成功与否
boolean[] hasRoles(List<String> roleIdentifiers);
boolean hasAllRoles(Collection<String> roleIdentifiers);
void checkRole(String roleIdentifier) throws AuthorizationException; //失败抛异常
void checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException;
void checkRoles(String... roleIdentifiers) throws AuthorizationException;
//权限验证
boolean isPermitted(String permission);
boolean isPermitted(Permission permission);
boolean[] isPermitted(String... permissions);
boolean[] isPermitted(List<Permission> permissions);
boolean isPermittedAll(String... permissions);
boolean isPermittedAll(Collection<Permission> permissions);
void checkPermission(String permission) throws AuthorizationException;
void checkPermission(Permission permission) throws AuthorizationException;
void checkPermissions(String... permissions) throws AuthorizationException;
void checkPermissions(Collection<Permission> permissions) throws AuthorizationException;
//会话(登录成功相当于建立了会话,然后调用getSession获取
Session getSession(); //相当于getSession(true)
Session getSession(boolean create); //当create=false,如果没有会话将返回null,
当create=true,没有也会强制创建一个
//退出
void logout();
//RunAs
void runAs(PrincipalCollection principals)
throws NullPointerException, IllegalStateException; //实现允许A作为B进行访问,
调用runAs(b)即可
boolean isRunAs(); //此时此方法返回true
PrincipalCollection getPreviousPrincipals(); //得到a的身份信息,
而getPrincipals()得到b的身份信息
PrincipalCollection releaseRunAs(); //不需要了RunAs则调用这个
//多线程
<V> V execute(Callable<V> callable) throws ExecutionException;
void execute(Runnable runnable);
<V> Callable<V> associateWith(Callable<V> callable);
Runnable associateWith(Runnable runnable);
- Subject的获取:一般不需要我们创建,直接通过SecurityUtils获取即可
public static Subject getSubject() {
Subject subject = ThreadContext.getSubject();
if (subject == null) {
subject = (new Subject.Builder()).buildSubject();
ThreadContext.bind(subject);
}
return subject;
}
- 首先查看当前线程是否绑定了Subject,没有则通过Subject.BUilder构建一个并绑定到线程返回。
- 如果想自定义Subject实例的创建,代码如下:
new Subject.Builder().principals(身份).authenticated(true/false).buildSubject()
- 然后自己绑定到线程即可。在new Subject.Builder()时如果没有传入SecurityManager,则自动调用SecurityUtils.getsecurityManager()获取一个默认实现类的对象。
- Subject一般用法
- 身份验证login()
- 授权hasRole*()/isPermitted*/checkRole*()/checkPermission*()
- 将相应的数据存储到会话Session
- 切换身份RunAs/多线程身份传播
- 退出
- 完整用户登录认证与授权示例:ideaProjects/shiroHelloWorld/chapter6、src/test/UserRealmTest为主测试