本文主要是介绍Shiro-Realm,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
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 <code>Realm</code>.*/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<String, ?> buildInstances(Ini.Section section, Map<String, ?> defaults) {this.builder = new ReflectionBuilder(defaults);return this.builder.buildObjects(section);}//ReflectionBuilder 里面就是一个HashMap就是操作,把SecurityManager创建好的也放置在里面,创建的Realm也是放置在里面最后的时候在处理SecurityManager里面的Realms。。。
public ReflectionBuilder(Map<String, ?> defaults) {this.objects = CollectionUtils.isEmpty(defaults) ? new LinkedHashMap<String, Object>() : 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 instancesfor (Map.Entry<String, String> entry : instanceMap.entrySet()) {createNewInstance((Map<String, Object>) objects, entry.getKey(), entry.getValue());}// Set all propertiesfor (Map.Entry<String, String> entry : propertyMap.entrySet()) {applyProperty(entry.getKey(), entry.getValue(), objects);}
protected void createNewInstance(Map<String, Object> 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<Realm> 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 {@Overridepublic String getName() {return "myrealm1";}@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token}@Overridepublic 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<AuthenticationListener> listeners;public AbstractAuthenticator() {listeners = new ArrayList<AuthenticationListener>();}public void setAuthenticationListeners(Collection<AuthenticationListener> listeners) {if (listeners == null) {this.listeners = new ArrayList<AuthenticationListener>();} else {this.listeners = listeners;}}public Collection<AuthenticationListener> 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);
}
多种模式选择策越进行处理。具体的策越模式还没有进行了解~~
这篇关于Shiro-Realm的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!