一、前言
一下代码以SSO用户登录列子代码。完整代码https://gitee.com/xuxueli0323/xxl-sso
二、使用
2.1 创建过滤器
创建一个过滤器,实现Filter 接口
public class XxlSsoTokenFilter extends HttpServlet implements Filter { private static Logger logger = LoggerFactory.getLogger(XxlSsoTokenFilter.class); private static final AntPathMatcher antPathMatcher = new AntPathMatcher(); private String ssoServer; private String logoutPath; private String excludedPaths; @Override public void init(FilterConfig filterConfig) throws ServletException { ssoServer = filterConfig.getInitParameter(Conf.SSO_SERVER); logoutPath = filterConfig.getInitParameter(Conf.SSO_LOGOUT_PATH); excludedPaths = filterConfig.getInitParameter(Conf.SSO_EXCLUDED_PATHS); logger.info("XxlSsoTokenFilter init."); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; // make url String servletPath = req.getServletPath(); // excluded path check if (excludedPaths!=null && excludedPaths.trim().length()>0) { for (String excludedPath:excludedPaths.split(",")) { String uriPattern = excludedPath.trim(); // 支持ANT表达式 if (antPathMatcher.match(uriPattern, servletPath)) { // excluded path, allow chain.doFilter(request, response); return; } } } // logout filter if (logoutPath!=null && logoutPath.trim().length()>0 && logoutPath.equals(servletPath)) { // logout SsoTokenLoginHelper.logout(req); // response res.setStatus(HttpServletResponse.SC_OK); res.setContentType("application/json;charset=UTF-8"); res.getWriter().println("{\"code\":"+ReturnT.SUCCESS_CODE+", \"msg\":\"\"}"); return; } // login filter XxlSsoUser xxlUser = SsoTokenLoginHelper.loginCheck(req); if (xxlUser == null) { // response res.setStatus(HttpServletResponse.SC_OK); res.setContentType("application/json;charset=UTF-8"); res.getWriter().println("{\"code\":"+Conf.SSO_LOGIN_FAIL_RESULT.getCode()+", \"msg\":\""+ Conf.SSO_LOGIN_FAIL_RESULT.getMsg() +"\"}"); return; } // ser sso user request.setAttribute(Conf.SSO_USER, xxlUser); // already login, allow chain.doFilter(request, response); return; } }
2.2 注册filter
使用java 配置 @Configuration 注解配置 ,通过FilterRegistrationBean ,向spring容器中注入 过滤器。
@Configuration public class XxlSsoConfig implements DisposableBean { @Value("${xxl.sso.server}") private String xxlSsoServer; @Value("${xxl.sso.logout.path}") private String xxlSsoLogoutPath; @Value("${xxl.sso.redis.address}") private String xxlSsoRedisAddress; @Value("${xxl-sso.excluded.paths}") private String xxlSsoExcludedPaths; @Bean public FilterRegistrationBean xxlSsoFilterRegistration() { // xxl-sso, redis init JedisUtil.init(xxlSsoRedisAddress); // xxl-sso, filter init FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setName("XxlSsoWebFilter"); registration.setOrder(1); registration.addUrlPatterns("/*"); registration.setFilter(new XxlSsoTokenFilter()); registration.addInitParameter(Conf.SSO_SERVER, xxlSsoServer); registration.addInitParameter(Conf.SSO_LOGOUT_PATH, xxlSsoLogoutPath); registration.addInitParameter(Conf.SSO_EXCLUDED_PATHS, xxlSsoExcludedPaths); return registration; } @Override public void destroy() throws Exception { // xxl-sso, redis close JedisUtil.close(); } }
三、执行流程
以springboot 为列子,看filter 是如何工作的
3.1 bean的注入
因为filter 以 FilterRegistrationBean 的形式 注入到spring 的容器,首先来看看这个类的结构 ,可以看到这个类实现 ServletContextInitializer 接口
3.2 断点跟踪
在FilterRegistrationBean 类中有个方法getFilter 获取的过滤器,在这里打个断点,看看spring在什么时候会来获取过滤器。
启动容器,进入断点 ,观察栈信息,可以看到是在创建spring容器后创建tomcat 服务进入的断点
然后拿到所有接口实现,调用
看下 FilterRegistrationBean 调用 onStartup 把filter获取注册到servletContext 容器中
最后 封装成 FilterMap放进org.apache.catalina.core.StandardContext#filterMaps
3.3 前端断点
在过滤器中打上断点,前端发起请求,进入断点
找到 ApplicationFilterChain 看到过滤器在 org.apache.catalina.core.ApplicationFilterChain#filters 中 ,分析发现添加过滤器的方法 ,在此方法设置断点,前端再次发请求
过滤器链创建完了之后 会调用 过滤器链,用里面的过滤器循环过滤