Realm:域,Shiro 从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看
成DataSource , 即安全数据源。如我们之前的ini 配置方式将使用
org.apache.shiro.realm.text.IniRealm。
public interface Realm {
/**
*返回一个唯一的Realm名字,这个用来标识
* @return the (application-unique) name assigned to this Realm
.
*/
String getName();
//判断此Realm是否支持此Token.不支持将不进行校验
boolean supports(AuthenticationToken token);
//根据Token获取认证信息
AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
}
[main]
#声明一个realm
myRealm1=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1
#指定securityManager的realms实现
securityManager.realms=$myRealm1
这里,我们来看一下,怎么把配置文件的信息初始化在securityManager中的realms中的,之前我们知道,初始化的时候,会把信息加载在Ini类中去,看看怎么初始化信息。之前我还没有看懂,断点跟着看懂了。
SecurityManager securityManager = factory.getInstance();从这句话跟着进入
AbstractFactory 工厂方法的模板创建的方式,留给子类去处理信息。
IniFactorySupport
之前这里说过,如果有配置文件和没有配置文件的两种处理方式
IniSecurityManagerFactory这里再次使用父类的模板方法
IniSecurityManagerFactory->createSecurityManager
从配置文件中的信息,main这个配置拿出来啦
之后创建默认的SecurityManager…
ReflectionBuilder 通过反射创建配置的信息的对象哦~~
private Map buildInstances(Ini.Section section, Map defaults) {
this.builder = new ReflectionBuilder(defaults);
return this.builder.buildObjects(section);
}
//ReflectionBuilder 里面就是一个HashMap就是操作,把SecurityManager创建好的也放置在里面,创建的Realm也是放置在里面最后的时候在处理SecurityManager里面的Realms。。。
public ReflectionBuilder(Map defaults) {
this.objects = CollectionUtils.isEmpty(defaults) ? new LinkedHashMap() : defaults;
}
然后将创建实例reaml 和设置属性的值
实例:myRealm1=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1
属性:securityManager.realms= myRealm1将创建实例,然后解析出来所有的实例,通过反射,放置在之前的object这个HashMap中,最后解析 myRealm1,去HashMap找到实例了,哈哈。
Map<String, String> instanceMap = new LinkedHashMap<String, String>();
Map<String, String> propertyMap = new LinkedHashMap<String, String>();
for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
if (entry.getKey().indexOf('.') < 0 || entry.getKey().endsWith(".class")) {
instanceMap.put(entry.getKey(), entry.getValue());
} else {
propertyMap.put(entry.getKey(), entry.getValue());
}
}
创建realm实例..通过反射,放置在HashMap中,之后我们就清楚了啊!创建成果啦按哈哈
// Create all instances
for (Map.Entry<String, String> entry : instanceMap.entrySet()) {
createNewInstance((Map<String, Object>) objects, entry.getKey(), entry.getValue());
}
// Set all properties
for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
applyProperty(entry.getKey(), entry.getValue(), objects);
}
protected void createNewInstance(Map objects, String name, String value) {
Object currentInstance = objects.get(name);
if (currentInstance != null) {
log.info("An instance with name '{}' already exists. " +
"Redefining this object as a new instance of type {}", name, value);
}
Object instance;//name with no property, assume right hand side of equals sign is the class name:
try {
//根据全限定名哦~~反射创建
instance = ClassUtils.newInstance(value);
if (instance instanceof Nameable) {
((Nameable) instance).setName(name);
}
} catch (Exception e) {
String msg = "Unable to instantiate class [" + value + "] for object named '" + name + "'. " +
"Please ensure you've specified the fully qualified class name correctly.";
throw new ConfigurationException(msg, e);
}
objects.put(name, instance);
}
之前我们看到过验证的流程,主要是我们没有涉及到Realm
1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;
2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
5、Authenticator 会把相应的token 传入Realm,从Realm 获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
下面也是一样的跟着进入看看设计
subject.login–>DelegatingSubject.login–>securityManager.login(this, token);一步步的代理进入,主要管理主要的业务逻辑,这样进入到了管家的login
这里我们可以看到管家的结构形式,慢慢的验证逻辑就会进入到具体的验证的函数中去
AuthenticatingSecurityManager
private Authenticator authenticator;
public AuthenticatingSecurityManager() {
super();
this.authenticator = new ModularRealmAuthenticator();
}
/**
* Delegates to the wrapped {@link org.apache.shiro.authc.Authenticator Authenticator} for authentication.
*/
//通过代理进入到具体的实现处理逻辑的类中去
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
return this.authenticator.authenticate(token);
}
AbstractAuthenticator->authenticate(AuthenticationToken token)中调用了子类的信息ModularRealmAuthenticator->doAuthenticate(AuthenticationToken authenticationToken)
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection realms = getRealms();
if (realms.size() == 1) {
return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
} else {
return doMultiRealmAuthentication(realms, authenticationToken);
}
}
然后调用相应的realm进行处理哦,这里是单个的情况!
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" +
token + "]. Please ensure that the appropriate Realm implementation is " +
"configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
}
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " +
"submitted AuthenticationToken [" + token + "].";
throw new UnknownAccountException(msg);
}
return info;
}
我们自定义中必须实现的函数就好了realm.getAuthenticationInfo(token);
public class MyRealm1 implements Realm {
@Override
public String getName() {
return "myrealm1";
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String)token.getPrincipal(); //得到用户名
String password = new String((char[])token.getCredentials()); //得到密码
if(!"zhang".equals(username)) {
throw new UnknownAccountException(); //如果用户名错误
}
if(!"123".equals(password)) {
throw new IncorrectCredentialsException(); //如果密码错误
}
//如果身份认证验证成功,返回一个AuthenticationInfo实现;
return new SimpleAuthenticationInfo(username, password, getName());
}
}
AbstractAuthenticator这里还有经典的设计模式监听,然后进行通知,名字忘了哈哈设计的非常的用心哦
public abstract class AbstractAuthenticator implements Authenticator, LogoutAware {
/**
* Any registered listeners that wish to know about things during the authentication process.
*/
private Collection listeners;
public AbstractAuthenticator() {
listeners = new ArrayList();
}
public void setAuthenticationListeners(Collection listeners) {
if (listeners == null) {
this.listeners = new ArrayList();
} else {
this.listeners = listeners;
}
}
public Collection getAuthenticationListeners() {
return this.listeners;
}
protected void notifySuccess(AuthenticationToken token, AuthenticationInfo info) {
for (AuthenticationListener listener : this.listeners) {
listener.onSuccess(token, info);
}
}
//消息还传送哦~~订阅模式
protected void notifyFailure(AuthenticationToken token, AuthenticationException ae) {
for (AuthenticationListener listener : this.listeners) {
listener.onFailure(token, ae);
}
}
protected void notifyLogout(PrincipalCollection principals) {
for (AuthenticationListener listener : this.listeners) {
listener.onLogout(principals);
}
}
public void onLogout(PrincipalCollection principals) {
notifyLogout(principals);
}
public final AuthenticationInfo authenticate(AuthenticationToken token)
throws AuthenticationException {
if (token == null) {
throw new IllegalArgumentException("Method null.");
}
log.trace("Authentication attempt received for token [{}]", token);
AuthenticationInfo info;
try {
info = doAuthenticate(token);
if (info == null) {
throw new AuthenticationException(msg);
}
} catch (Throwable t) {
AuthenticationException ae = null;
if (t instanceof AuthenticationException) {
ae = (AuthenticationException) t;
}
if (ae == null) {
ae = new AuthenticationException(msg, t);
}
try {
notifyFailure(token, ae);
} catch (Throwable t2) {
if (log.isWarnEnabled()) {
log.warn(msg, t2);
}
}
throw ae;
}
log.debug("Authentication successful for token [{}]. Returned account [{}]", token, info);
notifySuccess(token, info);
return info;
}
//模板方法
protected abstract AuthenticationInfo doAuthenticate(AuthenticationToken token)
throws AuthenticationException;
}
public interface AuthenticationListener {
void onSuccess(AuthenticationToken token, AuthenticationInfo info);
void onFailure(AuthenticationToken token, AuthenticationException ae);
void onLogout(PrincipalCollection principals);
}
多种模式选择策越进行处理。具体的策越模式还没有进行了解~~