shiro 再次通过源码谈谈登录的流程,之前理解的不是很清楚!

PrincipalCollection这个可以理解为当事人的信息!昨天在授权信息检查的时候,一直在传递这个信息,当时不是很理解,所以今天继续说说这个设计的意思到底是什么回事。以及登录流程之前疏忽的一些重要的信息,都统统的补齐。

subject.login(token);这个是今天的主要的角色,刚刚断点跟踪了一会才理解了到时是在做什么。

protected void login(String configFile, String username, String password) {
        //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
        Factory<org.apache.shiro.mgt.SecurityManager> factory =
                new IniSecurityManagerFactory(configFile);

        //2、得到SecurityManager实例 并绑定给SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        subject.login(token);
    }

(1)
DelegatingSubject这个是Subject的实现类,主要的成员函数如下,一个是当前的管家的引用,一个是PrincipalCollection 当事人的信息,具体代表什么意思细细的道来。

    protected PrincipalCollection principals;
    protected boolean authenticated;
    protected String host;
    protected Session session;
    protected boolean sessionCreationEnabled;
    protected transient SecurityManager securityManager;

进入到了Subject的实现类的login方法中,因为我们知道这些验证的信息都是来至于我们的管家的验证。

DelegatingSubject->
public void login(AuthenticationToken token)

这里写图片描述

(2)从这里开始,又开始交给管家去处理了,所以管家的继承图不能少啊!
这里写图片描述

DefaultSecurityManager.login

 public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
        AuthenticationInfo info;
        try {
         //这里就是给继承图的上面的处理认证信息
            info = authenticate(token);
        } catch (AuthenticationException ae) {
            try {
                onFailedLogin(token, ae, subject);
            } catch (Exception e) {
                if (log.isInfoEnabled()) {

                }
            }
            throw ae;
        }
        //根据当前的信息,创造一个Subject信息。
        Subject loggedIn = createSubject(token, info, subject);

        onSuccessfulLogin(token, info, loggedIn);

        return loggedIn;
    }

AuthenticatingSecurityManager
//成员变量里面的认证的门面哦~
private Authenticator authenticator;
//构造函数里面创建
this.authenticator = new ModularRealmAuthenticator();

AuthenticationInfo 作为返回信息,这里面试啥呢?还是不给力投路,一会看图片就知道了。认证之后,返回呢一个认证信息

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

ModularRealmAuthenticator的上面呢,还继承了一个主要处理验证成功和失败的监听通知的传送!

 ModularRealmAuthenticator->单个的验证realm在我们处理的时候其实这些信息已经全部的在了。通过配置读取的时候全部都设置好了。reaml处理数据的信息哦,无论是验证还是权限的信息都需要这个的处理哦!这里只配置了原始的默认的,只有一个Realm 
 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.getAuthenticationInfo(token); 这里返回的才是当前处理的重点,就是当前客户的信息。我们自己定义的时候都必须返回这AuthenticationInfo 的实现类才可以哦。

protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
        if (!realm.supports(token)) {

            throw new UnsupportedTokenException(msg);
        }
        AuthenticationInfo info = realm.getAuthenticationInfo(token);
        if (info == null) {

            throw new UnknownAccountException(msg);
        }
        return info;
    }

realm又一次进入到了realm的处理的世界,我们知道real的世界中呢,又是一个非常擅长使用template方法,继承结构的。所以还是再次来看看继承图,看这里有认证realm有授权的realm分工的很清楚啊,还有管理缓存的realm哦。这样的设计你说好不好呢?
这里写图片描述

AuthenticatingRealm认证的realm哦,进入到了这里,注意到这个是final方法,必须在这里验证哦~看看有没有缓存,没有缓存直接在进行下一步,查询信息

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

我们使用的是默认的IniRealm进行处理哦~,这个不能理解错误了。进入到了SimpleAccountRealm这个是IniRealm的父类的信息哦,这里的信息都是保存在内存中的,有一个users和roles的读写锁哦。具体的users是什么和roles这个Map里面的信息是什么一个是username->SimpleAccount 一个是roleName->SimpleRole


public class SimpleAccountRealm extends AuthorizingRealm {

    //TODO - complete JavaDoc
    protected final Map<String, SimpleAccount> users; //username-to-SimpleAccount
    protected final Map<String, SimpleRole> roles; //roleName-to-SimpleRole
    protected final ReadWriteLock USERS_LOCK;
    protected final ReadWriteLock ROLES_LOCK;

    public SimpleAccountRealm() {
        this.users = new LinkedHashMap<String, SimpleAccount>();
        this.roles = new LinkedHashMap<String, SimpleRole>();
        USERS_LOCK = new ReentrantReadWriteLock();
        ROLES_LOCK = new ReentrantReadWriteLock();
        //SimpleAccountRealms are memory-only realms - no need for an additional cache mechanism since we're
        //already as memory-efficient as we can be:
        setCachingEnabled(false);
    }

    public SimpleAccountRealm(String name) {
        this();
        setName(name);
    }

    protected SimpleAccount getUser(String username) {
        USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);
        } finally {
            USERS_LOCK.readLock().unlock();
        }
    }
    public void addAccount(String username, String password) {
        addAccount(username, password, (String[]) null);
    }

    public void addAccount(String username, String password, String... roles) {
        Set<String> roleNames = CollectionUtils.asSet(roles);
        SimpleAccount account = new SimpleAccount(username, password, getName(), roleNames, null);
        add(account);
    }

    protected String getUsername(SimpleAccount account) {
        return getUsername(account.getPrincipals());
    }

    protected String getUsername(PrincipalCollection principals) {
        return getAvailablePrincipal(principals).toString();
    }

    protected void add(SimpleAccount account) {
        String username = getUsername(account);
        USERS_LOCK.writeLock().lock();
        try {
            this.users.put(username, account);
        } finally {
            USERS_LOCK.writeLock().unlock();
        }
    }

    protected SimpleRole getRole(String rolename) {
        ROLES_LOCK.readLock().lock();
        try {
            return roles.get(rolename);
        } finally {
            ROLES_LOCK.readLock().unlock();
        }
    }

    public boolean roleExists(String name) {
        return getRole(name) != null;
    }

    public void addRole(String name) {
        add(new SimpleRole(name));
    }

    protected void add(SimpleRole role) {
        ROLES_LOCK.writeLock().lock();
        try {
            roles.put(role.getName(), role);
        } finally {
            ROLES_LOCK.writeLock().unlock();
        }
    }

    protected static Set<String> toSet(String delimited, String delimiter) {
        if (delimited == null || delimited.trim().equals("")) {
            return null;
        }

        Set<String> values = new HashSet<String>();
        String[] rolenamesArray = delimited.split(delimiter);
        for (String s : rolenamesArray) {
            String trimmed = s.trim();
            if (trimmed.length() > 0) {
                values.add(trimmed);
            }
        }

        return values;
    }

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        SimpleAccount account = getUser(upToken.getUsername());

        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = getUsername(principals);
        USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);
        } finally {
            USERS_LOCK.readLock().unlock();
        }
    }
}

这里先别管之前的验证了,先看看当前的user和roles到底是什么东西
这里写图片描述

这里的信息都是之前重Ini文件中获取过来,然后转换的哦!这个过程还是需要了解清楚的。
在来详细的看看user
这里写图片描述
在来详细看看roles对于昨天的学习,这个分段的处理信息Permission这个很好理解了哦~,当前角色对于的权限嘛是不是,这里看起来比user里面的要简单了很多啊,所以想看看roles怎么定义的!
这里写图片描述

SimpleRole 一个角色对应的所有的权限~Permission 这个Permission可以自己去处理,也不一定是系统默认定义的哦,原来那种是分段处理的哈哈,就像看到的这种样子。

/**
 * A simple representation of a security role that
 * has a name and a collection of permissions.  This object can be
 * used internally by Realms to maintain authorization state.
 *这个是被realms内部使用的,维护状态
 * @since 0.2
 */
public class SimpleRole implements Serializable {

    protected String name = null;
    protected Set<Permission> permissions;

    public SimpleRole() {
    }

    public SimpleRole(String name) {
        setName(name);
    }

    public SimpleRole(String name, Set<Permission> permissions) {
        setName(name);
        setPermissions(permissions);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Permission> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<Permission> permissions) {
        this.permissions = permissions;
    }

    public void add(Permission permission) {
        Set<Permission> permissions = getPermissions();
        if (permissions == null) {
            permissions = new LinkedHashSet<Permission>();
            setPermissions(permissions);
        }
        permissions.add(permission);
    }

    public void addAll(Collection<Permission> perms) {
        if (perms != null && !perms.isEmpty()) {
            Set<Permission> permissions = getPermissions();
            if (permissions == null) {
                permissions = new LinkedHashSet<Permission>(perms.size());
                setPermissions(permissions);
            }
            permissions.addAll(perms);
        }
    }

    public boolean isPermitted(Permission p) {
        Collection<Permission> perms = getPermissions();
        if (perms != null && !perms.isEmpty()) {
            for (Permission perm : perms) {
                if (perm.implies(p)) {
                    return true;
                }
            }
        }
        return false;
    }

    public int hashCode() {
        return (getName() != null ? getName().hashCode() : 0);
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (o instanceof SimpleRole) {
            SimpleRole sr = (SimpleRole) o;
            //only check name, since role names should be unique across an entire application:
            return (getName() != null ? getName().equals(sr.getName()) : sr.getName() == null);
        }
        return false;
    }

    public String toString() {
        return getName();
    }
}

SimpleAccount这个包含了很多的当前账号的信息,包含了用户名,凭证(密码)权限信息,我们看哈英文怎么说的。
contains principal (可以理解为账号)and credential (可以理解为密码)and authorization information(授权的信息) (roles and permissions) as instance variables。这个就是对于账号的信息哦
这里写图片描述

这个SimpleAccount还是比较大复杂的,保存了账号的凭证,密码,还有权限信息,其实这些在接口中都有体现,我们先看看继承的接口的信息把。
AuthenticationInfo 认证信息

public interface AuthenticationInfo extends Serializable {

    /**
     * 返回所有的principals和当前的Subject相关的,Each principal is an identifying piece of(唯一的标识)
     * information useful to the application such as a username, or user id, a given name, etc - anything useful
     * to the application to identify the current Subject.可以识别当前的Subject的
     * The returned PrincipalCollection should not contain any credentials used to verify principals, such
     * as passwords, private keys, etc.  Those should be instead returned by {@link #getCredentials() getCredentials()}.
     *不应该包含秘密的信息,也就是说Authentication(认证信息)在Subject为啥只有PrincipalCollection 而没得密码
     */
    PrincipalCollection getPrincipals();

    /**
     * Returns the credentials associated with the corresponding Subject.  A credential verifies one or more of the
     * {@link #getPrincipals() principals} associated with the Subject, such as a password or private key.  Credentials
     * are used by Shiro particularly during the authentication process to ensure that submitted credentials
     * during a login attempt match exactly the credentials here in the <code>AuthenticationInfo</code> instance.
     *这个就是密码啦!
     */
    Object getCredentials();

}

SaltedAuthenticationInfo 这个是加盐操作的接口,一般不直接通过密码就行比较,增加处理,盐通常由安全的伪随机数生成器生成,因此它们是有效的,盐值应安全存储在账户信息侧,以确保它与帐户的凭据一起维护。

public interface SaltedAuthenticationInfo extends AuthenticationInfo {

    /**
     * Returns the salt used to salt the account's credentials or {@code null} if no salt was used.
     *
     * @return the salt used to salt the account's credentials or {@code null} if no salt was used.
     */
    ByteSource getCredentialsSalt();
}

MergableAuthenticationInfo合并认证信息~一般不会用到吧!

public interface MergableAuthenticationInfo extends AuthenticationInfo {

    /**
     * Merges the given {@link AuthenticationInfo} into this instance.  The specific way
     * that the merge occurs is up to the implementation, but typically it involves combining
     * the principals and credentials together in this instance.  The <code>info</code> argument should
     * not be modified in any way.
     *
     * @param info the info that should be merged into this instance.
     */
    void merge(AuthenticationInfo info);

}

Account账号的信息包含认证的信息和授权的信息

public interface Account extends AuthenticationInfo, AuthorizationInfo {
一个是认证的信息,一个是授权的信息~哈哈写不写都可以
}

AuthorizationInfo 授权信息的集合

//授权信息
public interface AuthorizationInfo extends Serializable {

    /**
     * Returns the names of all roles assigned to a corresponding Subject.
     */
    Collection<String> getRoles();


    Collection<String> getStringPermissions();
    /**
     * Returns all type-safe {@link Permission Permission}s assigned to the corresponding Subject.  The permissions
     * returned from this method plus any returned from {@link #getStringPermissions() getStringPermissions()}
     * represent the total set of permissions.  The aggregate set is used to perform a permission authorization check.
     * 返回所有的权限的信息当前的用户拥有的
     * @return all type-safe {@link Permission Permission}s assigned to the corresponding Subject.
     */
    Collection<Permission> getObjectPermissions();
}

SimpleAccount好复杂啊!
下面的两个成员变量主要的实现了认证和授权这两个接口哦~,代理过去处理

 /**
     * The authentication information (principals and credentials) for this account.
     */
    private SimpleAuthenticationInfo authcInfo;

    /**
     * The authorization information for this account.
     */
    private SimpleAuthorizationInfo authzInfo;

SimpleAuthorizationInfo 这个比较简单,包含了角色信息和权限咯~~

public class SimpleAuthorizationInfo implements AuthorizationInfo {

    /**
     * The internal roles collection.
     */
    protected Set<String> roles;

    /**
     * Collection of all string-based permissions associated with the account.
     */
    protected Set<String> stringPermissions;

    /**
     * Collection of all object-based permissions associaed with the account.
     */
    protected Set<Permission> objectPermissions;

}

SimpleAuthenticationInfo 权限验证这个比较复杂啊~但是和还是一样的身份的,盐,密码。 PrincipalCollection 这个身份是一个重点,因为每一个Subject的成员变量中也拥有一个这样的变量。这里面包含了验证的realm,对应的用户的信息!
这里写图片描述

public class SimpleAuthenticationInfo implements MergableAuthenticationInfo, SaltedAuthenticationInfo {

    /**
     * The principals identifying the account associated with this AuthenticationInfo instance.
     */
    protected PrincipalCollection principals;
    /**
     * The credentials verifying the account principals.
     */
    protected Object credentials;

    /**
     * Any salt used in hashing the credentials.
     *
     * @since 1.1
     */
    protected ByteSource credentialsSalt;

    /**
     * Default no-argument constructor.
     */
    public SimpleAuthenticationInfo() {
    }

    /**
     * Constructor that takes in a single 'primary' principal of the account and its corresponding credentials,
     * associated with the specified realm.
     * <p/>
     * This is a convenience constructor and will construct a {@link PrincipalCollection PrincipalCollection} based
     * on the {@code principal} and {@code realmName} argument.
     *
     * @param principal   the 'primary' principal associated with the specified realm.
     * @param credentials the credentials that verify the given principal.
     * @param realmName   the realm from where the principal and credentials were acquired.
     */
    public SimpleAuthenticationInfo(Object principal, Object credentials, String realmName) {
        this.principals = new SimplePrincipalCollection(principal, realmName);
        this.credentials = credentials;
    }

    /**
     * Constructor that takes in a single 'primary' principal of the account, its corresponding hashed credentials,
     * the salt used to hash the credentials, and the name of the realm to associate with the principals.
     * <p/>
     * This is a convenience constructor and will construct a {@link PrincipalCollection PrincipalCollection} based
     * on the <code>principal</code> and <code>realmName</code> argument.
     *
     * @param principal         the 'primary' principal associated with the specified realm.
     * @param hashedCredentials the hashed credentials that verify the given principal.
     * @param credentialsSalt   the salt used when hashing the given hashedCredentials
     * @param realmName         the realm from where the principal and credentials were acquired.
     * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher
     * @since 1.1
     */
    public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) {
        this.principals = new SimplePrincipalCollection(principal, realmName);
        this.credentials = hashedCredentials;
        this.credentialsSalt = credentialsSalt;
    }

    /**
     * Constructor that takes in an account's identifying principal(s) and its corresponding credentials that verify
     * the principals.
     *
     * @param principals  a Realm's account's identifying principal(s)
     * @param credentials the accounts corresponding principals that verify the principals.
     */
    public SimpleAuthenticationInfo(PrincipalCollection principals, Object credentials) {
        this.principals = new SimplePrincipalCollection(principals);
        this.credentials = credentials;
    }

    /**
     * Constructor that takes in an account's identifying principal(s), hashed credentials used to verify the
     * principals, and the salt used when hashing the credentials.
     *
     * @param principals        a Realm's account's identifying principal(s)
     * @param hashedCredentials the hashed credentials that verify the principals.
     * @param credentialsSalt   the salt used when hashing the hashedCredentials.
     * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher
     * @since 1.1
     */
    public SimpleAuthenticationInfo(PrincipalCollection principals, Object hashedCredentials, ByteSource credentialsSalt) {
        this.principals = new SimplePrincipalCollection(principals);
        this.credentials = hashedCredentials;
        this.credentialsSalt = credentialsSalt;
    }
    public String toString() {
        return principals.toString();
    }

}

SimplePrincipalCollection继承了 PrincipalCollection
这里写图片描述

因为可能一个用户有很多的唯一标识符比如电话,邮箱等等~处理这个信息的时候做了个心眼SimplePrincipalCollection
SimplePrincipalCollection

  private Map<String, Set> realmPrincipals;
  一个是realm的名字,一个是对应当前的用户标识的集合。

SimpleAccountRealm主要的都知道了,那么我们返回来啦,继续讲解验证。
这里通过用户的名字获取到SimpleAccountRealm中 protected final Map< String, SimpleAccount> users; 保存的用户的信息,得到SimpleAccount的信息。
这里写图片描述

 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        SimpleAccount account = getUser(upToken.getUsername());

        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }

得到了这个信息之后,在返回验证realm中去检查当前的的AuthenticationToken 中的密码 和这个获取出来信息的密码一样不~一样就成功了。

AuthenticatingRealm

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

具体的密码的比较~的设计明天再说~~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值