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

来源:互联网 发布:fanuc编程模拟软件 编辑:程序博客网 时间:2024/06/03 10:01

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

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

0 0