zoukankan      html  css  js  c++  java
  • Authentication源码解析

    1、获取当前的 Subject. 调用 SecurityUtils.getSubject();

    从当前线程的threadLocals属性中获取Subject对象

    SecurityUtils
    public static Subject getSubject() {
            Subject subject = ThreadContext.getSubject();
            if (subject == null) {
                subject = (new Subject.Builder()).buildSubject();
                ThreadContext.bind(subject);
            }
            return subject;
        }
    ThreadContext
    public static Subject getSubject() {
            return (Subject) get(SUBJECT_KEY);
        }
    
    public static Object get(Object key) {
            if (log.isTraceEnabled()) {
                String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
                log.trace(msg);
            }
    
            Object value = getValue(key);
            if ((value != null) && log.isTraceEnabled()) {
                String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
                        key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
                log.trace(msg);
            }
            return value;
        }
    
    private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
    private static Object getValue(Object key) {
            Map<Object, Object> perThreadResources = resources.get();
            return perThreadResources != null ? perThreadResources.get(key) : null;
        }
    ThreadLocal<T>
    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null)
                    return (T)e.value;
            }
            return setInitialValue();
        }

    2、Subject 的 login(AuthenticationToken) 

    登录验证成功后将Subject.authenticated置位true,登录后再登陆时,当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()

    DelegatingSubject
    public void login(AuthenticationToken token) throws AuthenticationException {
            clearRunAsIdentitiesInternal();
            Subject subject = securityManager.login(this, token);
            ......
    
            this.principals = principals;
            this.authenticated = true;

    AuthenticatingSecurityManager中调用authenticator.authenticate(),其中authenticator初始化是org.apache.shiro.authc.pam.ModularRealmAuthenticator.ModularRealmAuthenticator

    this.authenticator = new ModularRealmAuthenticator();
    public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
            return this.authenticator.authenticate(token);
        }

    ModularRealmAuthenticator中循环Realms判断

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

    那ModularRealmAuthenticator中循环Realms是何时赋值的呢?Spring创建DefaultSecurityManager对象及属性赋值之后,调用afterRealmsSet() 

    AuthenticatingSecurityManager
    protected void afterRealmsSet() {
            super.afterRealmsSet();
            if (this.authenticator instanceof ModularRealmAuthenticator) {
                ((ModularRealmAuthenticator) this.authenticator).setRealms(getRealms());
            }
        }

    下面以单Realm为例,进行doSingleRealmAuthentication解析。

     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()中主要实现了:

    1、调用Realm.doGetAuthenticationInfo(),从数据源中获取用户、密码及相应的密码处理

    2、进行数据源获取的密码和登录页面获取密码的比对

    public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            AuthenticationInfo info = getCachedAuthenticationInfo(token);
            if (info == null) {
                //otherwise not cached, perform the lookup:
                info = doGetAuthenticationInfo(token);//从数据源中获取用户、密码及相应的密码处理
                log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
                if (token != null && info != null) {
                    cacheAuthenticationInfoIfPossible(token, info);
                }
            } else {
                log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
            }
    
            if (info != null) {
                assertCredentialsMatch(token, info);//进行数据源获取的密码和登录页面获取密码的比对
            } else {
                log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
            }
            return info;
        }

    开发中一般继承Realm,重写doGetAuthenticationInfo。

    最后分析下assertCredentialsMatch方法。

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

    根据credentialsMatcher 属性来进行的密码的比对。在定义Realm时根据加密方式定义相应的CredentialsMatcher,默认为SimpleCredentialsMatcher

    <bean id="myRealm" class="org.tarena.shiro.realm.MyRealm">
            <property name="credentialsMatcher" >
                <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                    <property name="hashAlgorithmName" value="MD5"></property>
                    <property name="hashIterations" value="1024"></property>
                </bean>
            </property>
        </bean>
  • 相关阅读:
    运行pyzbar时报 Could not find module ‘libzbar-64.dll ‘or one of its dependenci的错误
    [Tips] pandas 如何根据index进行slice
    [Tips] pandas逐行遍历
    [Tips] Docker build
    [Tips] redis的key自动过期策略
    [Tips] No module named ipykernel错误
    [Tips] mac下anconda python环境中site-package的位置
    [Tips] pandas获取每月最后一天
    [Tips] docker的daemon.json设置私有镜像仓库
    [Tips] nginx端口转发
  • 原文地址:https://www.cnblogs.com/xiaoliangup/p/10459686.html
Copyright © 2011-2022 走看看