相关阅读
简介
对资源(Servlet或静态内容)的请求或者响应执行过滤任务;
每个过滤器可以通过FilterConfig
获取自身初始化参数,以及ServletContext
用来加载所需资源;
核心方法
/**
* 初始化
* 由Web容器调用,只会调用一次
* 该方法执行成功,本过滤器才可以执行过滤工作
*/
public void init(FilterConfig filterConfig) throws ServletException;
/**
* 过滤
* 由Web容器调用,每个资源的请求经过过滤器链时,Web容器都会调用doFilter方法
* FilterChain允许当前过滤器将请求传递到下一个过滤器
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
/**
* 销毁
* 由Web容器调用,只会调用一次,仅当doFilter方法中所有线程都退出或者超时后才会调用
* 该方法执行后,Web容器不会再调用其doFilter方法
*/
public void destroy();
实现子类
public interface Filter
public abstract class AbstractFilter extends ServletContextSupport implements Filter // 实现init(FilterConfig)算法模板和getInitParam(String),提供onFilterConfigSet()算法细节的默认实现,由子类扩展
public abstract class NameableFilter extends AbstractFilter implements Nameable // 增加name字段,默认为FilterConfig.getFilterName()
public abstract class OncePerRequestFilter extends NameableFilter // 增加enabled使能标识,实现doFilter(ServletRequest, ServletResponse, FilterChain)算法模板,提供抽象方法doFilterInternal(ServletRequest, ServletResponse, FilterChain)算法细节由子类实现
public abstract class AbstractShiroFilter extends OncePerRequestFilter // 实现onFilterConfigSet()/doFilterInternal(ServletRequest, ServletResponse, FilterChain)算法细节,提供init()算法细节的默认实现,由子类扩展
public class ShiroFilter extends AbstractShiroFilter // 实现init()算法细节,设置SecurityManager/FilterChainResolver
public abstract class AdviceFilter extends OncePerRequestFilter // 实现doFilterInternal(ServletRequest, ServletResponse, FilterChain)/cleanup(ServletRequest, ServletResponse, Exception)算法模板,提供preHandle(ServletRequest, ServletResponse)/postHandle(ServletRequest, ServletResponse)/afterCompletion(ServletRequest, ServletResponse, Exception)算法细节的默认实现,由子类扩展
public class LogoutFilter extends AdviceFilter // 实现preHandle(ServletRequest, ServletResponse)算法细节
public abstract class PathMatchingFilter extends AdviceFilter implements PathConfigProcessor // 实现preHandle(ServletRequest, ServletResponse)算法模板和processPathConfig(String, String),提供onPreHandle(ServletRequest, ServletResponse, Object)算法细节的默认实现,由子类扩展
public class NoSessionCreationFilter extends PathMatchingFilter 实现onPreHandle(ServletRequest, ServletResponse, Object)算法细节
public class AnonymousFilter extends PathMatchingFilter // 实现onPreHandle(ServletRequest, ServletResponse, Object)算法细节
public abstract class AccessControlFilter extends PathMatchingFilter // 实现onPreHandle(ServletRequest, ServletResponse, Object)算法模板和saveRequestAndRedirectToLogin(ServletRequest, ServletResponse),提供抽象方法isAccessAllowed(ServletRequest, ServletResponse, Object)/onAccessDenied(ServletRequest, ServletResponse)算法细节由子类实现
public class UserFilter extends AccessControlFilter // 实现isAccessAllowed(ServletRequest, ServletResponse, Object)/onAccessDenied(ServletRequest, ServletResponse)算法细节
public class InvalidRequestFilter extends AccessControlFilter isAccessAllowed(ServletRequest, ServletResponse, Object)/onAccessDenied(ServletRequest, ServletResponse)
public abstract class AuthenticationFilter extends AccessControlFilter // 实现isAccessAllowed(ServletRequest, ServletResponse, Object)算法细节
public class PassThruAuthenticationFilter extends AuthenticationFilter // 实现onAccessDenied(ServletRequest, ServletResponse)算法细节
public abstract class AuthenticatingFilter extends AuthenticationFilter // 实现isAccessAllowed(ServletRequest, ServletResponse, Object)/cleanup(ServletRequest, ServletResponse, Exception)算法细节,实现executeLogin(ServletRequest, ServletResponse)算法模板,提供抽象方法createToken(ServletRequest, ServletResponse)算法细节由子类实现,提供createToken(String, String, boolean, String)/onLoginSuccess(AuthenticationToken, Subject, ServletRequest, ServletResponse)/onLoginFailure(AuthenticationToken, Subject, ServletRequest, ServletResponse)算法细节的默认实现,由子类扩展
public class FormAuthenticationFilter extends AuthenticatingFilter // 实现onAccessDenied(ServletRequest, ServletResponse)/createToken(ServletRequest, ServletResponse)/onLoginSuccess(AuthenticationToken, Subject, ServletRequest, ServletResponse)/onLoginFailure(AuthenticationToken, Subject, ServletRequest, ServletResponse)算法细节
abstract class HttpAuthenticationFilter extends AuthenticatingFilter // 实现isAccessAllowed(ServletRequest, ServletResponse, Object)/onAccessDenied(ServletRequest, ServletResponse)/createToken(ServletRequest, ServletResponse)算法细节,提供抽象方法getPrincipalsAndCredentials(String, String)算法细节由子类实现
public class BasicHttpAuthenticationFilter extends HttpAuthenticationFilter // 实现createToken(ServletRequest, ServletResponse)/getPrincipalsAndCredentials(String, String)算法细节
public class BearerHttpAuthenticationFilter extends HttpAuthenticationFilter // 实现createToken(ServletRequest, ServletResponse)/getPrincipalsAndCredentials(String, String)算法细节
public abstract class AuthorizationFilter extends AccessControlFilter // 实现onAccessDenied(ServletRequest, ServletResponse)算法细节
public class RolesAuthorizationFilter extends AuthorizationFilter // 实现isAccessAllowed(ServletRequest, ServletResponse, Object)算法细节
public class PermissionsAuthorizationFilter extends AuthorizationFilter // 实现isAccessAllowed(ServletRequest, ServletResponse, Object)算法细节
public class HttpMethodPermissionFilter extends PermissionsAuthorizationFilter // 实现isAccessAllowed(ServletRequest, ServletResponse, Object)算法细节
public class PortFilter extends AuthorizationFilter// 实现isAccessAllowed(ServletRequest, ServletResponse, Object)/onAccessDenied(ServletRequest, ServletResponse)算法细节
public class SslFilter extends PortFilter // 实现isAccessAllowed(ServletRequest, ServletResponse, Object)/postHandle(ServletRequest, ServletResponse)算法细节
AbstractFilter
简介
简化了初始化操作和获取初始化参数的Filter
基础抽象类;
子类通过重写onFilterConfigSet()
模板方法实现自身的初始化逻辑;
FilterChain
调用的doFilter()
由子类实现;
核心方法
// 过滤器配置,由Servlet容器提供
protected FilterConfig filterConfig;
/**
* 设置filterConfig
*/
public void setFilterConfig(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
setServletContext(filterConfig.getServletContext());
}
/**
* 获取初始化参数
*/
protected String getInitParam(String paramName) {
FilterConfig config = getFilterConfig();
if (config != null) {
return StringUtils.clean(config.getInitParameter(paramName));
}
return null;
}
/**
* 初始化
* 模板方法,onFilterConfigSet()方法由子类实现逻辑处理
*/
public final void init(FilterConfig filterConfig) throws ServletException {
setFilterConfig(filterConfig);
try {
// 触发子类的额外逻辑处理
onFilterConfigSet();
} catch (Exception e) {
if (e instanceof ServletException) {
throw (ServletException) e;
} else {
if (log.isErrorEnabled()) {
log.error("Unable to start Filter: [" + e.getMessage() + "].", e);
}
throw new ServletException(e);
}
}
}
/**
* 过滤器配置设置时处理
* 模板子方法,由子类实现
*/
protected void onFilterConfigSet() throws Exception {
}
/**
* 销毁
*/
public void destroy() {
}
NameableFilter
简介
支持名称的Filter
;
默认使用FilterConfig.getFilterName()
;
核心方法
// 过滤器名称,应用程序内唯一
private String name;
/**
* 获取过滤器名称
*/
protected String getName() {
if (this.name == null) {
// 未设置时,默认取FilterConfig.getFilterName()
FilterConfig config = getFilterConfig();
if (config != null) {
this.name = config.getFilterName();
}
}
return this.name;
}
OncePerRequestFilter
简介
保证在每次请求中只被执行一次的过滤器;
核心方法
// 使能标识,true表示执行过滤,否则放行请求
private boolean enabled = true;
/**
* 过滤
* 模板方法,定义过滤逻辑的算法骨架,由子类实现doFilterInternal算法细节
*/
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
// 如果已经执行过滤,则直接放行
log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName());
filterChain.doFilter(request, response);
} else //noinspection deprecation
if (/* added in 1.2: */ !isEnabled(request, response) ||
/* retain backwards compatibility: */ shouldNotFilter(request) ) {
// 未使能,则直接放行
log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.",
getName());
filterChain.doFilter(request, response);
} else {
// Do invoke this filter...
log.trace("Filter '{}' not yet executed. Executing now.", getName());
// 存储本过滤器名称,防止再次执行
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
// 执行过滤逻辑
doFilterInternal(request, response, filterChain);
} finally {
// Once the request has finished, we're done and we don't
// need to mark as 'already filtered' any more.
// 请求结束后,清除本过滤器名称
request.removeAttribute(alreadyFilteredAttributeName);
}
}
}
/**
* 是否需要执行过滤逻辑
*/
protected boolean isEnabled(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// 默认直接返回enable,子类可重写如必要
return isEnabled();
}
/**
* 过滤
* 算法细节,由子类实现
*/
protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException;
AbstractShiroFilter
简介
提供标准Shiro请求的过滤逻辑的基础抽象类,期望子类实现特定于配置的逻辑;
子类需要重写init()
方法执行配置和构造逻辑,通过setSecurityManager(WebSecurityManager)
和setFilterChainResolver(FilterChainResolver)
方法设置已构造的SecurityManager
和FilterChainResolver
;
核心方法
private WebSecurityManager securityManager;
private FilterChainResolver filterChainResolver;
// 在静态内存中启用本实例启用的SecurityManager实例标识
private boolean staticSecurityManagerEnabled;
/**
* 构造方法
*/
protected AbstractShiroFilter() {
// 默认不使用SecurityUtils.setSecurityManager
// 期望Subject实例通过此过滤器实例在请求处理线程上构造
this.staticSecurityManagerEnabled = false;
}
/**
* 过滤器配置设置时处理
* 算法细节
*/
protected final void onFilterConfigSet() throws Exception {
// 应用在静态内存中启用本实例启用的SecurityManager实例标识配置
//added in 1.2 for SHIRO-287:
applyStaticSecurityManagerEnabledConfig();
// 初始化
init();
// 确保SecurityManager有效
ensureSecurityManager();
//added in 1.2 for SHIRO-287:
if (isStaticSecurityManagerEnabled()) {
// 在静态内存中启用本实例启用的SecurityManager实例
SecurityUtils.setSecurityManager(getSecurityManager());
}
}
/**
* 应用在静态内存中启用本实例启用的SecurityManager实例标识配置
*/
private void applyStaticSecurityManagerEnabledConfig() {
// 获取在静态内存中启用本实例启用的SecurityManager实例标识配置
String value = getInitParam(STATIC_INIT_PARAM_NAME);
if (value != null) {
Boolean b = Boolean.valueOf(value);
if (b != null) {
// 不为null则应用该配置
setStaticSecurityManagerEnabled(b);
}
}
}
/**
* 初始化
* 模板子方法,由子类实现
*/
public void init() throws Exception {
}
/**
* 确保SecurityManager有效
*/
private void ensureSecurityManager() {
WebSecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
// 若SecurityManager实例不存在则创建默认实例
log.info("No SecurityManager configured. Creating default.");
securityManager = createDefaultSecurityManager();
setSecurityManager(securityManager);
}
}
/**
* 创建默认SecurityManager实例
*/
protected WebSecurityManager createDefaultSecurityManager() {
// 默认使用DefaultWebSecurityManager
return new DefaultWebSecurityManager();
}
/**
* 是否是HttpSession
*/
protected boolean isHttpSessions() {
return getSecurityManager().isHttpSessionMode();
}
/**
* 包装ServletRequest为ShiroHttpServletRequest
*/
protected ServletRequest wrapServletRequest(HttpServletRequest orig) {
return new ShiroHttpServletRequest(orig, getServletContext(), isHttpSessions());
}
/**
* 预处理ServletRequest
*/
protected ServletRequest prepareServletRequest(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletRequest toUse = request;
if (request instanceof HttpServletRequest) {
// 如果是HttpServletRequest则需要包装为ShiroHttpServletRequest
HttpServletRequest http = (HttpServletRequest) request;
toUse = wrapServletRequest(http);
}
return toUse;
}
/**
* 包装ServletResponse为ShiroHttpServletResponse
*/
protected ServletResponse wrapServletResponse(HttpServletResponse orig, ShiroHttpServletRequest request) {
return new ShiroHttpServletResponse(orig, getServletContext(), request);
}
/**
* 预处理ServletResponse
*/
protected ServletResponse prepareServletResponse(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletResponse toUse = response;
if (!isHttpSessions() && (request instanceof ShiroHttpServletRequest) &&
(response instanceof HttpServletResponse)) {
//the ShiroHttpServletResponse exists to support URL rewriting for session ids. This is only needed if
//using Shiro sessions (i.e. not simple HttpSession based sessions):
toUse = wrapServletResponse((HttpServletResponse) response, (ShiroHttpServletRequest) request);
}
return toUse;
}
/**
* 创建Subject
*/
protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}
/**
* 更新本地Session的最近一次访问时间
*/
protected void updateSessionLastAccessTime(ServletRequest request, ServletResponse response) {
if (!isHttpSessions()) { //'native’ sessions
// 是本地Session
Subject subject = SecurityUtils.getSubject();
//Subject should never _ever_ be null, but just in case:
if (subject != null) {
Session session = subject.getSession(false);
if (session != null) {
try {
// 更新最近一次访问时间
session.touch();
} catch (Throwable t) {
log.error("session.touch() method invocation has failed. Unable to update " +
"the corresponding session's last access time based on the incoming request.", t);
}
}
}
}
}
/**
* 过滤
*/
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {
Throwable t = null;
try {
// 预处理ServletRequest和ServletResponse
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
// 创建Subject
final Subject subject = createSubject(request, response);
// 创建当前线程执行的任务并执行
//noinspection unchecked
subject.execute(new Callable() {
public Object call() throws Exception {
// 更新Session最近一次访问时间
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;
}
//otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:
String msg = "Filtered request failed.";
throw new ServletException(msg, t);
}
}
/**
* 根据指定的ServletRequest和ServletResponse获取过滤器链
*/
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
FilterChain chain = origChain;
FilterChainResolver resolver = getFilterChainResolver();
if (resolver == null) {
// 未配置FilterChainResolver,则直接返回原始过滤器链
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;
}
/**
* 执行过滤器链
*/
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
throws IOException, ServletException {
// 获取指定ServletRequest绑定的过滤器链
FilterChain chain = getExecutionChain(request, response, origChain);
// 放行,交给下一个过滤器处理
chain.doFilter(request, response);
}
ShiroFilter
简介
Web应用程序基于web.xml
配置Shiro的首要Shiro过滤器;
期望ServletContext
中存在基于web.xml
配置的Shiro WebEnvironment
;
核心方法
/**
* 初始化
* 基于已存在的WebEnvironment实例配置SecurityManager和FilterChainResolver
*/
@Override
public void init() throws Exception {
WebEnvironment env = WebUtils.getRequiredWebEnvironment(getServletContext());
// 设置WebSecurityManager
setSecurityManager(env.getWebSecurityManager());
FilterChainResolver resolver = env.getFilterChainResolver();
if (resolver != null) {
// 设置FilterChainResolver
setFilterChainResolver(resolver);
}
}
AdviceFilter
简介
通过preHandle()
、postHandle()
、afterCompletion()
方法为ServletRequest
启用AOP风格的环绕通知;
核心方法
/**
* 预处理,是否允许过滤器链继续执行
* 模板方法,由子类实现算法细节
*/
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
return true;
}
/**
* 后处理
* 前提是过滤器链执行期间未发生异常
* 模板方法,由子类实现算法细节
*/
protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
}
/**
* 完成后处理
* 不论preHandle返回false还是过滤器链执行期间发生异常都会执行(finally代码块)
* 模板方法,由子类实现算法细节
*/
public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
}
/**
* 执行过滤器链
*/
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception {
// 放行,交给下一个过滤器处理
chain.doFilter(request, response);
}
/**
* 过滤
*/
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
Exception exception = null;
try {
// 预处理,是否继续执行过滤器链
boolean continueChain = preHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]");
}
if (continueChain) {
// 继续执行过滤器链
executeChain(request, response, chain);
}
// 后处理
postHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Successfully invoked postHandle method");
}
} catch (Exception e) {
exception = e;
} finally {
// 清理资源如必要
cleanup(request, response, exception);
}
}
/**
* 清理资源
*/
protected void cleanup(ServletRequest request, ServletResponse response, Exception existing)
throws ServletException, IOException {
Exception exception = existing;
try {
// 完成后处理
afterCompletion(request, response, exception);
if (log.isTraceEnabled()) {
log.trace("Successfully invoked afterCompletion method.");
}
} catch (Exception e) {
if (exception == null) {
exception = e;
} else {
log.debug("afterCompletion implementation threw an exception. This will be ignored to " +
"allow the original source exception to be propagated.", e);
}
}
if (exception != null) {
// 处理异常
if (exception instanceof ServletException) {
throw (ServletException) exception;
} else if (exception instanceof IOException) {
throw (IOException) exception;
} else {
if (log.isDebugEnabled()) {
String msg = "Filter execution resulted in an unexpected Exception " +
"(not IOException or ServletException as the Filter API recommends). " +
"Wrapping in ServletException and propagating.";
log.debug(msg);
}
throw new ServletException(exception);
}
}
}
LogoutFilter
简介
登出过滤器,收到请求后,立即注销当前Subject
,然后重定向到配置的redirectUrl
;
核心方法
// 默认的重定向URL
public static final String DEFAULT_REDIRECT_URL = "/";
// 重定向URL
private String redirectUrl = DEFAULT_REDIRECT_URL;
// 仅支持POST请求标识
private boolean postOnlyLogout = false;
/**
* 预处理
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
// 获取当前Subject
Subject subject = getSubject(request, response);
// Check if POST only logout is enabled
if (isPostOnlyLogout()) {
// 校验请求方式
// check if the current request's method is a POST, if not redirect
if (!WebUtils.toHttp(request).getMethod().toUpperCase(Locale.ENGLISH).equals("POST")) {
return onLogoutRequestNotAPost(request, response);
}
}
String redirectUrl = getRedirectUrl(request, response, subject);
//try/catch added for SHIRO-298:
try {
subject.logout();
} catch (SessionException ise) {
log.debug("Encountered session exception during logout. This can generally safely be ignored.", ise);
}
issueRedirect(request, response, redirectUrl);
// 登出后不应进行进一步的交互
return false;
}
/**
* 获取当前Subject
*/
protected Subject getSubject(ServletRequest request, ServletResponse response) {
// 直接返回SecurityUtils.getSubject()
// 子类可重写获取逻辑
return SecurityUtils.getSubject();
}
/**
* 重定向到指定URL
*/
protected void issueRedirect(ServletRequest request, ServletResponse response, String redirectUrl) throws Exception {
WebUtils.issueRedirect(request, response, redirectUrl);
}
/**
* 获取重定向URL
*/
protected String getRedirectUrl(ServletRequest request, ServletResponse response, Subject subject) {
// 直接返回redirectUrl
// 子类可重写获取逻辑
return getRedirectUrl();
}
/**
* 登出请求非POST方式处理
*/
protected boolean onLogoutRequestNotAPost(ServletRequest request, ServletResponse response) {
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
// 响应405
httpServletResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
httpServletResponse.setHeader("Allow", "POST");
// 不进行进一步的交互
return false;
}
PathMatchingFilter
简介
支持路径匹配的基础抽象过滤器;
仅拦截指定的路径,其它路径直接放行;
核心方法
// 默认路径分隔符
private static final String DEFAULT_PATH_SEPARATOR = "/";
// 正则匹配器
protected PatternMatcher pathMatcher = new AntPathMatcher();
// 需要处理的路径集合
protected Map<String, Object> appliedPaths = new LinkedHashMap<String, Object>();
/**
* 处理路径配置
*/
public Filter processPathConfig(String path, String config) {
String[] values = null;
if (config != null) {
values = split(config);
}
// 保存路径配置
this.appliedPaths.put(path, values);
return this;
}
/**
* 获取应用程序内的上下文路径
*/
protected String getPathWithinApplication(ServletRequest request) {
// 子类可重写获取逻辑
return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
}
/**
* 路径匹配
*/
protected boolean pathsMatch(String path, ServletRequest request) {
// 获取应用程序内的上下文路径
String requestURI = getPathWithinApplication(request);
log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, Encode.forHtml(requestURI));
boolean match = pathsMatch(path, requestURI);
if (!match) {
// 未匹配,则去除路径末尾的路径分隔符后再进行一次路径匹配
if (requestURI != null && !DEFAULT_PATH_SEPARATOR.equals(requestURI)
&& requestURI.endsWith(DEFAULT_PATH_SEPARATOR)) {
requestURI = requestURI.substring(0, requestURI.length() - 1);
}
if (path != null && !DEFAULT_PATH_SEPARATOR.equals(path)
&& path.endsWith(DEFAULT_PATH_SEPARATOR)) {
path = path.substring(0, path.length() - 1);
}
log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, Encode.forHtml(requestURI));
match = pathsMatch(path, requestURI);
}
return match;
}
/**
* 路径匹配
*/
protected boolean pathsMatch(String pattern, String path) {
boolean matches = pathMatcher.matches(pattern, path);
log.trace("Pattern [{}] matches path [{}] => [{}]", pattern, path, matches);
return matches;
}
/**
* 预处理
*/
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
// 无配置路径处理,直接放行
if (log.isTraceEnabled()) {
log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately.");
}
return true;
}
// 遍历配置需要处理的路径集合
for (String path : this.appliedPaths.keySet()) {
// If the path does match, then pass on to the subclass implementation for specific checks
//(first match 'wins'):
if (pathsMatch(path, request)) {
// 路径匹配上则根据配置参数进行处理
log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path);
// 获取配置参数
Object config = this.appliedPaths.get(path);
return isFilterChainContinued(request, response, path, config);
}
}
//no path matched, allow the request to go through:
return true;
}
/**
* 是否继续执行过滤器链
* 模板方法,定义算法骨架,由子类实现算法细节
*/
private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
String path, Object pathConfig) throws Exception {
if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
if (log.isTraceEnabled()) {
log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}]. " +
"Delegating to subclass implementation for 'onPreHandle' check.",
new Object[]{getName(), path, pathConfig});
}
//The filter is enabled for this specific request, so delegate to subclass implementations
//so they can decide if the request should continue through the chain or not:
return onPreHandle(request, response, pathConfig);
}
if (log.isTraceEnabled()) {
log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}]. " +
"The next element in the FilterChain will be called immediately.",
new Object[]{getName(), path, pathConfig});
}
//This filter is disabled for this specific request,
//return 'true' immediately to indicate that the filter will not process the request
//and let the request/response to continue through the filter chain:
return true;
}
/**
* 预处理
* 算法细节,由子类实现
*/
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// 默认返回true
// 由子类实现定制化逻辑
return true;
}
/**
* 是否需要执行过滤逻辑
*/
protected boolean isEnabled(ServletRequest request, ServletResponse response, String path, Object mappedValue)
throws Exception {
return isEnabled(request, response);
}
NoSessionCreationFilter
简介
禁止在请求期间创建新会话的PathMatchingFilter
;
可放置在任何导致不打算参与会话的服务的过滤器链前;
本过滤器可以保证以下行为:
- 如果调用本过滤器时,当前
Subject
还没有会话,那么在请求期间如果调用subject.getSession()
和subject.getSession(true)
就会抛出异常; - 如果调用本过滤器时,当前
Subject
已经有会话,本过滤器将无任何效果;
核心方法
/**
* 预处理
*/
@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// 禁止创建新会话
request.setAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED, Boolean.FALSE);
return true;
}
AnonymousFilter
简介
支持立即访问路径而不执行任何类型的安全检查的过滤器;
核心方法
/**
* 预处理
*/
@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {
// 始终返回true,不对路径做任何安全检查
// Always return true since we allow access to anyone
return true;
}
AccessControlFilter
简介
控制资源访问并支持重定向到登录页面的超类过滤器;
提供saveRequestAndRedirectToLogin(ServletRequest, ServletResponse)
方法供子类调用;
核心方法
// 默认登录URL
public static final String DEFAULT_LOGIN_URL = "/login.jsp";
// 登录URL
private String loginUrl = DEFAULT_LOGIN_URL;
/**
* 是否允许指定请求访问
* 算法细节,由子类实现
*/
protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;
/**
* 请求被拒绝处理
*/
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return onAccessDenied(request, response);
}
/**
* 请求被拒绝处理
* 算法细节,由子类实现
*/
protected abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;
/**
* 预处理
*/
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// 是否允许指定请求访问
return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}
/**
* 是否是登录请求
*/
protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
return pathsMatch(getLoginUrl(), request);
}
/**
* 保存当前请求并重定向到登录页面
*/
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
// 保存当前请求
saveRequest(request);
// 重定向到登录页面
redirectToLogin(request, response);
}
/**
* 保存当前请求
*/
protected void saveRequest(ServletRequest request) {
WebUtils.saveRequest(request);
}
/**
* 重定向到登录页面
*/
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
String loginUrl = getLoginUrl();
WebUtils.issueRedirect(request, response, loginUrl);
}
UserFilter
简介
基于用户的资源访问控制的过滤器;
如果当前用户已知,不论是已登录,或者被记住,则允许访问;否则重定向到登录页面;
核心方法
/**
* 是否允许指定请求访问
*/
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginRequest(request, response)) {
// 如果是登录请求直接放行
return true;
} else {
Subject subject = getSubject(request, response);
// 当前用户是否存在,不论是鉴权过的还是记住的用户
// If principal is not null, then the user is known and should be allowed access.
return subject.getPrincipal() != null;
}
}
/**
* 请求被拒绝处理
*/
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// 保存当前请求并重定向到登录页面
saveRequestAndRedirectToLogin(request, response);
return false;
}
InvalidRequestFilter
简介
拦截恶意请求的过滤器,恶意请求会返回400;
核心方法
/**
* 是否允许指定请求访问
*/
@Override
protected boolean isAccessAllowed(ServletRequest req, ServletResponse response, Object mappedValue) throws Exception {
HttpServletRequest request = WebUtils.toHttp(req);
// check the original and decoded values
return isValid(request.getRequestURI()) // user request string (not decoded)
&& isValid(request.getServletPath()) // decoded servlet part
&& isValid(request.getPathInfo()); // decoded path info (may be null)
}
/**
* 请求是否有效
*/
private boolean isValid(String uri) {
return !StringUtils.hasText(uri)
|| ( !containsSemicolon(uri)
&& !containsBackslash(uri)
&& !containsNonAsciiCharacters(uri));
}
/**
* 请求被拒绝时处理
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
WebUtils.toHttp(response).sendError(400, "Invalid request");
return false;
}
AuthenticationFilter
简介
要求鉴权当前用户的基础抽象过滤器;
该类封装了检查用户是否已鉴权的逻辑,子类需要实现对未鉴权的请求执行特定逻辑;
核心方法
// 默认鉴权成功跳转URL
public static final String DEFAULT_SUCCESS_URL = "/";
// 鉴权成功跳转URL
private String successUrl = DEFAULT_SUCCESS_URL;
/**
* 是否允许指定请求访问
*/
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = getSubject(request, response);
// 当前用户是否已鉴权
return subject.isAuthenticated();
}
/**
* 登录成功后重定向到之前的请求路径
*/
protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {
WebUtils.redirectToSavedRequest(request, response, getSuccessUrl());
}
PassThruAuthenticationFilter
简介
放行登录请求的鉴权过滤器;
如果自定义MVC登录控制器或验证器,则该过滤器可能是合适的;
核心方法
/**
* 请求被拒绝处理
*/
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response)) {
// 如果是登录请求直接放行
return true;
} else {
// 否则保存当前请求并重定向到登录页面
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
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);
}
}
/**
* 创建AuthenticationToken
* 算法细节,由子类实现
*/
protected abstract AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception;
/**
* 创建AuthenticationToken
*/
protected AuthenticationToken createToken(String username, String password,
ServletRequest request, ServletResponse response) {
boolean rememberMe = isRememberMe(request);
String host = getHost(request);
return createToken(username, password, rememberMe, host);
}
/**
* 创建AuthenticationToken
*/
protected AuthenticationToken createToken(String username, String password,
boolean rememberMe, String host) {
return new UsernamePasswordToken(username, password, rememberMe, host);
}
/**
* 登录成功处理
* 算法细节,由子类实现
*/
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
ServletRequest request, ServletResponse response) throws Exception {
return true;
}
/**
* 登录失败处理
* 算法细节,由子类实现
*/
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
ServletRequest request, ServletResponse response) {
return false;
}
/**
* 获取Subject的host信息
*/
protected String getHost(ServletRequest request) {
return request.getRemoteHost();
}
/**
* 是否使能RememberMe服务
* 算法细节,由子类实现
*/
protected boolean isRememberMe(ServletRequest request) {
return false;
}
/**
* 是否允许指定请求访问
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
// 当前用户是否已鉴权,或者非登录请求是否允许放行
return super.isAccessAllowed(request, response, mappedValue) ||
(!isLoginRequest(request, response) && isPermissive(mappedValue));
}
/**
* 是否允许放行
*/
protected boolean isPermissive(Object mappedValue) {
if(mappedValue != null) {
String[] values = (String[]) mappedValue;
return Arrays.binarySearch(values, PERMISSIVE) >= 0;
}
return false;
}
/**
* 清理资源
*/
@Override
protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) throws ServletException, IOException {
if (existing instanceof UnauthenticatedException || (existing instanceof ServletException && existing.getCause() instanceof UnauthenticatedException))
{
// 处理鉴权失败
try {
// 拒绝访问处理
onAccessDenied(request, response);
existing = null;
} catch (Exception e) {
existing = e;
}
}
// 调用父类方法完成资源清理
super.cleanup(request, response, existing);
}
FormAuthenticationFilter
简介
要求请求用户已被鉴权的过滤器;
如果是登录请求,则进一步判断是否是登陆提交请求,是则进行登陆操作,否则直接放行;非登录请求则重定向到配置的登录页面,强制用户完成登录后才可以继续访问资源;
核心方法
public static final String DEFAULT_ERROR_KEY_ATTRIBUTE_NAME = "shiroLoginFailure";
public static final String DEFAULT_USERNAME_PARAM = "username";
public static final String DEFAULT_PASSWORD_PARAM = "password";
public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe";
// 请求中用户名信息属性
private String usernameParam = DEFAULT_USERNAME_PARAM;
// 请求中密码信息属性
private String passwordParam = DEFAULT_PASSWORD_PARAM;
// 请求中RememberMe信息属性
private String rememberMeParam = DEFAULT_REMEMBER_ME_PARAM;
private String failureKeyAttribute = DEFAULT_ERROR_KEY_ATTRIBUTE_NAME;
/**
* 构造方法
*/
public FormAuthenticationFilter() {
setLoginUrl(DEFAULT_LOGIN_URL);
}
/**
* 设置登录路径
*/
@Override
public void setLoginUrl(String loginUrl) {
String previous = getLoginUrl();
if (previous != null) {
// 清除原先登录路径的配置参数
this.appliedPaths.remove(previous);
}
super.setLoginUrl(loginUrl);
if (log.isTraceEnabled()) {
log.trace("Adding login url to applied paths.");
}
// 添加新登录路径的配置参数为空
this.appliedPaths.put(getLoginUrl(), null);
}
/**
* 请求被拒绝处理
*/
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;
}
}
/**
* 是否是登录提交请求
*/
protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {
return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD);
}
/**
* 创建AuthenticationToken
*/
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
return createToken(username, password, request, response);
}
/**
* 获取指定请求中的RememberMe信息
*/
protected boolean isRememberMe(ServletRequest request) {
return WebUtils.isTrue(request, getRememberMeParam());
}
/**
* 登录成功处理
*/
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
ServletRequest request, ServletResponse response) throws Exception {
// 跳转到原先请求路径
issueSuccessRedirect(request, response);
// 本过滤器完成处理,不再进行下一步过滤处理
//we handled the success redirect directly, prevent the chain from continuing:
return false;
}
/**
* 登录失败处理
*/
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
ServletRequest request, ServletResponse response) {
if (log.isDebugEnabled()) {
log.debug( "Authentication exception", e );
}
// 设置失败属性
setFailureAttribute(request, e);
//login failed, let request continue back to the login page:
return true;
}
/**
* 设置失败属性
*/
protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
String className = ae.getClass().getName();
request.setAttribute(getFailureKeyAttribute(), className);
}
/**
* 获取指定请求中的用户名信息
*/
protected String getUsername(ServletRequest request) {
return WebUtils.getCleanParam(request, getUsernameParam());
}
/**
* 获取指定请求中的密码信息
*/
protected String getPassword(ServletRequest request) {
return WebUtils.getCleanParam(request, getPasswordParam());
}
HttpAuthenticationFilter
简介
支持只拦截配置的HTTP Method,未配置则默认拦截;根据HTTP Authorization
请求头判断是否是登录请求,如果是则执行登录;登录失败或者非登录请求则返回401;
核心方法
// 授权请求头字段
protected static final String AUTHORIZATION_HEADER = "Authorization";
// 鉴权请求头字段
protected static final String AUTHENTICATE_HEADER = "WWW-Authenticate";
// 应用程序名称
private String applicationName = "application";
// 查找HTTP WWW-Authenticate 头字段方案
private String authcScheme;
// 查找HTTP Authorization 头字段方案
private String authzScheme;
/**
* 是否允许指定请求访问
*/
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
HttpServletRequest httpRequest = WebUtils.toHttp(request);
String httpMethod = httpRequest.getMethod();
// Check whether the current request's method requires authentication.
// If no methods have been configured, then all of them require auth,
// otherwise only the declared ones need authentication.
// 校验HTTP Method
Set<String> methods = httpMethodsFromOptions((String[])mappedValue);
// 未配置则默认需要鉴权
boolean authcRequired = methods.size() == 0;
for (String m : methods) {
if (httpMethod.toUpperCase(Locale.ENGLISH).equals(m)) { // list of methods is in upper case
// 在配置中则需要鉴权
authcRequired = true;
break;
}
}
if (authcRequired) {
// 调用父类方法进行鉴权
return super.isAccessAllowed(request, response, mappedValue);
}
else {
return true;
}
}
/**
* 将配置参数转换成HTTP Method
*/
private Set<String> httpMethodsFromOptions(String[] options) {
Set<String> methods = new HashSet<String>();
if (options != null) {
for (String option : options) {
// to be backwards compatible with 1.3, we can ONLY check for known args
// ideally we would just validate HTTP methods, but someone could already be using this for webdav
if (!option.equalsIgnoreCase(PERMISSIVE)) {
methods.add(option.toUpperCase(Locale.ENGLISH));
}
}
}
return methods;
}
/**
* 请求被拒绝处理
* 处理 两阶段提交 鉴权协议
*/
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
boolean loggedIn = false; //false by default or we wouldn't be in this method
if (isLoginAttempt(request, response)) {
loggedIn = executeLogin(request, response);
}
if (!loggedIn) {
sendChallenge(request, response);
}
return loggedIn;
}
/**
* 是否是登录尝试
* 默认实现是判断请求头AUTHORIZATION_HEADER字段内容
*/
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
String authzHeader = getAuthzHeader(request);
return authzHeader != null && isLoginAttempt(authzHeader);
}
/**
* 是否是登录请求
*/
@Override
protected final boolean isLoginRequest(ServletRequest request, ServletResponse response) {
return this.isLoginAttempt(request, response);
}
/**
* 获取请求头AUTHORIZATION_HEADER字段内容
*/
protected String getAuthzHeader(ServletRequest request) {
HttpServletRequest httpRequest = WebUtils.toHttp(request);
return httpRequest.getHeader(AUTHORIZATION_HEADER);
}
/**
* 是否是登录尝试
*/
protected boolean isLoginAttempt(String authzHeader) {
//SHIRO-415: use English Locale:
String authzScheme = getAuthzScheme().toLowerCase(Locale.ENGLISH);
return authzHeader.toLowerCase(Locale.ENGLISH).startsWith(authzScheme);
}
/**
* 发送质询
*/
protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
log.debug("Authentication required: sending 401 Authentication challenge response.");
HttpServletResponse httpResponse = WebUtils.toHttp(response);
// 设置401响应码
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
String authcHeader = getAuthcScheme() + " realm=\"" + getApplicationName() + "\"";
// 设置响应头AUTHENTICATE_HEADER字段
httpResponse.setHeader(AUTHENTICATE_HEADER, authcHeader);
return false;
}
/**
* 创建AuthenticationToken
*/
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String authorizationHeader = getAuthzHeader(request);
if (authorizationHeader == null || authorizationHeader.length() == 0) {
// 请求头(AUTHORIZATION_HEADER字段内容为空则构造空AuthenticationToken
// Create an empty authentication token since there is no
// Authorization header.
return createToken("", "", request, response);
}
log.debug("Attempting to execute login with auth header");
// 获取用户名和密码信息
String[] prinCred = getPrincipalsAndCredentials(authorizationHeader, request);
if (prinCred == null || prinCred.length < 2) {
// 仅存在用户名信息
// Create an authentication token with an empty password,
// since one hasn't been provided in the request.
String username = prinCred == null || prinCred.length == 0 ? "" : prinCred[0];
return createToken(username, "", request, response);
}
String username = prinCred[0];
String password = prinCred[1];
// 根据用户名/密码创建UsernamePasswordToken
return createToken(username, password, request, response);
}
/**
* 获取用户名和密码信息
* 模板方法,定义算法骨架,由子类实现算法细节
*/
protected String[] getPrincipalsAndCredentials(String authorizationHeader, ServletRequest request) {
if (authorizationHeader == null) {
return null;
}
String[] authTokens = authorizationHeader.split(" ");
if (authTokens == null || authTokens.length < 2) {
return null;
}
return getPrincipalsAndCredentials(authTokens[0], authTokens[1]);
}
/**
* 获取用户名和密码信息
* 算法细节,由子类实现
*/
abstract String[] getPrincipalsAndCredentials(String scheme, String value);
BasicHttpAuthenticationFilter
简介
基于HTTP Basic协议(使用UsernamePasswordToken
)进行登录尝试;
核心方法
/**
* 构造方法
*/
public BasicHttpAuthenticationFilter() {
// 默认使用BASIC
setAuthcScheme(HttpServletRequest.BASIC_AUTH);
setAuthzScheme(HttpServletRequest.BASIC_AUTH);
}
/**
* 创建AuthenticationToken
*/
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String authorizationHeader = getAuthzHeader(request);
if (authorizationHeader == null || authorizationHeader.length() == 0) {
// 请求头(AUTHORIZATION_HEADER字段内容为空则构造空AuthenticationToken
// Create an empty authentication token since there is no
// Authorization header.
return createToken("", "", request, response);
}
log.debug("Attempting to execute login with auth header");
// 获取用户名和密码信息
String[] prinCred = getPrincipalsAndCredentials(authorizationHeader, request);
if (prinCred == null || prinCred.length < 2) {
// 仅存在用户名信息
// Create an authentication token with an empty password,
// since one hasn't been provided in the request.
String username = prinCred == null || prinCred.length == 0 ? "" : prinCred[0];
return createToken(username, "", request, response);
}
String username = prinCred[0];
String password = prinCred[1];
// 根据用户名/密码创建UsernamePasswordToken
return createToken(username, password, request, response);
}
/**
* 获取用户名和密码信息
*/
protected String[] getPrincipalsAndCredentials(String scheme, String encoded) {
// base64编码用户名和密码
String decoded = Base64.decodeToString(encoded);
return decoded.split(":", 2);
}
BearerHttpAuthenticationFilter
简介
基于HTTP Bearer协议(使用BearerToken
)进行登录尝试;
核心方法
private static final String BEARER = "Bearer";
/**
* 构造方法
*/
public BearerHttpAuthenticationFilter() {
// 默认使用Bearer
setAuthcScheme(BEARER);
setAuthzScheme(BEARER);
}
/**
* 创建AuthenticationToken
*/
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String authorizationHeader = getAuthzHeader(request);
if (authorizationHeader == null || authorizationHeader.length() == 0) {
// 请求头(AUTHORIZATION_HEADER字段内容为空则构造空BearerToken
// Create an empty authentication token since there is no
// Authorization header.
return createBearerToken("", request);
}
log.debug("Attempting to execute login with auth header");
// 获取token信息
String[] prinCred = getPrincipalsAndCredentials(authorizationHeader, request);
if (prinCred == null || prinCred.length < 1) {
// 不存在token信息则构造空BearerToken
// Create an authentication token with an empty password,
// since one hasn't been provided in the request.
return createBearerToken("", request);
}
// 根据token创建BearerToken
String token = prinCred[0] != null ? prinCred[0] : "";
return createBearerToken(token, request);
}
/**
* 获取用户名和密码信息
*/
@Override
protected String[] getPrincipalsAndCredentials(String scheme, String token) {
return new String[] {token};
}
/**
* 创建BearerToken
*/
protected AuthenticationToken createBearerToken(String token, ServletRequest request) {
return new BearerToken(token, request.getRemoteHost());
}
AuthorizationFilter
简介
授权过滤器,实现对未授权请求的后续处理;
子类需要实现isAccessAllowed(ServletRequest, ServletResponse, Object) throws IOException
;
核心方法
// 未授权跳转的路径
private String unauthorizedUrl;
/**
* 请求被拒绝处理
*/
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
Subject subject = getSubject(request, response);
// If the subject isn't identified, redirect to login URL
// 未登录
if (subject.getPrincipal() == null) {
saveRequestAndRedirectToLogin(request, response);
} else {
// If subject is known but not authorized, redirect to the unauthorized URL if there is one
// If no unauthorized URL is specified, just return an unauthorized HTTP status code
String unauthorizedUrl = getUnauthorizedUrl();
//SHIRO-142 - ensure that redirect _or_ error code occurs - both cannot happen due to response commit:
// 存在未授权跳转路径则跳转
if (StringUtils.hasText(unauthorizedUrl)) {
WebUtils.issueRedirect(request, response, unauthorizedUrl);
} else {
WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
return false;
}
RolesAuthorizationFilter
简介
基于角色授权的过滤器;
核心方法
/**
* 是否允许指定请求访问
*/
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
Subject subject = getSubject(request, response);
String[] rolesArray = (String[]) mappedValue;
if (rolesArray == null || rolesArray.length == 0) {
// 没有指定的角色
//no roles specified, so nothing to check - allow access.
return true;
}
Set<String> roles = CollectionUtils.asSet(rolesArray);
// 登录用户是否拥有该角色集合
return subject.hasAllRoles(roles);
}
PermissionsAuthorizationFilter
简介
权限授权过滤器;
如果当前登录用户拥有指定的权限,则放行;没有,则拒绝访问;
核心方法
/**
* 是否允许指定请求访问
*/
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
Subject subject = getSubject(request, response);
String[] perms = (String[]) mappedValue;
boolean isPermitted = true;
if (perms != null && perms.length > 0) {
// 只需要一个权限
if (perms.length == 1) {
if (!subject.isPermitted(perms[0])) {
isPermitted = false;
}
} else {
// 需要多个权限
if (!subject.isPermittedAll(perms)) {
isPermitted = false;
}
}
}
return isPermitted;
}
HttpMethodPermissionFilter
简介
基于权限授权过滤器,将HTTP Method转换成对应的动作并构造新的权限进行访问控制;
核心方法
// 动作标识
private static final String CREATE_ACTION = "create";
private static final String READ_ACTION = "read";
private static final String UPDATE_ACTION = "update";
private static final String DELETE_ACTION = "delete";
/**
* HTTP Method和动作的对应关系
*/
private static enum HttpMethodAction {
DELETE(DELETE_ACTION),
GET(READ_ACTION),
HEAD(READ_ACTION),
MKCOL(CREATE_ACTION), //webdav, but useful here
OPTIONS(READ_ACTION),
POST(CREATE_ACTION),
PUT(UPDATE_ACTION),
TRACE(READ_ACTION);
private final String action;
private HttpMethodAction(String action) {
this.action = action;
}
public String getAction() {
return this.action;
}
}
/**
* 构造方法
*/
public HttpMethodPermissionFilter() {
for (HttpMethodAction methodAction : HttpMethodAction.values()) {
httpMethodActions.put(methodAction.name().toLowerCase(), methodAction.getAction());
}
}
/**
* 获取HTTP Method对应的动作
*/
protected String getHttpMethodAction(String method) {
String lc = method.toLowerCase();
String resolved = getHttpMethodActions().get(lc);
return resolved != null ? resolved : method;
}
/**
* 构造权限数组
*/
protected String[] buildPermissions(String[] configuredPerms, String action) {
// 无配置的权限或无有效动作
if (configuredPerms == null || configuredPerms.length <= 0 || !StringUtils.hasText(action)) {
return configuredPerms;
}
String[] mappedPerms = new String[configuredPerms.length];
// loop and append :action
for (int i = 0; i < configuredPerms.length; i++) {
// 构造动作权限
mappedPerms[i] = configuredPerms[i] + ":" + action;
}
if (log.isTraceEnabled()) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mappedPerms.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(mappedPerms[i]);
}
log.trace("MAPPED '{}' action to permission(s) '{}'", action, sb);
}
return mappedPerms;
}
/**
* 是否允许指定请求访问
*/
@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
String[] perms = (String[]) mappedValue;
// append the http action to the end of the permissions and then back to super
// 获取HTTP Method对应的动作
String action = getHttpMethodAction(request);
// 根据动作构造权限数组
String[] resolvedPerms = buildPermissions(perms, action);
// 判断是否拥有该权限数组
return super.isAccessAllowed(request, response, resolvedPerms);
}
PortFilter
简介
基于端口授权的过滤器;要求请求来自配置的端口号(未配置则默认80),如果不满足,则重定向到新URL(将原URL的端口替换为配置的端口)的过滤器;
核心方法
// 请求默认配置端口
public static final int DEFAULT_HTTP_PORT = 80;
// HTTP协议
public static final String HTTP_SCHEME = "http";
/**
* 获取配置的端口
*/
protected int toPort(Object mappedValue) {
String[] ports = (String[]) mappedValue;
if (ports == null || ports.length == 0) {
// 未配置,则使用默认端口
return getPort();
}
if (ports.length > 1) {
throw new ConfigurationException("PortFilter can only be configured with a single port. You have " +
"configured " + ports.length + ": " + StringUtils.toString(ports));
}
return Integer.parseInt(ports[0]);
}
/**
* 是否允许指定请求访问
*/
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
int requiredPort = toPort(mappedValue);
int requestPort = request.getServerPort();
// 请求的端口号和配置的端口号是否一致
return requiredPort == requestPort;
}
/**
* 获取协议
*/
protected String getScheme(String requestScheme, int port) {
if (port == DEFAULT_HTTP_PORT) {
// HTTP协议
return HTTP_SCHEME;
} else if (port == SslFilter.DEFAULT_HTTPS_PORT) {
// HTTPS协议
return SslFilter.HTTPS_SCHEME;
} else {
return requestScheme;
}
}
/**
* 请求被拒绝处理
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
// 获取配置端口号
//just redirect to the specified port:
int port = toPort(mappedValue);
// 获取协议
String scheme = getScheme(request.getScheme(), port);
// 构造重定向URL
StringBuilder sb = new StringBuilder();
sb.append(scheme).append("://");
sb.append(request.getServerName());
if (port != DEFAULT_HTTP_PORT && port != SslFilter.DEFAULT_HTTPS_PORT) {
sb.append(":");
sb.append(port);
}
if (request instanceof HttpServletRequest) {
sb.append(WebUtils.toHttp(request).getRequestURI());
String query = WebUtils.toHttp(request).getQueryString();
if (query != null) {
sb.append("?").append(query);
}
}
// 重定向到配置端口
WebUtils.issueRedirect(request, response, sb.toString());
return false;
}
SslFilter
简介
基于端口授权的过滤器;要求请求来自配置的端口号(未配置则默认443)并且是安全的,如果不满足,则重定向到新URL(将原URL的端口替换为配置的端口)的过滤器;
核心方法
// 请求默认配置端口
public static final int DEFAULT_HTTPS_PORT = 443;
// HTTPS协议
public static final String HTTPS_SCHEME = "https";
// HTTP Strict Transport Security
private HSTS hsts;
/**
* 构造方法
*/
public SslFilter() {
// 默认配置端口为443
setPort(DEFAULT_HTTPS_PORT);
this.hsts = new HSTS();
}
/**
* 获取协议
*/
@Override
protected String getScheme(String requestScheme, int port) {
if (port == DEFAULT_HTTP_PORT) {
return PortFilter.HTTP_SCHEME;
} else {
return HTTPS_SCHEME;
}
}
/**
* 是否允许指定请求访问
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// 请求来自配置的端口号并且请求是安全的
return super.isAccessAllowed(request, response, mappedValue) && request.isSecure();
}
/**
* 后处理
*/
@Override
protected void postHandle(ServletRequest request, ServletResponse response) {
if (hsts.isEnabled()) {
StringBuilder directives = new StringBuilder(64)
.append("max-age=").append(hsts.getMaxAge());
if (hsts.isIncludeSubDomains()) {
directives.append("; includeSubDomains");
}
HttpServletResponse resp = (HttpServletResponse) response;
resp.addHeader(HSTS.HTTP_HEADER, directives.toString());
}
}
/**
* HTTP Strict Transport Security 帮助类
*/
public class HSTS {
public static final String HTTP_HEADER = "Strict-Transport-Security";
// 默认关闭
public static final boolean DEFAULT_ENABLED = false;
public static final int DEFAULT_MAX_AGE = 31536000; // approx. one year in seconds
public static final boolean DEFAULT_INCLUDE_SUB_DOMAINS = false;
private boolean enabled;
private int maxAge;
private boolean includeSubDomains;
public HSTS() {
this.enabled = DEFAULT_ENABLED;
this.maxAge = DEFAULT_MAX_AGE;
this.includeSubDomains = DEFAULT_INCLUDE_SUB_DOMAINS;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public int getMaxAge() {
return maxAge;
}
public void setMaxAge(int maxAge) {
this.maxAge = maxAge;
}
public boolean isIncludeSubDomains() {
return includeSubDomains;
}
public void setIncludeSubDomains(boolean includeSubDomains) {
this.includeSubDomains = includeSubDomains;
}
}