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 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类中去,看看怎么初始化信息。之前我还没有看懂,断点跟着看懂了。
Shiro-Realm_第1张图片
SecurityManager securityManager = factory.getInstance();从这句话跟着进入
AbstractFactory 工厂方法的模板创建的方式,留给子类去处理信息。
Shiro-Realm_第2张图片

IniFactorySupport
Shiro-Realm_第3张图片
之前这里说过,如果有配置文件和没有配置文件的两种处理方式
Shiro-Realm_第4张图片
IniSecurityManagerFactory这里再次使用父类的模板方法
Shiro-Realm_第5张图片

Shiro-Realm_第6张图片

IniSecurityManagerFactory->createSecurityManager
从配置文件中的信息,main这个配置拿出来啦
Shiro-Realm_第7张图片
之后创建默认的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;
    }

Shiro-Realm_第8张图片

然后将创建实例reaml 和设置属性的值
实例:myRealm1=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1
属性:securityManager.realms= myRealm1objectHashMap 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
Shiro-Realm_第9张图片
这里我们可以看到管家的结构形式,慢慢的验证逻辑就会进入到具体的验证的函数中去
Shiro-Realm_第10张图片

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);
    }

这里依然运用了模板方法哦!
Shiro-Realm_第11张图片

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);
}

多种模式选择策越进行处理。具体的策越模式还没有进行了解~~

你可能感兴趣的:(权限管理)