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;
}
具体的密码的比较~的设计明天再说~~