过滤器
Shiro还提供了过滤器,可以配置我们的过滤规则,过滤规则对顺序是有要求的,短路优先原则,也就是前面的适配成功之后,就不会再适配后面的规则了。
Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能,直接查看DefaultFilter类。
路径如下:org.apache.shiro.web.filter.mgt
public enum DefaultFilter { anon(AnonymousFilter.class), authc(FormAuthenticationFilter.class), authcBasic(BasicHttpAuthenticationFilter.class), logout(LogoutFilter.class), noSessionCreation(NoSessionCreationFilter.class), perms(PermissionsAuthorizationFilter.class), port(PortFilter.class), rest(HttpMethodPermissionFilter.class), roles(RolesAuthorizationFilter.class), ssl(SslFilter.class), user(UserFilter.class); private final Class filterClass; private DefaultFilter(Class filterClass) { this.filterClass = filterClass; } public Filter newInstance() { return (Filter)ClassUtils.newInstance(this.filterClass); } public Class getFilterClass() { return this.filterClass; } public static Map createInstanceMap(FilterConfig config) { Map filters = new LinkedHashMap(values().length); DefaultFilter[] var2 = values(); int var3 = var2.length; for(int var4 = 0; var4 < var3; ++var4) { DefaultFilter defaultFilter = var2[var4]; Filter filter = defaultFilter.newInstance(); if (config != null) { try { filter.init(config); } catch (ServletException var9) { String msg = "Unable to correctly init default filter instance of type " + filter.getClass().getName(); throw new IllegalStateException(msg, var9); } } filters.put(defaultFilter.name(), filter); } return filters; } }AnonymousFilter 匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例/static/**=anon
不需要继承AccessControlFilter,只继承PathMatchingFilter即可
@Override protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) { // Always return true since we allow access to anyone return true; }AuthenticationFilter:其主要行为就是认证的过滤,是否通过判断(isAccessAllowed)中进行认证判断。
// 执行登录的公共方法抽取到了这里 protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { // 创建Token时获取用户名密码的行为抽象化 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); } } protected abstract AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception; 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); } protected AuthenticationToken createToken(String username, String password, boolean rememberMe, String host) { return new UsernamePasswordToken(username, password, rememberMe, host); }FormAuthenticationFilter:其主要行为就是通知拒绝后的处理、登录成功后的行为,登录失败后的行为。基于表单的拦截器;如/**=authc,如果没有登录会跳到相应的登录页面登录
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { HttpServletRequest httpRequest = WebUtils.toHttp(request); String httpMethod = httpRequest.getMethod(); Setmethods = 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; } } private SethttpMethodsFromOptions(String[] options) { Setmethods = new HashSet(); if (options != null) { for (String option : options) { if (!option.equalsIgnoreCase(PERMISSIVE)) { methods.add(option.toUpperCase(Locale.ENGLISH)); } } } return methods; }LogoutFilter 退出过滤器LogoutFilter自
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 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; }PermissionsAuthorizationFilter 其默认是获取pathConfig中字段并进行比对,一般来说我们需要自定义permissons集合。权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例/user/**=perms["user:create"]
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { Subject subject = getSubject(request, response); // 该过滤器只有mappedValue不为空的时候才会生效,形如perms[file:edit,file:delete] 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; }PortFilter 端口拦截器,主要属性port(80):可以通过的端口;示例/test= port[80],如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { int requiredPort = toPort(mappedValue); int requestPort = request.getServerPort(); return requiredPort == requestPort; } /** * 默认过滤80端口的请求 * /some/path/** = port * 过滤8080端口的请求 * /another/path/** = port[8080] **/ 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]); }
@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); 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; } protected String getScheme(String requestScheme, int port) { if (port == DEFAULT_HTTP_PORT) { return HTTP_SCHEME; // 443端口 : https } else if (port == SslFilter.DEFAULT_HTTPS_PORT) { return SslFilter.HTTPS_SCHEME; } else { return requestScheme; } }