一、前言

  一下代码以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 中  ,分析发现添加过滤器的方法 ,在此方法设置断点,前端再次发请求

  

 

 

   

  
  

 

 

   过滤器链创建完了之后 会调用 过滤器链,用里面的过滤器循环过滤

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2021-04-23 22:04  JonRain0625  阅读(499)  评论(0编辑  收藏  举报