是五月呀!

shiro开发(四)Realm及相关对象

AuthenticationToken

AuthenticationToken用于收集用户提交的身份(如用户名)及凭据(如密码):

1
2
3
4
public interface AuthenticationToken extends Serializable {
Object getPrincipal(); //身份
Object getCredentials(); //凭据
}

扩展接口RememberMeAuthenticationToken提供了boolean isRememberMe()实现“记住我”的功能;
扩展接口HostAuthenticationToken提供了String getHost()方法用于获取用户“主机”的功能。

Shiro提供了一个直接拿来用的UsernamePasswordToken,用于实现用户名/密码Token组,另外其实现了RememberMeAuthenticationTokenHostAuthenticationToken,可以实现记住我及主机验证的支持。

我们可以实现自己的AuthenticationToken,例如想要统一账号登录,这个账号可能是平台唯一账号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyAuthenticationToken implements AuthenticationToken {
private String account; // 统一账号
public MyAuthenticationToken(String account) {
this.account = account;
}
@Override
public Object getPrincipal() {
return getAccount();
}
@Override
public Object getCredentials() {
return getAccount(); // 密码凭证也返回统一账号
}
public String getAccount() {
return this.account;
}
}

AuthenticationInfo

AuthenticationInfo有两个作用:

  1. 如果Realm是AuthenticatingRealm子类,则提供给AuthenticatingRealm内部使用的CredentialsMatcher进行凭据验证;
  2. 提供给SecurityManager来创建Subject(提供身份信息)。

说一下第一个功能。
一般我们在实现Realm时,会实现doGetAuthenticationInfo方法,获取认证信息:

1
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

然后AuthenticatingRealm内部会使用这个认证信息进行凭证校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
if (!cm.doCredentialsMatch(token, info)) {
//not successful - throw an exception to indicate this:
String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
"credentials during authentication. If you do not wish for credentials to be examined, you " +
"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}

我们可以实现自己的AuthenticationInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyAuthenticationInfo implements AuthenticationInfo {
private PrincipalCollection principals;
private String credentials;
public MyAuthenticationInfo(Object principal, String realmName) {
this.principals = new SimplePrincipalCollection(principal, credentials);
this.credentials = credentials;
}
@Override
public PrincipalCollection getPrincipals() {
return this.principals;
}
@Override
public Object getCredentials() {
return this.credentials;
}
}

其中PrincipalCollection用于聚合这些身份信息,一般用SimplePrincipalCollection就好。

当然,还要写一个对应的比对CredentialsMatcher,用来对MyAuthenticationInfo进行凭证校验:

1
2
3
4
5
6
7
8
9
10
public class MintCredentialsMatcher extends PasswordMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
if(token instanceof MyAuthenticationToken) {
return token.getCredentials().equals(info.getCredentials());
}
return super.doCredentialsMatch(token, info);
}
}

在MyRealm的doGetAuthenticationInfo方法中,返回一个MyAuthenticationInfo,将MyAuthenticationToken的account赋值给MyAuthenticationInfo的credentials:

1
return new MyAuthenticationInfo(user, myAuthenticationToken.getAccount());

这样在校验时就能成功了。

AuthorizationInfo

AuthorizationInfo包含了授权信息,当我们使用AuthorizingRealm时,如果身份验证成功,在进行授权时就通过doGetAuthorizationInfo方法获取角色/权限信息用于授权验证。
Shiro提供了一个实现SimpleAuthorizationInfo,大多数时候使用这个即可。

Subject

Subject是Shiro的核心对象,基本所有身份验证、授权都是通过Subject完成。

身份信息获取

1
2
Object getPrincipal(); //Primary Principal
PrincipalCollection getPrincipals(); // PrincipalCollection

身份验证

1
2
3
void login(AuthenticationToken token) throws AuthenticationException;
boolean isAuthenticated();
boolean isRemembered();

通过login登录,如果登录失败将抛出相应的AuthenticationException,如果登录成功调用isAuthenticated就会返回true,即已经通过身份验证;
如果isRemembered返回true,表示是通过记住我功能登录的而不是调用login方法登录的。
isAuthenticated/isRemembered是互斥的,即如果其中一个返回true,另一个返回false。

角色授权验证

1
2
3
4
5
6
boolean hasRole(String roleIdentifier);
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;

hasRole*进行角色验证,验证后返回true/false;而checkRole*验证失败时抛出AuthorizationException异常。

权限授权验证

1
2
3
4
5
6
7
8
9
10
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;

isPermitted*进行权限验证,验证后返回true/false;而checkPermission*验证失败时抛出AuthorizationException

会话

1
2
Session getSession(); //相当于getSession(true)
Session getSession(boolean create);

类似于Web中的会话。如果登录成功就相当于建立了会话,接着可以使用getSession获取;如果create=false如果没有会话将返回null,而create=true如果没有会话会强制创建一个。

退出

1
void logout();

参考:
第六章 Realm及相关对象——《跟我学Shiro》
Apache Shiro Reference Documentation