史上最全shiro源码流程剖析(没有比这更全面的,不服来辩)
2021-03-27 18:27:34 4 举报
shiro源码流程全面剖析
作者其他创作
大纲/内容
flag =isSessionStorageEnabled(subject)
servletContext
this.startTimestamp = new Date()
模板方法,由子类实现
wac != null
getRememberedPrincipals()
cm!=null
enabled = true
Permission 接口
asSet( )
name
SecurityUtils.getSubject()
抛出异常,结束
ALREADY_FILTERED_SUFFIX = \".FILTERED\"
boolean isRemembered();
notifyStart(session);
如果发生异常
destroy( )
WebSecurityManager
CacheManager
field.setAccessible(true);
否
PrincipalCollection
是
将手动注册的filter添加给manager
有该权限,返回true
sessionValidationSchedulerEnabled
Collection<Object> getAttributeKeys()
protected Session retrieveSession(SessionKey sessionKey)
Authorizer (授权器)
SESSION
info = realm.getAuthenticationInfo(token);
获取本类的authenticationCache
根据“PRINCIPALS”为键到subjectContext中找
cache != null && token != null
scheduler = new ExecutorServiceSessionValidationScheduler(this);
static
AuthorizingRealm
this.service = Executors.newSingleThreadScheduledExecutor( )
builder.buildWebSubject( )
留意这个类的构造方法,这里面addDefaultFilters(false),没有添加默认的过滤器
final
RolePermissionResolver permissionRoleResolver;
继续抛出异常
继承
filterChainResolver
void setSecurityManager(SecurityManager securityManager);
Subject对象去创建SessionContext上下文对象
boolean
SessionKey key = getSessionKey(context);
chain == null?
Session
context.setAuthenticationInfo(info);
callable
info == null
授权info有3种获取权限的方法 (此处最好结合AuthorizationInfo接口一起看)一个是Permission类型的:Permission接口有判断是否还有权限的方法 boolean implies(Permission p)一个是stringPermission字符串类型的:可以被permissionResolver解析成Permission类型一个是roleString字符串类型的:可以被RolePermissionResolver解析成Permission类型
info == null ??
staticSecurityManagerEnabled
这里拿到的key就是传进去的principals
是,从备用中拿
使用securityManager的SessionManager属性获取Session
Iterable 接口
RequestPairSource 接口
回调所有的listener
是,返回false
使用securityManager的subjectDao属性,来保存subject
request
Field field = DelegatingSubject.class.getDeclaredField(\"principals\");
SubjectContext context = createSubjectContext();
this.session == null && create
参数:拦截的路径,过滤器的名字,指定的值
this.lastAccessTime = this.startTimestamp;
stopTimestamp
UUID.randomUUID().toString()
httpSession != null
token != null ? token.getPrincipal() : null;
mergeAuthenticationState(subject);
AbstractValidatingSessionManager 抽象类
Object key = getAuthorizationCacheKey(principals);
MapContext 类
cacheManager.getCache(cacheName)
Session session = wsc.resolveSession();
try
确保从key中能拿到servletRequest和ServletResponse,否则抛出异常
asList()
s != null
((ValidatingSession) session).validate()
CacheManagerAware 接口
单realm认证,直接返回
void delete(Session session);
PrincipalCollection principals = getPrincipals();
info = cache.get(key);
decryptionCipherKey
subject = SecurityUtils.getSubject()
contextAttribute
Date getLastAccessTime(SessionKey key);
String name = getName();String value = getValue();String comment = getComment();String domain = getDomain();String path = calculatePath(request);int maxAge = getMaxAge();int version = getVersion();boolean secure = isSecure();boolean httpOnly = isHttpOnly();
AuthenticatingSecurityManager
sessionId 相关
protected Session doCreateSession(SessionContext context)
观察者模式
返回
模板方法,没有实现
WebApplicationContextUtils.findWebApplicationContext(getServletContext())
boolean
HttpServletRequest request = WebUtils.getHttpRequest(sessionContext);
long getTimeout()
返回new SimpleCookie()
是,返回null
CredentialsMatcher credentialsMatcher;
host
Date getLastAccessTime();
String targetBeanName = getTargetBeanName();
sessionValidationScheduler
默认值1小时
context = ensureSecurityManager(context);
getPrincipals()
默认实现sessionKey.getSessionId();
info == null?
这里的鉴权方法,分两种:一个是Permission类型、一个是role字符串类型 1. Permission类型鉴权, permissionString需要被解析成Permission类型,在做判断; Permission类型直接做判断 2. role字符串类型:可以直接拿来判断是否有指定角色 如果存在RolePermissionResolver,则角色会被解析成Permission,来做鉴权,不存在就不会
boolean authorizationCachingEnabled;
Callable<V> associated = associateWith(callable);
String getHost();void setHost(String host);String resolveHost();
sessionIdCookieEnabled
NameableFilter 抽象类
如果拿到了session,那么根据指定的key再session里尝试拿一下PrincipalCollection用户身份
getCacheManager()
!authc
Session getSession(SessionKey key)
CollectionUtils.isEmpty(principals)
返回 s
否,scheduler不是null
返回info
先匹配到的filterChain立即return 返回
protected abstract Session doGetSession(SessionKey key)
void stop(SessionKey key)
String getValue();
以SimpleSessionFactory的实现为例
this.listeners
SUBJECT
填充builder属性
SecurityManager resolveSecurityManager();
new CookieRememberMeManager( )
判断是否有标记
DefaultWebSessionContext 类
this.sessionIdGenerator.generateId(session)
调用构造方法,使用了SubjectThreadState,不仅把subject给了,也把subject中的securityManager也给了SubjectThreadState
path
cache = getAvailableAuthenticationCache(token)
getSubjectFactory().createSubject(context)
boolean isSessionCreationEnabled();
下一个perm
final Object
start(SessionContext context)
void login(AuthenticationToken token)
webApplicationContext
EventBus
AUTHENTICATION_INFO
cookie == null
info = doAuthenticate(token);
String
以ServletContainerSessionManager实现为例
long
如果,authc还是false的话,再去看下subjectContext.resolveSession
((SimpleSession) session).setId(sessionId)
session不为null的话,将会被装饰
拿完授权信息后就可以判断了
rmm = getRememberMeManager();
target.call()
通知 ->认证器里的所有监听器AuthenticationListener成功
Collection<Permission>
void checkPermission(String permission)...............................
根据principals拿到缓存的key,key默认拿的就是principals
实现接口方法,默认实现:path作为key,config使用逗号隔开的数组,放入appliedPaths
DefaultSessionKey 类
Serializable getSessionId();
AccessControlFilter 抽象类
CollectionUtils.isEmpty(currentPrincipals) ??
建造者模式
attributes
Session start ( SessionContext context )
ServletRequest getServletRequest();
SUBJECT_KEY (常量)
AbstractSessionManager 抽象类
realms = getRealms()
info != null && cache != null
Filter 接口
将以上三种都解析成Permission对象,添加到Permission对象集合中
getAuthenticationInfo(AuthenticationToken token)
info = doGetAuthorizationInfo(principals);
Boolean sessionAuthc = session.getAttribute(AUTHENTICATED_SESSION_KEY);
AuthenticatingFilter 抽象类
Filter filter = (Filter) bean;
这里直接拿到Permission
WebSecurityManager 接口
static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = \".authorizationCache\";
String authenticationCacheName;
getCredentials( )
successUrl
WebSubject接口继承了RequestPairSource接口
currentPrincipals == null || currentPrincipals.isEmpty() ??
void
SessionsSecurityManager
否,表示拿到的perm是空的,没有任何权限.返回false
onExpiration(session)
Date
request.getAttribute(alreadyFilteredAttributeNam) != null
Object removeAttribute(Object key)
CachingRealm
authenticationCachingEnabled // 先看AuthenticatingRealm的成员变量authenticationCache&&isCachingEnabled // 拿父类CachingRealm的成员变量cachingEnabled
String getDomain();
DefaultWebSubjectContext
String requestURI = getPathWithinApplication(request);
boolean isSecure();
this.sessionManager.validateSessions();
sessionManager = getSessionManager()
boolean isPermitted(String permission);
模板方法,子类去实现
SubjectFactory subjectFactory;
listener.onStop(forNotification);
id
先从sessionContext中去根据“HOST”为键去找,找不到就从sessionContext里获取到request后,再使用request.getRemoteHost( )
先查缓存
认证token接口:一般是由客户端提供给shiro,从接口定义上来看, 1. getPrincipal()返回的是Object类型,应当理解为登录时客户端拿给shiro的一个客户端这边的身份,让shiro能够使用这个身份来对客户端的身份进行甄别,甄别后需要shiro根据这个token产生一个结果,这个结果就是AuthenticationInfo2. 密码也是Object类型,而AuthenticationInfo中也有获取Credentials方法,也是Object类型,所以shiro就可以根据认证token中的credentials和认证信息里的credentials比对,如果比对成功,那么认证信息里的PrincipalCollection就归这个客户端了
SessionIdGenerator
SubjectContext
通知rmm,然后继续抛出该异常
填充SecurityManager属性
1. 以“SECURITY_MANAGER”为键,到subjectContext对象中找2. 如果上面找不到的话,就去调用securityManager = SecurityUtils.getSecurityManager()
根据request,将web容器的过滤器链和shiro的过滤器给包装起来形成新的chain
从缓存中根据key拿到授权信息
public static final int DEFAULT_MAX_AGE = -1; public static final int DEFAULT_VERSION = -1; //These constants are protected on purpose so that the test case can use them protected static final String NAME_VALUE_DELIMITER = \"=\"; protected static final String ATTRIBUTE_DELIMITER = \"; \
SimpleSession 类
httpOnly
授权缓存中没有,则去查询,这是个模板方法,也是最核心的方法之一
安全管理器
this.timeout = 30
AuthenticationToken 接口
SecurityUtils
SESSION_CREATION_ENABLED
init(FilterConfig filterConfig)
currentPrincipals = (PrincipalCollection)field.get(subject);
DefaultSecurityManager
CredentialsMatcher cm = getCredentialsMatcher();
如果不支持,换下一个realm
\"Set-Cookie\"
1. 设置realm后,会主动调用afterRealmsSet(); 2. 如果realm实现了CacheManagerAware接口,那么把cacheManager设置给该realm3. 如果realm实现了EventBusAware接口,那么把eventBus设置给该realm
this.httpOnly = true;
PermissionResolver permissionResolver;
ShiroFilterFactoryBean
否,表示已经有了缓存或者关闭了缓存返回cache
Set<String>
SubjectContext接口
1. 默认直接 new DefaultSubjectContext对象;2. DefaultSubjectContext通过继承了MapContext,实现了SubjectContext接口;3.DefaultSubjectContext这个类里面持有着很多的static final的静态常量
boolean isPermittedAll(String... permissions);
Authorizer authorizer
String
exception != null
鉴权字符由权限解析器解析为Permission对象
flag = perm.implies(permission)
调用
如果mappedValue强转为String[]数组后,数组中有\"permissive\"字符串,则返回true,否则返回false
判断subjectContext对象中(MapContext),根据键“SECURITY_MANAGER”去找是否保存了securityManager
Collection<Realm> realms = getRealms();
以“PRINCIPALS”为键将principals放入SubjectContext中
授权缓存生效流程
Session s = retrieveSession(key);
securityManager
获取SessionValidationScheduler属性
securityManager == null
SubjectDAO subjectDAO;
AbstractNativeSessionManager 抽象类
String name;
WebSessionKey 类
PrincipalCollection 接口
AuthorizationFilter 抽象类
Set<Permission> permissions = new HashSet<Permission>();
根据“SERVLET_REQUEST”为键,到subjectContext对象中找
根据授权信息,返回授权信息中包含的Permission,而Permission代表权限
根据传过来的principals拿到AuthorizationInfo授权信息
void notifyStart(Session session)
将subject对象存入线程本地变量的map中
sessions
loginUrl
FilterChainManager filterChainManager = getFilterChainManager();
登录的目的就是为了设置这些属性到SubjectContext中,从而创建subject对象,并解析相关的数据到subject对象中
this.subjectDAO.save(subject)
<T> Collection<T>
void setTimeout(long maxIdleTimeInMillis)
Session
flag
Cookie 接口
Subject getSubject();
AuthenticationInfo info = getAuthenticationInfo();principals = info.getPrincipals();
int
sessionId == null
SessionDAO 接口
前面全都被catch住了,走clearnup的逻辑
使用暴力反射拿到DelegatingSubject的PrincipalCollection对象
clearCachedAuthorizationInfo(principals);
模板方法:默认返回false
实现接口
CacheManager cacheManager
创建过滤器链管理器
applyGlobalSessionTimeout(session);
SecurityManager securityManager = getSecurityManager()
perm.implies(permission)
securityManager = SecurityUtils.getSecurityManager()
defaultFilters = manager.getFilters()
boolean sessionEnabled = wsc.isSessionCreationEnabled();
newSubjectContextInstance() 方法
设置给subjectContext
onStop(s)
PrincipalCollection getPrincipals();
永远返回true
!scheduler.isEnabled()
void validate()
sessionManager instanceof WebSessionManager && ((WebSessionManager)sessionManager).isServletContainerSessions()
交给过滤器链执行
DefaultSessionManager 类
this.targetBeanName == null
授权信息接口:从接口定义上来看, 权限可以有字符串和Permission类型的, 角色只能是字符串
最后我们看到了,shiro使用了ProxiedFilterChain去包装了web容器的过滤器链,并且把自己的过滤器也给配置了进去,所以过滤操作就交给了ProxiedFilterChain了
AuthenticationStrategy strategy = getAuthenticationStrategy();
从授权Info中拿到权限字符串,通过PermissionResolver挨个解析成Permission对象
Subject对象的securityManager属性去根据SessionContext创建Session
如果PrincipalCollection是null的话,就没必要查了如果不是null的话,先去拿授权缓存
返回true
boolean isAuthenticated();
pathMatcher = new AntPathMatcher();
匹配token的credentials和info的credentials
1. 这个类是spring-web jar包提供的,它设计的目的就是为了让web容器中的filter把请求,转移到或者说代理到spring容器中的filter (bean)来,从而实现了web filter应用和spring容器的互通;完全理解这个类,需要从它继承的父类一步步去看,就能知道了2. WebApplicationContext(即web版的spring容器)保存在ServletContext中的键是contextAttribute;3. delegate就是在spring容器中的代理filter,由这个代理filter来执行真正的过滤;4. targetBeanName就是从spring容器中找到代理filter时,用的名字,即beanName;
void setComment(String comment);
Object config = this.appliedPaths.get(path);
ServletResponse servletResponse;
String[] nameConfigPair = toNameConfigPair(filterToken);
expired
(个数)
getRealmNames( )
把subjectContext中存的东西都放到一个新的DefaultWebSubjectContext对象里面去;subjectContext继承自MapContext,这里把subjectContext的MapContext都放到新的SubjectContext对象的MapContext中了
instance
SECURITY_MANAGER
SecurityManager securityManager = wsc.resolveSecurityManager();
void setMaxAge(int maxAge);
重点来了,这个chain是web容器的过滤器链
context.resolveHost();
唯一构造方法(无参):默认实现是new MemorySessionDAO();
AUTHENTICATED
principals = getRememberedIdentity(context);
SimpleCookie 类
1. 以“SESSION_CREATION_ENABLED”为键,到subjectContext对象中找2. 如果上面找不到的话,就是true
save(subject);
subject.isRunAs() && subject instanceof DelegatingSubject??
targetFilterLifecycle = false
SERVLET_RESPONSE
Serializable generateSessionId(Session session)
enableSessionValidation( )
SecurityUtils.getSubject();
boolean isAllow = subject.isPermittedAll(perms)
filterConfig
SecurityManager
可以看到使用的是SpringShiroFilter
SecurityManager securityManager = getSecurityManager();
session
Session session = createSession(context);
static final AtomicInteger INSTANCE_COUNT
Exception exception = existingEx;
代理的真正目标filter
Serializable
sessionFactory= getSessionFactory()
AuthenticationToken getAuthenticationToken();void setAuthenticationToken(AuthenticationToken token);
this.version = DEFAULT_VERSION;
fromRealm(String realmName)
boolean supports(AuthenticationToken token);
DelegatingSubject#execute(Callable callable),将过滤器的逻辑都封装到了callable中
isTargetFilterLifecycle()
final Callable<V>
通知securityManager的RememberMeManager
作为key
默认属性填充实现对象
ThreadContext
核心方法
static final String
抛出异常
afterSessionValidationEnabled()
super.doClearCache(principals);
Collection<String>
Session getSession(boolean create);
把当前的securityManager设置给subjectContext
eventBus
拿认证时放入的principalCollection或者是Run as
associated.call()
从授权Info中拿到角色字符串,通过RolePermissionResolver挨个解析成Permission对象
Class<? extends AuthenticationToken> authenticationTokenClass;
缓存认证信息info流程
delegate
如果这里的existing抛出的是UnauthenticatedException未认证异常,那么就吞掉这个异常,span style=\"font-size: inherit;\
获取到所有的realm
AdviceFilter 抽象类
设置subject上下文的SUBJECT
session != null
boolean isServletContainerSessions();
sessionFactory
boolean isAuthenticated();
默认实现
perms = resolvePermissions(info.getStringPermissions());
如果session中指定key存的existingPrincipals和currentPrincipals不一致,就更新一下session的这个key
this.interval > 0
clearRunAsIdentitiesInternal();
scheduler == null
先去拿AuthenticatingRealm的成员变量authenticationCache
Session s = doReadSession(sessionId)
flag == true ??
chainResolver.setFilterChainManager(manager);
int getVersion();
Serializable 接口
判断当前Permission对象是否包含传过来的这个Permission对象的权限
WebUtils.toHttp(response).sendError(401);
去到subject对象里面拿session属性,根据指定的RUN_AS_PRICINPALS_SESSION_KEY从session中拿值,只有这里拿到了值,才会走下面的逻辑给currentPrincipas赋值,否则,currentPrincipals就是个null
重写了preHandle方法
this.sessionDAO.create(session)
DelegatingSubject
listener.onExpiration(forNotification);
configured.proxy(original);
遍历默认的过滤器,挨个设置全局属性
rincipalCollection principals = context.resolvePrincipals();
SessionManager 接口
Date getStartTimestamp(SessionKey key);
boolean isValid(SessionKey key);
cacheManager
key默认取的是token的principal
void update(Session session)
byType(Class<T> type)
session instanceof ValidatingSession
根据“SUBJECT”为键,到subjectContext对象中找
Subject existingSubject = getSubject()
void runAs(PrincipalCollection principals)
ServletContainerSessionManager 类
servletRequest
context.setPrincipals(principals);
PatternMatcher
context.isSessionCreationEnabled();
拿授权缓存的逻辑与拿认证缓存的逻辑基本一致
session中指定的key有数据,但是currentPrincipals中没有PrincipalCollection,那就删除掉session中的数据
void setAuthenticated(boolean authc);
结束
context = resolvePrincipals(context);
String path = StringUtils.clean(getPath())
void applyGlobalSessionTimeout(Session session)
填充subjectContext属性
Object
唯一无参构造方法默认实现用的是:JavaUuidSessionIdGenerator
获取权限解析器,默认实现为WildcardPermissionResolver
给子类去用
也就是说DefaultFilterChainManager中保存了,拦截路径<——> NamedFilterList的Map
创建路径匹配过滤器链解析器
this.sessionManager.getSession(key)
String host = getHost(sessionContext);
maxAge
创建一个SubjectContext对象
threadState.bind();
AuthenticationInfo info = info = authenticate(token);
Filter
existing == null
perms != null && perms.length > 0
resolveRolePermissions(info.getRoles())
String name = getName();String value = DELETED_COOKIE_VALUE;String comment = null;String domain = getDomain();String path = calculatePath(request);int maxAge = 0; // 设置为 0int version = getVersion();boolean secure = isSecure();boolean httpOnly = false;
final String HOST = DefaultSessionContext.class.getName() + \".HOST\";
模板方法:默认返回true
threadState
默认实现:new DefaultSubjectFactory( )
调用realm对象的doGetAuthorizationInfo方法
SubjectCallable<V> 类
void setPath(String path);
SecurityManager亲自上阵
comment
scheduler = getSessionValidationScheduler();
principals
!isSessionCreationEnabled()
ServletResponse getServletResponse();
flag1 = isSessionValidationSchedulerEnabled()
否,session等于空,返回null
Session session = subject.getSession(false);
this.authenticator.authenticate(token)
String getPathWithinApplication(ServletRequest request)
key默认存的是principal,即当前用户身份
session == null
唯一构造方法(无参):默认实现是true
cache = getAuthenticationCache()
SecurityManager接口 (安全管理器)
void setValue(String value)
注册的filter:一个名字对应一个filter对象
ServletRequest request = getServletRequest();
流程到这里,说明使用subjectContext不能直接解析出Session,那么shiro会尝试使用sessionManager来获取Session
DefaultSecurityManager#createSubject(subjectContext)
perms != null && !perms.isEmpty()
线程池调度,注意scheduler实现了Runnable接口
否,没有缓存
targetBeanName
this.maxAge = DEFAULT_MAX_AGE;
protected abstract Serializable doCreate(Session session)
doValidate(session)
(只是个代理,具体的操作还是要委托给securityManager)
从认证器中获取认证策略
默认就返回principals作为key,不过这个方法是protected,可以被重写
ValidatingSessionManager 接口
开始遍历realms
Object key = getAuthenticationCacheKey(token);
返回结果
boolean authenticated = wsc.resolveAuthenticated();
webSecurityManager = getSecurityManager()
根据“SUBJECT”为键
context != null
protected abstract Session createSession(SessionContext context)
为什么要用反射去拿Subject的principals属性?首先看下getPrincipals()方法,它里面先去session里面拿RUN_AS_PRINCIPALS_SESSION_KEY,然后再去拿的subject的principals属性。所以这里使用反射的话,应该是不希望去拿session的run as
返回false
CookieRememberMeManager
doCreateSession( context )
sessionDAO.readSession(sessionId)
AUTHENTICATION_TOKEN
RedisSessionDAO 接口
session.setTimeout(getGlobalSessionTimeout());
sessionEnabled
List
这一步不用多说,强转一下
LogoutAware 接口
sessionCreationEnabled
chain = new SimpleNamedFilterList(chainName);
getSecurityManager
1. 创建一个shiro的HttpServletSession,实现了shiro自定义的的Session接口,具体的实现则是交给了HttpSession2. 把host以“HOST_SESSION_KEY”为键存入到httpSession变量中
cache != null
existingSubject != null
CacheManager cacheManager;
chain.add(filter);
这里的session是Shiro的Session接口实例,意为:把java.servlet的HttpSession包装起来,用来实现shiro的Session的接口 host 放在session里面
this.enabled = true;
long getGlobalSessionTimeout()
抛出异常结束
此时是在AbstractShiroFilter中
先将对象绑定到本地线程
this.delegate == null
是,则有缓存
感兴趣的话,可以去看下CookieRemeberMeManager的具体实现
isEnabled()默认返回true,shouldNotFilter默认返回false
sessionIdCookie
FactoryBean 接口
Subject
boolean rememberMe = isRememberMe(request);
getPrimaryPrincipal( )
WebSubject.Builder 类
PrincipalCollection resolvePrincipals();
...
PrincipalCollection principals = wsc.resolvePrincipals();
public SimpleCookie()
实现
context.resolvePrincipals();
wsc.setServletRequest(this.servletRequest)
cache = getAuthenticationCacheLazy();
Session readSession(Serializable sessionId)
AbstractShiroFilter createInstance()
this.securityManager.createSubject(this.subjectContext)
CachingSecurityManager
timeout
跟最开始创建subject的流程一样
拿了一下authenticationCache
session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
EventBus eventBus
response
RememberMeManager rememberMeManager
根据key移除授权缓存info
subject.login(token);
使用StoppingAwareProxiedSession来对session对象进行装饰,并且把它传给了当前的subject对象
return subject.isAuthenticated()
context.setAuthenticated(true);
enableSessionValidationIfNecessary()
Object getAttribute(Object key)
RememberMeManager
Cookie
Session resolveSession();
AbstractSessionDAO 抽象类
listener.onStart(session);
createSession(context)
context = ensureSecurityManager(context);
SECURITY_MANAGER_KEY (常量)
this.appliedPaths == null || this.appliedPaths.isEmpty()
sessionFactory.createSession(context)
protected abstract Session doReadSession(Serializable sessionId);
AuthenticationInfo 接口
Authenticator (认证器)
String getName();
doCall(this.callable)
CachingSessionDAO 接口
Subject subject = doCreateSubject(context);
final String SERVLET_REQUEST = DefaultWebSessionContext.class.getName() + \".SERVLET_REQUEST\";
void setDomain(String domain);
StringUtils.hasText(host)
Collection<Permission> getPermissions(AuthorizationInfo info)
Session forNotification = beforeInvalidNotification(session);
使用过滤器链解析器,并把原始的chain包装成ProxiedFilterChain实例
serializer
void initFilterBean()
boolean isHttpOnly();
onChange(session);
调用AccessConrolFilter里的方法
mergePrincipals(subject);
token != null && info != null
final ThreadState
public SimpleCookie(Cookie cookie)
根据给定的PrincipalCollection,返回授权信息
SessionContext sessionContext = createSessionContext();
说明身份跟获取这个身份时的realm相关,即这个身份是由哪个Realm给的
FilterConfig
授权缓存存储流程
flag1&&flag2
Serializable getId();
Initializable 接口
info = getCachedAuthenticationInfo(AuthenticationToken token)
return new SimpleSession(host);
Subject
获取subject对象的ServletResponse属性
context.resolveSecurityManager();
WebSessionManager 接口
这里用的RolePermissionResolver
SERVLET_REQUEST
SessionDAO
1. 在构造方法里 默认new 创建ModularRealmAuthorizer,设置给authorizer对象2. 重写了afterRealmsSet()方法,如果授权器是ModularRealmAuthorizer的话,将realm添加给authenticator对象3. 极其重要的检查权限的方法:isPermittedAll、checkPermission、hasRole、checkRole,都是通过委托给authorizer完成的
this.interval默认1小时
Collection<Permission> perms = getPermissions(info);
getSubject
boolean hasAllRoles(Collection<String> roleIdentifiers);
返回securityManager
authc == null
assertRealmsConfigured()
从过滤器链解析器中获取过滤器链管理器,你要解析嘛,你里面肯定要有东西的嘛,这个东西就是过滤器链管理器来维护的
默认使用ImmutableProxiedSession包装了session
Date getStartTimestamp();
Session session = lookupSession(key);
key == null
PathConfigProcessor 接口
final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
设置subject上下文的AUTHENTICATION_INFO
final String SERVLET_RESPONSE = DefaultWebSessionContext.class.getName() + \".SERVLET_RESPONSE\";
false
抛出NPE异常
boolean authenticationCachingEnabled;
void doClearCache(PrincipalCollection principals)
SecurityManager (备用)
获取到从realm里面拿到的授权信息
密码匹配流程
Session session = this.securityManager.start(sessionContext);
ServletRequest servletRequest
new DefaultWebSubjectFactory()
确保realm配置了,不为空
successUrl = \"/\"
host != null
subject是否为空
AUTHENTICATED_SESSION_KEY
void setHttpOnly(boolean httpOnly);
返回此对象
scheduler = createSessionValidationScheduler();
ServletContextSupport
authc = sessionAuthc != null && sessionAuthc;
int getMaxAge();
!CollectionUtils.isEmpty(existingPrincipals) ??
String getHost(SessionKey key);
下一个realm
DefaultSubjectContext
ThreadContext.bind(this.subject);
如果sessionId是null,将抛出异常
模板方法,空实现
isPermitted、isPermittedAll、checkPermission、checkPermissions、hasRole、hasRoles
doGetSession(key)
根据名字去拿filter的List,这个configured就是根据request匹配到的所有过滤器的集合
!currentPrincipals.equals(existingPrincipals) ??
使用构建者,创建Subject对象
先从ThreadContext中获取Subject
!requestPath.startsWith(path)
此时表示没有缓存,并且需要创建缓存
从授权Info中直接拿到Permission对象
获取本类的authenticationCacheName作为cacheName,到父类cachingRealm中的cacheManager中去拿Cache
boolean isValid()
Permission p = Resolver.resolvePermission(permission);
boolean isPermitted(Permission permission);
调用realm获取认证信息的方法
requestPath.length() == cookiePath.length()|| cookiePath.charAt(cookiePath.length() - 1) == '/'|| requestPath.charAt(cookiePath.length()) == '/'
\
通知 ->认证器里的所有监听器AuthenticationListener失败
this.session = decorate(session);
createSubject(context)
getStringPermissions( )
Object getAvailablePrincipal(PrincipalCollection principals)
filters
globalSessionTimeout
以“AUTHENTICATION_INFO”为键,到subjectContext中获取AuthenticationInfo
request = ((WebSubject) existing).getServletRequest();
String getComment();
先获取到授权缓存,拿到成员变量authorizationCache
如果filter实现了PathConfigProcessor接口,那么filter的appliedPaths将会被设置
OncePerRequestFilter 抽象类
void logout();
Session session = subject.getSession(false)
Authenticator authenticator
把sessionManager设置给了scheduler
返回 isAllow
如果s是null,将抛出异常
String[] perms = (String[]) mappedValue;
SessionValidationScheduler
SecurityManager为null
从PrincipalCollection对象中,能够从身份集合中获取到一个首选的Principal
delegate.init(getFilterConfig())
交给过滤器处理,并且当该过滤器执行完毕后,清除这个标记
类似
existingSubject对象的Session属性,create是传过来的false
PrincipalCollection releaseRunAs();
Collection<Permission> perms = info.getObjectPermissions();
WebSecurityManager根据subjectContext获取Subject对象;其实这里见名知义:subjectContext意为subject的上下文,而由securityManager根据subject的上下文对象,创建出Subject对象是很合理的
将securityManager对象绑定给了ThreadContext了
Nameable 接口
NamedFilterList chain = getChain(chainName);
getName()+\".Filtered\"作为标记
SessionKey 接口
getRoles( )
boolean isRunAs();
SecurityUtils.getSecurityManager()
WebSessionContext 接口
Serializable create(Session session)
value
subject.getPrincipal() == null
unauthorizedUrl
则循环遍历appliedPaths,找到第一个匹配的(即满足下面条件的),若没有找到,则返回true
this.delegate = initDelegate(wac);
scheduler.enableSessionValidation();
Collection<Session> getActiveSessions();
Session session = resolveSession();
void setGlobalSessionTimeout(long globalSessionTimeout)
void checkRole(String roleIdentifier)...............................
WebSessionContext wsc = new DefaultWebSessionContext();
context.resolveSession();
PathMatchingFilter 抽象类
void setSessionCreationEnabled(boolean enabled);boolean resolveAuthenticated();AuthenticationInfo getAuthenticationInfo();void setAuthenticationInfo(AuthenticationInfo info);
AuthenticationInfo info = getAuthenticationInfo();
WebSubjectContext wsc = (WebSubjectContext) context;
sessionContext 接口
AuthorizingSecurityManager
deleteInvalidSessions
HttpSession httpSession = request.getSession(false);
门面模式
<T> T
默认实现: new DefaultSubjectDAO( )
PermissionResolver resolver = getPermissionResolver()
public Object getObject( )
filterChains
public SimpleSession()
所以这里以currentPrincipals起决定导向, 如果currentPrincipals为空,那会话里面也不能有PRINCIPALS_SESSION_KEY如果currentPrincipals为空,那就更新会话里的PRINCIPALS_SESSION_KEY
cookie.getValue()
context.setSession(session)
SubjectContext context = copy(subjectContext);
Session getSession();
从DefaultFilterChainManager根据名字,也就是url拿到过滤器链,默认实现是SimpleNamedFilterList
循环遍历chainNames
cipherService
cache!=null
关键点:delegatingFilterProxy把请求和响应交给 了delegate去做真正的过滤,而delegate 恰恰是从web版的spring容器中拿到的, 于是,这样就完成了过度
将安全管理器设置给它
boolean hasRole(String roleIdentifier)
返回 Subject
wac = findWebApplicationContext();
authenticated
resolveSession的逻辑1. 先根据“SESSION”为键到SubejctContext对象中找Session,找到了就返回; 2. 如果没找到,那就根据“SUBJECT”为键到SubejctContext对象中找Subject;3. 如果找到了Subject对象,那就调用session = existingSubject.getSession(false)返回session
获取subject对象的ServletRequest属性
如果没拿到Session,就去找session管理器去拿一个Session
FilterChainResolver resolver = getFilterChainResolver();
chainNames = filterChainManager.getChainNames()
notifyStop(s);
SessionManager (会话管理器)
continueChain
String name = getName()
1. 优先取成员变量的name属性作为该filter的名字,次之再取filterConfig中的filterName
默认返回true,可被重写
获取sessionFactory属性
1. 在构造方法里 默认new 创建ModularRealmAuthenticator,设置给authenticator对象2. 重写了afterRealmsSet()方法,如果认证器是ModularRealmAuthenticator的话,将realm添加给authenticator对象3. 极其重要的一个方法【AuthenticationInfo authenticate(AuthenticationToken token)】, 根据token返回认证信息,是委托给认证器完成的,this.authenticator.authenticate(token)
authenticate( authenticationToken)
implies(Permission p)
此方法默认返回true,可被重写
获取当前SimpleCookie对象的path属性,是否匹配请求对象request的uri
Subject.Builder
根据SessionKey到SessionManager中去获取Session
认证缓存开关
否,表示授权缓存中有授权info
session = context.resolveSession()
void setName(String name);
request == null
DefaultFilterChainManager manager = new DefaultFilterChainManager();
void setSessionId(Serializable sessionId);
String getPath();
startTimestamp
void setHost(String host);String getHost();
domain
session = subject.getSession();
跳过没有实现Authorizer接口的realm
Serializable getSessionId();void setSessionId(Serializable sessionId);
boolean[] isPermitted(List<Permission> permissions);
authc = info != null;
flag = !isHttpSessions()
把过滤器链管理器设置给过滤器链解析器
这里,仍然是securityManager的调用方法处;根据subjectContext获取SessionKey
boolean isRememberMe(ServletRequest request)
这一步执行完,subjectContext中一定会有securityManager,如果是web环境,上面还会传过来request和response对象在subjectContext中
String host = wsc.resolveHost();
String host = getHost(request);
return loggedIn;
void validateSessions();
是否要开启使用缓存
获取sessionValidationSchedulerEnabled属性
BeanPostProcessor 接口
getSessionStorageEvaluator().isSessionStorageEnabled(subject)
AuthenticationFilter 抽象类
获取当前SimpleCookie对象的path属性,并清理前后空格
private Session lookupSession(SessionKey key)
context = resolveSession(context);
Object getPrincipal();
把sessionValidationInterval属性设置给了scheduler
this.callable.call()
永远返回false
StringUtils.hasText(getUnauthorizedUrl())
void setSubject(Subject subject);
Session 接口
securityManager交给授权器来鉴权
realm.supports(token) ??
遍历realm
this.listeners
是,抛异常,然后通知
遍历perms
getPrincipal( )
verifySessionId(sessionId)
session是null,authc仍然是false
void setSession(Session session);
遍历request中的cookie,找到名字叫name的cookie,有的话,就返回
循环遍历perms,循环完了还是false的话,那就是没有权限了
getSecurityManager( )就是拿的ShiroFilterFactoryBean中设置的SecurityManager
Subject接口
获取请求的uri
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
根据subjectContext去创建Subject对象
Serializable
void setVersion(int version);
NativeSessionManager 接口
session == null ??
NamedFilterList configured = getChain(chainName)
DefaultWebSessionManager 类
void touch(SessionKey key)
AuthorizationInfo info = getAuthorizationInfo(principals);
authenticationCache == null
void touch()
ServletResponse response = wsc.resolveServletResponse();
设置subject上下文的AUTHENTICATED
否,authc获取到了值
lastAccessTime
sessionValidationInterval
createSubject(context);
final ServletResponse
onChange(session)
boolean isPermissive(Object mappedValue)
PERMISSIVE = \"permissive\"
subject.isPermitted(permission)
saveToSession(subject);
hasPrincipals()
模板方法,由子类是实现
唯一构造方法(无参):默认实现是new SimpleSessionFactory();
AbstractFilter 抽象类
获取配置的所有过滤器链的名字,(我们之前看过了这个chainName是怎么来的),并遍历,开始作匹配了要
applyGlobalPropertiesIfNecessary(filter);
sessionId
authc返回true
过滤器链:pattern及对应处理该pattern的filter对象的名字 (多个使用逗号隔开,名字就用上面注册的filter名字)
ServletRequest request = wsc.resolveServletRequest();
SESSION_ID
logout(Subject subject);
否,即existingSubject是null
info.getObjectPermissions();
void stop()
懒加载cache过程
PrincipalCollection getPreviousPrincipals();
获取安全管理器的逻辑
String host = context.getHost();
Destroyable 接口
subjectContext
认证器来对token进行认证
loginUrl = \"/login.jsp\"
alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
boolean cachingEnabled;
assertRealmsConfigured();
设置subject上下文的AUTHENTICATION_TOKEN
ServletRequest getServletRequest();void setServletRequest(ServletRequest request);
bind(Subject subject)
String getHost();
currentPrincipals = subject.getPrincipals();
getSession(SessionKey key)
ise instanceof ExpiredSessionException
ServletResponse getServletResponse();void setServletResponse(ServletResponse response);
secure
否,表示拿不到HttpSession的话,就返回null吧
通知
没有principals,即没有登录
将filter放到DefaultFilterChainManager的filterChains中指定的拦截路径对应的NamedFilterList
static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = \".authenticationCache\";
返回Subject对象
获取认证缓存流程
DefaultSessionContext 类
existingSubject对象的sessionCreationEnabled属性
Set
final String SESSION_ID = DefaultSessionContext.class.getName() + \".SESSION_ID\";
1. 从subjectContext中根据“SESSION_ID”为键获取SessionId,2. 从subjectContext中根据“SERVLET_REQUEST”为键获取request,3. 从subjectContext中根据“SERVLET_RESPONSE”为键获取response,4. 把上面3个属性都设置到WebSessionKey中
PermissionsAuthorizationFilter 类
Session session = getSession();
DefaultFilterChainManager 过滤器链管理器
WebDelegatingSubject 类
以“AUTHENTICATED”为键,到subjectContext中获取authc
SecurityManager getSecurityManager();
AbstractRememberMeManager
webSecurityManager.isHttpSessionMode()
context.resolveAuthenticated();
Collection<SessionListener>
String authorizationCacheName;
AuthorizationInfo 接口
这里做的事情和mergePrincipals差不多,也是先Subject.getSession( ),只不过这里操作的key是DefaultSubjectContext.AUTHENTICATED_SESSION_KEY表示是否认证了,认证了就存个true放到session里
info = doGetAuthenticationInfo(token);
DelegatingFilterProxy
void
构建Builder对象
sessionDAO
HttpSession httpSession = request.getSession();
filterChainDefinitionMap
sessionIdUrlRewritingEnabled
获取AbstractShiroFilter配置的WebSecurityManager,并调用它的isHttpSessionMode()方法
session = existingSubject.getSession(false)
bean instanceof Filter
NamedFilterList chain = ensureChain(chainName);
是否开启 允许subject创建session
AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals)
oneByType(Class<T> type)
boolean[] hasRoles(List<String> roleIdentifiers);
PrincipalCollection
Serializable sessionId = doCreate(session)
protected abstract Session retrieveSession(SessionKey key)
realms.size() == 1??
void setPrincipals(PrincipalCollection principals);
从认证缓存中,根据token拿认证信息info流程
获取到过滤器链解析器
void setSecure(boolean secure);
ServletRequest 相关
获取到手动设置的拦截过滤规则
看到这个方法的名字,我们就知道shiro要来真的了
session.touch()
判断subjectContext对象中(MapContext),根据键“SESSION”去找是否保存了session
getPrincipals( )
SubjectContext
获取手动注册的filter
delegateMonitor = new Object()
HttpServletRequest request = WebUtils.getHttpRequest(key);
return subject;
setSessionValidationScheduler(scheduler);
否,返回info==null
重点!!!shiro去获取请求的uri,
volatile Filter
encryptionCipherKey
拿到securityManager的SubjectFactory属性,并使用它的createSubject(SubjectContext subjectContext)方法
Collection<Realm> realms
请求的uri不是以当前cookie的path打头的
查询缓存的逻辑
这里找的这个Filter,其实就是SpringShiroFilter,因为ShiroFilterFactoryBean实现了FactoryBean接口,返回的正是SpringShiroFilter实例
Collection<Object> getAttributeKeys(SessionKey sessionKey)
long getTimeout(SessionKey key)
String host = getHost();
EventBusAware 接口
成员变量
获取subject对象的host属性
AbstractShiroFilter
PrincipalCollection接口: 身份的集合是认证信息给的;理解为身份的一个集合,这个身份是Object类型,方便扩展;并且还有以下功能;
context.setSecurityManager(this)
MemorySessionDAO 接口
unauthorizedUrl
这里仍然以ServletContainerSessionManager实现为例
将subject对象绑定给了ThreadContext了
cache = getAvailableAuthorizationCache();
以DefaultSubjectDAO的实现为例
ServletResponse 相关
isEmpty();
否,session是null
拿全部身份的集合
以SUBJECT_KEY为键
AuthenticationInfo
version
isAuthenticationCachingEnabled()
context.setSubject(subject);
WebSubject 接口
用key去获取
RealmSecurityManager
Session session = resolveContextSession(context);
Realm
true
flag2 = scheduler == null || !scheduler.isEnabled()
SessionManager sessionManager;
buildSubject()
String[] filterTokens = splitChainDefinition(chainDefinition);
AuthenticatingRealm
获取SeucurityManager
AuthorizingRleam核心鉴权方法:(其它鉴权方法都是基于这个来做判断的) AuthenticatingRealm可以根据客户端提供的token,返回AuthenticationInfo认证信息; 而从AuthenticationInfo中就可以得到PrincipalCollection(身份的一个集合); AuthorizingRealm有个方法可以从PrincipalCollection中获取AuthorizationInfo(即授权信息); 而这个核心鉴权方法正好使用了AuthorizationInfo来判断是否具有某个Permission类型权限
Filter initDelegate(WebApplicationContext wac)
先从ThreadContext中获取SecurityManager
getObjectPermissions( )
上面能拿到的话,authc就是true
Session session = resolveSession();principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
!(realm instanceof Authorizer)
Collection
this.sessionManager.start(context)
1. 在构造方法里 默认new 创建DefaultSessionManager,设置给sessionManager对象2. 如果sessionManager实现了CacheManagerAware接口的话,那么把CacheManager设置给sessionManager
cache == null && authcCachingEnabled
1. 调用 String host = super.resolveHost(); 解析顺序HOST——>>AUTHENTICATION_TOKEN ——>> resolveSession2. 如果上面没有解析出来,就调用resolveServletRequest(),然后再调用host = request.getRemoteHost()
多realm认证
host 相关
DefaultWebSessionContext 类:使用MapContext来实现存取:\".SERVLET_REQUEST\",\".SERVLET_RESPONSE\"\".HOST\
s == null
AuthenticationInfo
SessionFactory
sessionIdGenerator
public SimpleCookie(String name)
关键代码
servletResponse
身份集合里,是不是啥都没有
此处实现是:DefaultWebSubjectContext
securityManager = context.resolveSecurityManager()
wsc.setHost(host);
从SubjectContext对象中获取属性,创建并设置到DelegateSubject对象中
Subject existing = getSubject();
this.targetBeanName = getFilterName()
Serializable sessionId = getSessionId(sessionKey);
resolvePermissions(info.getStringPermissions())
Session s = retrieveSessionFromDataSource(sessionId);
DefaultWebSecurityManager
ThreadContext.bind(securityManager)
rmm.getRememberedPrincipals(subjectContext)
获取安全管理器
isFilterChainContinued逻辑
PrincipalCollection currentPrincipals = null;
final ServletRequest
清除掉session中的run as身份
!CollectionUtils.isEmpty(currentPrincipals) ??
void checkValid(SessionKey key)
然后再把currentPrincipals放入到sessino中
listeners
boolean shouldNotFilter(request)
WebApplicationContext
默认实现,调用下面这个方法
boolean isHttpSessionMode();
拿特定类型的身份
getName();
session = getSession(key)
public SimpleSession() { this.timeout = 30*60*1000; this.startTimestamp = new Date(); this.lastAccessTime = this.startTimestamp; } public SimpleSession(String host) { this(); this.host = host; }
根据“AUTHENTICATION_INFO”为键
key != null
PrincipalCollection existingPrincipals =session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
securityManager来鉴权了
cache.remove(key);
!CollectionUtils.isEmpty(principals)
create(s)
Session s = newSessionInstance(context);
认证信息接口:从接口定义上来看, principalCollection认证信息中会包含多个“身份”,这个身份对应的是Object类型 密码也是Object类型,提供给shiro去比对,如果比对成功,那么这个认证信息里的PrincipalCollection就归客户端了
判断subjectContext对象中(MapContext),根据键“SUBJECT”去找是否保存了Subject
以DefaultWebSubjectFactory的实现为例
调用此方法,填充属性
FilterChainManager manager = createFilterChainManager()
path!=null&&flag
FilterChainResolver
new ServletContainerSessionManager()
这样去获取SecurityManager应该存在一个问题1. 这个securityManager无非是从两个地方去拿,并且两个地方都是直接去拿,并没有创建的逻辑在里面,所以必须手动设置安全管理器
WebDelegatingSubject
线程池执行的任务
这里用PermissionResolver
HOST
saveRequest(request);
设置session的超时时间
supports(AuthenticationToken token);
0 条评论
下一页