相关阅读
- Spring Shiro流程简析 登录请求处理
- Spring Shiro流程简析 注解驱动的权限管理
- Spring Shiro基础组件 Configuration
- Spring Shiro基础组件 Filter
简介
Shiro框架通过拦截器实现用户鉴权,其核心为AbstractShiroFilter
;
在SpringBoot框架中,可以借助FactoryBean
机制来配置AbstractShiroFilter
;
配置代码
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 自定义URL过滤器链规则
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// 自定义过滤器实现
LinkedHashMap<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("authc", new ShiroFormAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filterMap);
return shiroFilterFactoryBean;
}
过滤器链创建
ShiroFilterFactoryBean
实现了FactoryBean<AbstractShiroFilter>
接口,故可以通过其getObject
方法获取AbstractShiroFilter
实例,代码如下:
public AbstractShiroFilter getObject() throws Exception {
if (instance == null) {
// Spring会保证创建Bean时的线程安全
// 还未创建实例,则创建实例
instance = createInstance();
}
return instance;
}
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
// 校验SecurityManager
SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
// 创建过滤器链管理器
FilterChainManager manager = createFilterChainManager();
// 创建过滤器链解析器,内部持有过滤器链管理器
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
// 创建SpringShiroFilter,并传入SecurityManager和PathMatchingFilterChainResolver
// SecurityManager用于用户鉴权
// PathMatchingFilterChainResolver 用于为URL创建过滤器链
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
在创建FilterChainManager
实例时,就会根据配置信息构建URL对应的过滤器链,代码如下:
protected FilterChainManager createFilterChainManager() {
DefaultFilterChainManager manager = new DefaultFilterChainManager();
// 应用全局配置到默认的过滤器
// 默认的过滤器见DefaultFilter
Map<String, Filter> defaultFilters = manager.getFilters();
for (Filter filter : defaultFilters.values()) {
applyGlobalPropertiesIfNecessary(filter);
}
//Apply the acquired and/or configured filters:
// 应用全局配置到配置的过滤器
Map<String, Filter> filters = getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry<String, Filter> entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
manager.addFilter(name, filter, false);
}
}
// 设置全局过滤器
manager.setGlobalFilters(this.globalFilters);
// 根据配置构建URL对应的过滤器链
Map<String, String> chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}
// 构建任意URL默认的过滤器链
manager.createDefaultChain("/**");
return manager;
}
根据配置信息为URL构建对应的过滤器链的实现代码如下:
public void createChain(String chainName, String chainDefinition) {
// 校验配置信息
if (!StringUtils.hasText(chainName)) {
throw new NullPointerException("chainName cannot be null or empty.");
}
if (!StringUtils.hasText(chainDefinition)) {
throw new NullPointerException("chainDefinition cannot be null or empty.");
}
if (log.isDebugEnabled()) {
log.debug("Creating chain [" + chainName + "] with global filters " + globalFilterNames + " and from String definition [" + chainDefinition + "]");
}
// 若存在全局过滤器,则先加入到过滤器链中
if (!CollectionUtils.isEmpty(globalFilterNames)) {
globalFilterNames.stream().forEach(filterName -> addToChain(chainName, filterName));
}
// 分解过滤器定义
// "authc, roles[admin,user], perms[file:edit]" ->
// { "authc", "roles[admin,user]", "perms[file:edit]" }
String[] filterTokens = splitChainDefinition(chainDefinition);
// 遍历每个过滤器定义
for (String token : filterTokens) {
// 分解过滤器名称和配置参数
// "authc" -> {"authc", null}
// "roles[admin,user]" -> {"roles", "admin,user"}
String[] nameConfigPair = toNameConfigPair(token);
// 加入过滤器链
addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
}
}
加入过滤器链的实现代码如下:
public void addToChain(String chainName, String filterName) {
addToChain(chainName, filterName, null);
}
public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
if (!StringUtils.hasText(chainName)) {
throw new IllegalArgumentException("chainName cannot be null or empty.");
}
// 根据过滤器名查找对应的过滤器
// 默认的对应关系在DefaultFilter中定义
// 可以通过调用ShiroFilterFactoryBean.setFilters自定义对应关系
Filter filter = getFilter(filterName);
if (filter == null) {
throw new IllegalArgumentException("There is no filter with name '" + filterName +
"' to apply to chain [" + chainName + "] in the pool of available Filters. Ensure a " +
"filter with that name/path has first been registered with the addFilter method(s).");
}
// 应用过滤器配置
// chainName就是访问过滤器时的URL
// chainSpecificFilterConfig就是过滤器的配置信息
applyChainConfig(chainName, filter, chainSpecificFilterConfig);
// 确保过滤器链存在,没有就创建
NamedFilterList chain = ensureChain(chainName);
// 添加过滤器到过滤器链
chain.add(filter);
}
至此,需要拦截的URL的对应过滤器链就创建完成;
接下来就是如何执行过滤器链;
过滤器链执行
在上文中,配置的ShiroFilterFactoryBean
实例会在Spring IOC容器中引入AbstractShiroFilter
,那么处理请求的过滤器链中就会存在过滤器AbstractShiroFilter
;AbstractShiroFilter
在执行过滤逻辑时,会将原过滤器链进行代理,在代理过滤器链ProxiedFilterChain
中加入了Shiro配置的过滤器链,且优先将Shiro配置的过滤器链执行完,再执行原过滤器链,从而在请求被真正处理之前执行了对请求进行权限管理相关操作;
AbstractShiroFilter
过滤逻辑如下:
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {
Throwable t = null;
try {
// 包装ServletRequest,支持Subject相关操作
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
// 包装ServletResponse,基于ShiroHttpSession
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
// 创建新Subject
// 虽然是新创建,但是会通过Session设置上次登录信息
final Subject subject = createSubject(request, response);
// 同步执行
subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response);
// 创建代理过滤器链并执行
executeChain(request, response, chain);
return null;
}
});
} catch (ExecutionException ex) {
t = ex.getCause();
} catch (Throwable throwable) {
t = throwable;
}
if (t != null) {
if (t instanceof ServletException) {
throw (ServletException) t;
}
if (t instanceof IOException) {
throw (IOException) t;
}
String msg = "Filtered request failed.";
throw new ServletException(msg, t);
}
}
主要分为两步:
- 创建Subject;
- 创建代理过滤器链并执行;
创建Subject
AbstractShiroFilter
会为每次请求都创建一个新Subject
,代码如下:
protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}
借助WebSubject.Builder
实现Subject
创建,代码如下:
public Builder(SecurityManager securityManager, ServletRequest request, ServletResponse response) {
// 调用父类Subject.Builder构造函数
super(securityManager);
if (request == null) {
throw new IllegalArgumentException("ServletRequest argument cannot be null.");
}
if (response == null) {
throw new IllegalArgumentException("ServletResponse argument cannot be null.");
}
setRequest(request);
setResponse(response);
}
protected SubjectContext newSubjectContextInstance() {
return new DefaultWebSubjectContext();
}
父类Subject.Builder
构造方法代码如下:
public Builder(SecurityManager securityManager) {
if (securityManager == null) {
throw new NullPointerException("SecurityManager method argument cannot be null.");
}
this.securityManager = securityManager;
// WebSubject.Builder会创建DefaultWebSubjectContext
this.subjectContext = newSubjectContextInstance();
if (this.subjectContext == null) {
throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
"cannot be null.");
}
this.subjectContext.setSecurityManager(securityManager);
}
WebSubject.Builder.buildSubject
的实现内部也是调用父类Subject.Builder.buildSubject
,代码如下:
public Subject buildSubject() {
return this.securityManager.createSubject(this.subjectContext);
}
借助SecurityManager
实现构建Subject
,代码如下:
public Subject createSubject(SubjectContext subjectContext) {
// 创建拷贝,后续基于拷贝操作
SubjectContext context = copy(subjectContext);
// SecurityManager不存在就设置自身
context = ensureSecurityManager(context);
// 查找Session如果存在的话
context = resolveSession(context);
// 查找PrincipalCollection如果存在的话
context = resolvePrincipals(context);
// 创建Subject
Subject subject = doCreateSubject(context);
// 保存Subject
save(subject);
return subject;
}
其中查找Session
的过程比较重要,接下来重点分析;
查找Session
resolveSession
方法实现代码如下:
protected SubjectContext resolveSession(SubjectContext context) {
if (context.resolveSession() != null) {
// 若已存在则直接返回
log.debug("Context already contains a session. Returning.");
return context;
}
try {
// 尝试查找Session
Session session = resolveContextSession(context);
if (session != null) {
// 找到则保存到SubjectContext中
context.setSession(session);
}
} catch (InvalidSessionException e) {
log.debug("Resolved SubjectContext context session is invalid. Ignoring and creating an anonymous " +
"(session-less) Subject instance.", e);
}
return context;
}
protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {
// 根据SubjectContext构建SessionKey
// DefaultWebSecurityManager重写了该方法实现
SessionKey key = getSessionKey(context);
if (key != null) {
// 存在SessionKey则尝试查找Session
return getSession(key);
}
return null;
}
// DefaultWebSecurityManager.java
protected SessionKey getSessionKey(SubjectContext context) {
if (WebUtils.isWeb(context)) {
// 如果是Web请求则构建WebSessionKey
// 即使SubjectContext不存在SessionId也会构建SessionKey
Serializable sessionId = context.getSessionId();
ServletRequest request = WebUtils.getRequest(context);
ServletResponse response = WebUtils.getResponse(context);
return new WebSessionKey(sessionId, request, response);
} else {
return super.getSessionKey(context);
}
}
public Session getSession(SessionKey key) throws SessionException {
// 使用SessionManager查找Session
return this.sessionManager.getSession(key);
}
在Spring环境下,默认使用DefaultWebSessionManager
,用户可自定义SessionManager
实现,用于获取SessionId
;DefaultWebSessionManager.getSession(SessionKey)
方法继承自父类AbstractNativeSessionManager
,代码如下:
public Session getSession(SessionKey key) throws SessionException {
Session session = lookupSession(key);
return session != null ? createExposedSession(session, key) : null;
}
private Session lookupSession(SessionKey key) throws SessionException {
if (key == null) {
throw new NullPointerException("SessionKey argument cannot be null.");
}
return doGetSession(key);
}
// AbstractValidatingSessionManager.java
protected final Session doGetSession(final SessionKey key) throws InvalidSessionException {
// 确保开启Session校验如果需要的话
enableSessionValidationIfNecessary();
log.trace("Attempting to retrieve session with key {}", key);
// 查找Session
Session s = retrieveSession(key);
if (s != null) {
// 校验Session
// 每次获取到Session后进行校验,确保Session有效
validate(s, key);
}
return s;
}
// DefaultSessionManager.java
protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
// 从SessionKey中解析SessionId
// Web请求从根据Request和Response生成ID
Serializable sessionId = getSessionId(sessionKey);
if (sessionId == null) {
log.debug("Unable to resolve session ID from SessionKey [{}]. Returning null to indicate a " +
"session could not be found.", sessionKey);
return null;
}
// 根据SessionId查找
Session s = retrieveSessionFromDataSource(sessionId);
if (s == null) {
//session ID was provided, meaning one is expected to be found, but we couldn't find one:
String msg = "Could not find session with ID [" + sessionId + "]";
throw new UnknownSessionException(msg);
}
return s;
}
// DefaultWebSessionManager.java
public Serializable getSessionId(SessionKey key) {
Serializable id = super.getSessionId(key);
if (id == null && WebUtils.isWeb(key)) {
// 如果是Web请求,则从ServletRequest和ServletResponse中获取SessionId
ServletRequest request = WebUtils.getRequest(key);
ServletResponse response = WebUtils.getResponse(key);
id = getSessionId(request, response);
}
return id;
}
// DefaultWebSessionManager.java
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
return getReferencedSessionId(request, response);
}
// DefaultWebSessionManager.java
private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
// 尝试从Cookie中获取
String id = getSessionIdCookieValue(request, response);
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
} else {
// 尝试从请求URL路径中获取JSESSIONID
id = getUriPathSegmentParamValue(request, ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
if (id == null && request instanceof HttpServletRequest) {
// 尝试从请求参数中获取JSESSIONID
String name = getSessionIdName();
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
String queryString = httpServletRequest.getQueryString();
if (queryString != null && queryString.contains(name)) {
id = request.getParameter(name);
}
if (id == null && queryString != null && queryString.contains(name.toLowerCase())) {
//try lowercase:
id = request.getParameter(name.toLowerCase());
}
}
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
}
}
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
}
request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled());
return id;
}
// DefaultSessionManager.java
protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
// 从SessionDao中根据SessionId查找
// 默认使用MemorySessionDAO
return sessionDAO.readSession(sessionId);
}
如果之前已存在Session
,那么这时候就能找到对应的Session
,并将其放入SubjectContext
中;
继续回到SecurityManager
构建Subject
的方法中,代码如下:
public Subject createSubject(SubjectContext subjectContext) {
// 创建拷贝,后续基于拷贝操作
SubjectContext context = copy(subjectContext);
// SecurityManager不存在就设置自身
context = ensureSecurityManager(context);
// 查找Session如果存在的话
context = resolveSession(context);
// 查找PrincipalCollection如果存在的话
context = resolvePrincipals(context);
// 创建Subject
Subject subject = doCreateSubject(context);
// 保存Subject
save(subject);
return subject;
}
resolvePrincipals
实现代码如下:
protected SubjectContext resolvePrincipals(SubjectContext context) {
PrincipalCollection principals = context.resolvePrincipals();
// 当前Subject还不存在PrincipalCollection
if (isEmpty(principals)) {
log.trace("No identity (PrincipalCollection) found in the context. Looking for a remembered identity.");
// 从RememberMeManager中查找
principals = getRememberedIdentity(context);
if (!isEmpty(principals)) {
log.debug("Found remembered PrincipalCollection. Adding to the context to be used " +
"for subject construction by the SubjectFactory.");
// 找到则保存到SubjectContext
context.setPrincipals(principals);
} else {
log.trace("No remembered identity found. Returning original context.");
}
}
return context;
}
解析完SubjectContext
后,就可以创建Subject
了,代码如下:
protected Subject doCreateSubject(SubjectContext context) {
return getSubjectFactory().createSubject(context);
}
// DefaultWebSubjectFactory.java
public Subject createSubject(SubjectContext context) {
boolean isNotBasedOnWebSubject = context.getSubject() != null && !(context.getSubject() instanceof WebSubject);
if (!(context instanceof WebSubjectContext) || isNotBasedOnWebSubject) {
return super.createSubject(context);
}
WebSubjectContext wsc = (WebSubjectContext) context;
SecurityManager securityManager = wsc.resolveSecurityManager();
Session session = wsc.resolveSession();
boolean sessionEnabled = wsc.isSessionCreationEnabled();
// 获取PrincipalCollection
PrincipalCollection principals = wsc.resolvePrincipals();
boolean authenticated = wsc.resolveAuthenticated();
String host = wsc.resolveHost();
ServletRequest request = wsc.resolveServletRequest();
ServletResponse response = wsc.resolveServletResponse();
return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,
request, response, securityManager);
}
其中wsc.resolvePrincipals()
的过程比较重要,接下来重点分析;
查找Principals
wsc.resolvePrincipals()
的实现代码如下:
public PrincipalCollection resolvePrincipals() {
// 查找PrincipalCollection
PrincipalCollection principals = getPrincipals();
if (isEmpty(principals)) {
// 从鉴权信息中获取如果存在的话
AuthenticationInfo info = getAuthenticationInfo();
if (info != null) {
principals = info.getPrincipals();
}
}
if (isEmpty(principals)) {
// 从已存在的Subject中获取如果存在的话
Subject subject = getSubject();
if (subject != null) {
principals = subject.getPrincipals();
}
}
if (isEmpty(principals)) {
// 从Session中获取如果存在的话
// 用户登录成功后,Shiro会将PrincipalCollection信息保存到Session中,详见mergePrincipals
Session session = resolveSession();
if (session != null) {
principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
}
}
return principals;
}
最后就是保存Subject
,代码如下:
protected void save(Subject subject) {
// 借助SubjectDao保存Subject
this.subjectDAO.save(subject);
}
默认使用DefaultSubjectDAO
,代码如下:
public Subject save(Subject subject) {
if (isSessionStorageEnabled(subject)) {
// 开启Session保存则保存到Session中
saveToSession(subject);
} else {
log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " +
"authentication state are expected to be initialized on every request or invocation.", subject);
}
return subject;
}
protected void saveToSession(Subject subject) {
// 合并当前Subject中的Principals
mergePrincipals(subject);
// 合并当前Subject中的鉴权标志
mergeAuthenticationState(subject);
}
创建出Subject后就开始构建代理过滤器链并执行;
创建代理过滤器链并执行
代码如下:
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
throws IOException, ServletException {
FilterChain chain = getExecutionChain(request, response, origChain);
chain.doFilter(request, response);
}
构建代理过滤器链代码如下:
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
// 备份原过滤器链
FilterChain chain = origChain;
FilterChainResolver resolver = getFilterChainResolver();
if (resolver == null) {
// 不存在过滤器链解析器,则无需构建代理过滤器链
log.debug("No FilterChainResolver configured. Returning original FilterChain.");
return origChain;
}
// 根据请求和原过滤器链构建代理过滤器链
FilterChain resolved = resolver.getChain(request, response, origChain);
if (resolved != null) {
log.trace("Resolved a configured FilterChain for the current request.");
// 存在代理过滤器链则使用
chain = resolved;
} else {
log.trace("No FilterChain configured for the current request. Using the default.");
}
return chain;
}
由上文过滤器链创建可知,过滤器链解析器默认使用PathMatchingFilterChainResolver
,其getChain
方法代码如下:
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
FilterChainManager filterChainManager = getFilterChainManager();
if (!filterChainManager.hasChains()) {
return null;
}
final String requestURI = getPathWithinApplication(request);
final String requestURINoTrailingSlash = removeTrailingSlash(requestURI);
// 遍历配置的所有的过滤器链
for (String pathPattern : filterChainManager.getChainNames()) {
// 请求URL匹配当前过滤器链路径,则存在过滤器链需要代理
if (pathMatches(pathPattern, requestURI)) {
if (log.isTraceEnabled()) {
log.trace("Matched path pattern [{}] for requestURI [{}]. " +
"Utilizing corresponding filter chain...", pathPattern, Encode.forHtml(requestURI));
}
return filterChainManager.proxy(originalChain, pathPattern);
} else {
// 两者去除'/'后,再次匹配
pathPattern = removeTrailingSlash(pathPattern);
if (pathMatches(pathPattern, requestURINoTrailingSlash)) {
if (log.isTraceEnabled()) {
log.trace("Matched path pattern [{}] for requestURI [{}]. " +
"Utilizing corresponding filter chain...", pathPattern, Encode.forHtml(requestURINoTrailingSlash));
}
return filterChainManager.proxy(originalChain, pathPattern);
}
}
}
return null;
}
代理过滤器链构建出之后,就开始执行过滤器链
chain.doFilter(request, response);
在分析执行过滤器链之前,先分析创建代理过滤器链并执行这一步是如何执行的,代码如下:
subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response);
executeChain(request, response, chain);
return null;
}
});
// DelegatingSubject.java
public <V> V execute(Callable<V> callable) throws ExecutionException {
// 创建SubjectCallable
Callable<V> associated = associateWith(callable);
try {
return associated.call();
} catch (Throwable t) {
throw new ExecutionException(t);
}
}
// SubjectCallable.java
public V call() throws Exception {
try {
// 保存Subject到本地线程
// 这样在任务执行过程中就可以访问到Subject
threadState.bind();
// 真正执行任务
return doCall(this.callable);
} finally {
// 清除Subject信息
threadState.restore();
}
}
// SubjectThreadState.java
public void bind() {
SecurityManager securityManager = this.securityManager;
if ( securityManager == null ) {
//try just in case the constructor didn't find one at the time:
securityManager = ThreadContext.getSecurityManager();
}
// 备份,会在restore中恢复
this.originalResources = ThreadContext.getResources();
ThreadContext.remove();
// 保存Subject到本地线程
// 这样就可以通过SecurityUtils.getSubject()获取到该Subject
ThreadContext.bind(this.subject);
if (securityManager != null) {
// 保存SecurityManager到本地线程
ThreadContext.bind(securityManager);
}
}
由上文可知,在过滤器链执行过程中,可以通过SecurityUtils.getSubject()
获取到为本次请求创建的Subject
,Shiro过滤器中,我们简单以FormAuthenticationFilter
为例,分析其过滤逻辑;
FormAuthenticationFilter
继承自AuthenticationFilter
,AuthenticationFilter
实现了isAccessAllowed
方法,代码如下:
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
// 根据Request和Response获取Subject
// 其实就是获取本地线程中的Subject
Subject subject = getSubject(request, response);
// 已经完成鉴权且Principal存在,则允许通过
return subject.isAuthenticated() && subject.getPrincipal() != null;
}
protected Subject getSubject(ServletRequest request, ServletResponse response) {
// 获取本地线程中的Subject
return SecurityUtils.getSubject();
}
FormAuthenticationFilter
实现了onAccessDenied
算法细节,代码如下:
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response)) {
if (isLoginSubmission(request, response)) {
// 登录请求且允许登录的话,则执行登录
if (log.isTraceEnabled()) {
log.trace("Login submission detected. Attempting to execute login.");
}
return executeLogin(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Login page view.");
}
//allow them to see the login page ;)
return true;
}
} else {
if (log.isTraceEnabled()) {
log.trace("Attempting to access a path which requires authentication. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
// 保存当前请求并重定向到登录页面
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
executeLogin
由父类AuthenticatingFilter
实现,代码如下:
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
// 创建AuthenticationToken
AuthenticationToken token = createToken(request, response);
if (token == null) {
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
"must be created in order to execute a login attempt.";
throw new IllegalStateException(msg);
}
try {
Subject subject = getSubject(request, response);
// 进行登录
// 未抛出异常则登录成功
subject.login(token);
return onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException e) {
return onLoginFailure(token, e, request, response);
}
}
FormAuthenticationFilter
实现了createToken
算法细节,代码如下;
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
// 获取请求参数中的用户名
String username = getUsername(request);
// 获取请求参数中的密码
String password = getPassword(request);
// 调用AuthenticatingFilter.createToken创建AuthenticationToken
return createToken(username, password, request, response);
}
protected String getUsername(ServletRequest request) {
return WebUtils.getCleanParam(request, getUsernameParam());
}
protected String getPassword(ServletRequest request) {
return WebUtils.getCleanParam(request, getPasswordParam());
}
至此,过滤器链执行的分析结束。