SpringSecurity源码分析(二) SpringBoot集成SpringSecurity即Spring安全框架的执行过程

news2025/7/28 2:31:26

    在上一篇文章中我们描述了SpringSecurity的执行过程。我们我们了解到了以下内容

在SpringSecurity框架中有三个非常核心的类和接口,分别是

            1.SecurityFilterChain接口

            2.FilterChainProxy类

            3.DelegatingFilterProxy类

springboot项目中,客户端向Tomcat 服务器发起请求。因为Tomcat的源码不再本次文章的讨论范围之内。以下部分内容复制了其他老师的流程图

四大容器的作用:

Engine:可以处理Tomcat所接收到所有请求,不管这些请求是请求哪个应用或哪个Servlet的。

Host:可以处理某个特定域名的所有请求

Context:可以处理某个应用的所有请求

Wrapper:可以处理某个Servlet的所有请求

在这里插入图片描述

 在上面的流程图中我们从Wrapper包装器开发分析。

该Wrapper类实际上是StandardWrapperValve类。在请求的执行过程中调用了StandardWrapperValve类中的invoke方法。

在该方法内部

    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Initialize local variables we may need
        boolean unavailable = false;
        Throwable throwable = null;
        // This should be a Request attribute...
        long t1=System.currentTimeMillis();
        requestCount.incrementAndGet();
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();

        // Check for the application being marked unavailable
        if (!context.getState().isAvailable()) {
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                           sm.getString("standardContext.isUnavailable"));
            unavailable = true;
        }

        // Check for the servlet being marked unavailable
        if (!unavailable && wrapper.isUnavailable()) {
            container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
                    wrapper.getName()));
            long available = wrapper.getAvailable();
            if ((available > 0L) && (available < Long.MAX_VALUE)) {
                response.setDateHeader("Retry-After", available);
                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                        sm.getString("standardWrapper.isUnavailable",
                                wrapper.getName()));
            } else if (available == Long.MAX_VALUE) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                        sm.getString("standardWrapper.notFound",
                                wrapper.getName()));
            }
            unavailable = true;
        }

        // Allocate a servlet instance to process this request
        try {
            if (!unavailable) {
                //获取servlet,实际上获取到的是DispatcherServlet
                servlet = wrapper.allocate();
            }
        } catch (UnavailableException e) {
            container.getLogger().error(
                    sm.getString("standardWrapper.allocateException",
                            wrapper.getName()), e);
            long available = wrapper.getAvailable();
            if ((available > 0L) && (available < Long.MAX_VALUE)) {
                response.setDateHeader("Retry-After", available);
                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                           sm.getString("standardWrapper.isUnavailable",
                                        wrapper.getName()));
            } else if (available == Long.MAX_VALUE) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                           sm.getString("standardWrapper.notFound",
                                        wrapper.getName()));
            }
        } catch (ServletException e) {
            container.getLogger().error(sm.getString("standardWrapper.allocateException",
                             wrapper.getName()), StandardWrapper.getRootCause(e));
            throwable = e;
            exception(request, response, e);
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            container.getLogger().error(sm.getString("standardWrapper.allocateException",
                             wrapper.getName()), e);
            throwable = e;
            exception(request, response, e);
            servlet = null;
        }

        MessageBytes requestPathMB = request.getRequestPathMB();
        DispatcherType dispatcherType = DispatcherType.REQUEST;
        if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
        request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
        request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
                requestPathMB);
        // Create the filter chain for this request
        //创建请求的过滤器链
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

        // Call the filter chain for this request
        // NOTE: This also calls the servlet's service() method
        try {
            if ((servlet != null) && (filterChain != null)) {
                // Swallow output if needed
                if (context.getSwallowOutput()) {
                    try {
                        SystemLogHandler.startCapture();
                        if (request.isAsyncDispatching()) {
                            request.getAsyncContextInternal().doInternalDispatch();
                        } else {
                            filterChain.doFilter(request.getRequest(),
                                    response.getResponse());
                        }
                    } finally {
                        String log = SystemLogHandler.stopCapture();
                        if (log != null && log.length() > 0) {
                            context.getLogger().info(log);
                        }
                    }
                } else {
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else {
                        filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                    }
                }

            }
        } catch (ClientAbortException e) {
            throwable = e;
            exception(request, response, e);
        } catch (IOException e) {
            container.getLogger().error(sm.getString(
                    "standardWrapper.serviceException", wrapper.getName(),
                    context.getName()), e);
            throwable = e;
            exception(request, response, e);
        } catch (UnavailableException e) {
            container.getLogger().error(sm.getString(
                    "standardWrapper.serviceException", wrapper.getName(),
                    context.getName()), e);
            //            throwable = e;
            //            exception(request, response, e);
            wrapper.unavailable(e);
            long available = wrapper.getAvailable();
            if ((available > 0L) && (available < Long.MAX_VALUE)) {
                response.setDateHeader("Retry-After", available);
                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                           sm.getString("standardWrapper.isUnavailable",
                                        wrapper.getName()));
            } else if (available == Long.MAX_VALUE) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                            sm.getString("standardWrapper.notFound",
                                        wrapper.getName()));
            }
            // Do not save exception in 'throwable', because we
            // do not want to do exception(request, response, e) processing
        } catch (ServletException e) {
            Throwable rootCause = StandardWrapper.getRootCause(e);
            if (!(rootCause instanceof ClientAbortException)) {
                container.getLogger().error(sm.getString(
                        "standardWrapper.serviceExceptionRoot",
                        wrapper.getName(), context.getName(), e.getMessage()),
                        rootCause);
            }
            throwable = e;
            exception(request, response, e);
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            container.getLogger().error(sm.getString(
                    "standardWrapper.serviceException", wrapper.getName(),
                    context.getName()), e);
            throwable = e;
            exception(request, response, e);
        }

        // Release the filter chain (if any) for this request
        if (filterChain != null) {
            filterChain.release();
        }

        // Deallocate the allocated servlet instance
        try {
            if (servlet != null) {
                wrapper.deallocate(servlet);
            }
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            container.getLogger().error(sm.getString("standardWrapper.deallocateException",
                             wrapper.getName()), e);
            if (throwable == null) {
                throwable = e;
                exception(request, response, e);
            }
        }

创建拦截器链

    public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {

        // If there is no servlet to execute, return null
        if (servlet == null)
            return null;

        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

        // Acquire the filter mappings for this Context
        StandardContext context = (StandardContext) wrapper.getParent();
        //重点关注此处,获取在上一篇文章中的filterMaps属性
        FilterMap filterMaps[] = context.findFilterMaps();

        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return (filterChain);

        // Acquire the information we will need to match filter mappings
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }

        String servletName = wrapper.getName();

        // Add the relevant path-mapped filters to this filter chain
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            //获取拦截器配置,重点关注此处
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // Add filters that match on servlet name second
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // Return the completed filter chain
        return filterChain;
    }

 在上面的代码中分析ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());

在该代码中获取filterConfigs属性中的值,该属性中的值实在StandardContext中startInternal方法中调用的filterStart方法赋值进去的,该该方法中循环filterDefs属性(上一篇有交代其赋值过程),

生成ApplicationFilterConfig对象并对filterConfigs赋值,赋值的内容是name = "springSecurityFilterChain",value为ApplicationFilterConfig且其filter属性是DelegatingFilterProxyRegistrationBean类型的对象。

public boolean filterStart() {

        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Starting filters");
        }
        // Instantiate and record a FilterConfig for each defined filter
        boolean ok = true;
        synchronized (filterConfigs) {
            filterConfigs.clear();
            for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
                String name = entry.getKey();
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug(" Starting filter '" + name + "'");
                }
                try {
                    ApplicationFilterConfig filterConfig =
                            new ApplicationFilterConfig(this, entry.getValue());
                    //重点关注此处
                    filterConfigs.put(name, filterConfig);
                } catch (Throwable t) {
                    t = ExceptionUtils.unwrapInvocationTargetException(t);
                    ExceptionUtils.handleThrowable(t);
                    getLogger().error(sm.getString(
                            "standardContext.filterStart", name), t);
                    ok = false;
                }
            }
        }

        return ok;
    }

    ApplicationFilterConfig(Context context, FilterDef filterDef)
            throws ClassCastException, ClassNotFoundException, IllegalAccessException,
            InstantiationException, ServletException, InvocationTargetException, NamingException,
            IllegalArgumentException, NoSuchMethodException, SecurityException {

        super();

        this.context = context;
        this.filterDef = filterDef;
        // Allocate a new filter instance if necessary
        if (filterDef.getFilter() == null) {
            getFilter();
        } else {
            this.filter = filterDef.getFilter();
            getInstanceManager().newInstance(filter);
            initFilter();
        }
    }

通过以下方法filterChain.doFilter (request.getRequest(), response.getResponse());调用拦截器。

    @Override
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

        if( Globals.IS_SECURITY_ENABLED ) {
            final ServletRequest req = request;
            final ServletResponse res = response;
            try {
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedExceptionAction<Void>() {
                        @Override
                        public Void run()
                            throws ServletException, IOException {
                            internalDoFilter(req,res);
                            return null;
                        }
                    }
                );
            } catch( PrivilegedActionException pe) {
                Exception e = pe.getException();
                if (e instanceof ServletException)
                    throw (ServletException) e;
                else if (e instanceof IOException)
                    throw (IOException) e;
                else if (e instanceof RuntimeException)
                    throw (RuntimeException) e;
                else
                    throw new ServletException(e.getMessage(), e);
            }
        } else {
            internalDoFilter(request,response);
        }
    }

 分析其调用的internalDoFilter方法。在该方法中可以看到

1.Filter filter = filterConfig.getFilter(); 获取拦截器

2.filter.doFilter(request, response, this);调用拦截器

3.servlet.service(request, response);执行DispatcherServlet的service方法,真正的请求调用。

在方法1中filterConfig.getFilter()获取到的过滤器,

    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                }
                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                        ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
                } else {
                    filter.doFilter(request, response, this);
                }
            } catch (IOException | ServletException | RuntimeException e) {
                throw e;
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                throw new ServletException(sm.getString("filterChain.filter"), e);
            }
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        try {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(request);
                lastServicedResponse.set(response);
            }

            if (request.isAsyncSupported() && !servletSupportsAsync) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                        Boolean.FALSE);
            }
            // Use potentially wrapped request from this point
            if ((request instanceof HttpServletRequest) &&
                    (response instanceof HttpServletResponse) &&
                    Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();
                Object[] args = new Object[]{req, res};
                SecurityUtil.doAsPrivilege("service",
                                           servlet,
                                           classTypeUsedInService,
                                           args,
                                           principal);
            } else {
                servlet.service(request, response);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.servlet"), e);
        } finally {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(null);
                lastServicedResponse.set(null);
            }
        }
    }

在该方法中通过filterDef.getFilterClass()获取到上一篇中设置的filterClass即springSecurityFilterChain,又通过newInstance的到

    Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException,
            InstantiationException, ServletException, InvocationTargetException, NamingException,
            IllegalArgumentException, NoSuchMethodException, SecurityException {

        // Return the existing filter instance, if any
        if (this.filter != null)
            return (this.filter);

        // Identify the class loader we will be using
        String filterClass = filterDef.getFilterClass();
        this.filter = (Filter) getInstanceManager().newInstance(filterClass);

        initFilter();

        return (this.filter);

    }

在下面的源码中获取FilterChainProxy对象并调用该对象的里的doFIlter方法。

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// Lazily initialize the delegate if necessary.
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			synchronized (this.delegateMonitor) {
				delegateToUse = this.delegate;
				if (delegateToUse == null) {
					WebApplicationContext wac = findWebApplicationContext();
					if (wac == null) {
						throw new IllegalStateException("No WebApplicationContext found: " +
								"no ContextLoaderListener or DispatcherServlet registered?");
					}
					delegateToUse = initDelegate(wac);
				}
				this.delegate = delegateToUse;
			}
		}

		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}

 该段代码通过targetBeanName 获取到bean对象,targetName是springSecurityFilterChain,该beanName对应的类型是FilterChainProxy 。

	protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		String targetBeanName = getTargetBeanName();
		Assert.state(targetBeanName != null, "No target bean name set");
		Filter delegate = wac.getBean(targetBeanName, Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}

调用doFilter方法

	protected void invokeDelegate(
			Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		delegate.doFilter(request, response, filterChain);
	}

 以下是FilterChainProxy类里的doFilter方法。

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
		if (clearContext) {
			try {
				request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
				doFilterInternal(request, response, chain);
			}
			finally {
				SecurityContextHolder.clearContext();
				request.removeAttribute(FILTER_APPLIED);
			}
		}
		else {
            //重点关注此处代码
			doFilterInternal(request, response, chain);
		}
	}
	private void doFilterInternal(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {

		FirewalledRequest fwRequest = firewall
				.getFirewalledRequest((HttpServletRequest) request);
		HttpServletResponse fwResponse = firewall
				.getFirewalledResponse((HttpServletResponse) response);

		List<Filter> filters = getFilters(fwRequest);

		if (filters == null || filters.size() == 0) {
			if (logger.isDebugEnabled()) {
				logger.debug(UrlUtils.buildRequestUrl(fwRequest)
						+ (filters == null ? " has no matching filters"
								: " has an empty filter list"));
			}

			fwRequest.reset();

			chain.doFilter(fwRequest, fwResponse);

			return;
		}

		VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
		vfc.doFilter(fwRequest, fwResponse);
	}

分析上面的doFilterInternal里的源码。

 在List<Filter> filters = getFilters(fwRequest),源码见下面,该方法的作用是

 1.获取到了filterChains属性,该属性是在上文分析在performBuild里的通过FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);方法获取的所有的继承了WebSecurityConfigurerAdapter类的类。

2.循环filterChains属性,通过chain.getFilters()获取到了WebSecurityConfigurerAdapter通过addFilter addFilterBefore addFilterAfter添加的客户自定义的过滤器

	private List<Filter> getFilters(HttpServletRequest request) {
		for (SecurityFilterChain chain : filterChains) {
			if (chain.matches(request)) {
				return chain.getFilters();
			}
		}

		return null;
	}

调用VirtualFilterChain类里的doFilter方法。nextFilter.doFilter(request, response, this);方法调用了下一个过滤器的doFilter方法。这样就重复调用VirtualFilterChain里的doFilter方法直至所有的过滤器方法执行完成。

		public void doFilter(ServletRequest request, ServletResponse response)
				throws IOException, ServletException {
			if (currentPosition == size) {
				if (logger.isDebugEnabled()) {
					logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
							+ " reached end of additional filter chain; proceeding with original chain");
				}

				// Deactivate path stripping as we exit the security filter chain
				this.firewalledRequest.reset();

				originalChain.doFilter(request, response);
			}
			else {
				currentPosition++;

				Filter nextFilter = additionalFilters.get(currentPosition - 1);

				if (logger.isDebugEnabled()) {
					logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
							+ " at position " + currentPosition + " of " + size
							+ " in additional filter chain; firing Filter: '"
							+ nextFilter.getClass().getSimpleName() + "'");
				}

				nextFilter.doFilter(request, response, this);
			}
		}
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/368080.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day51【代码随想录】动态规划之回文子串、最长回文子序列

文章目录前言一、回文子串&#xff08;力扣647&#xff09;二、最长回文子序列&#xff08;力扣516&#xff09;前言 1、回文子串 2、最长回文子序列 一、回文子串&#xff08;力扣647&#xff09; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目…

从网易到支付宝,3年外包生涯做完,我这人生算是彻底废了......

我为什么一直做外包呢&#xff0c;原因是薪资和技术方面。 在华三做了一年外包&#xff0c;薪资5k&#xff0c;功能测试&#xff0c;接触Linux和网络&#xff0c;但是说实在的技术很难沉淀&#xff0c;就像雾里看花一样&#xff0c;过年之后&#xff0c;想走的人都走了&#x…

Word处理控件Aspose.Words功能演示:使用 C# VB.NET 将 DOCX 转换为 DOC 或将 DOC 转换为 DOCX

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

深度学习编译器CINN(1):框架概览和编译安装

目录 框架概览 编译安装 参考 框架概览 CINN是一种在不改变模型代码的条件下加速飞桨模型运行速度的深度学习编译器。CINN致力于创造训推一体自动调优、分布式编译加速等特色功能&#xff0c;对深度学习模型提供全自动、极致的性能优化&#xff0c;并在科研界和工业界建立影…

【布隆过滤器(Bloom Filter)基本概念与原理、Bloom Filter优点与缺点、以及应用场景】

布隆过滤器&#xff08;Bloom Filter&#xff09;基本概念与原理、Bloom Filter优点与缺点、以及应用场景 Bloom Filter 基本概念 布隆过滤器是1970年由一个叫布隆的小伙子提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在…

DualCor: Event Causality Extraction with Event Argument Correlations论文解读

Event Causality Extraction with Event Argument Correlations(带有事件论元相关性的事件因果关系抽取) 论文&#xff1a;2301.11621.pdf (arxiv.org) 代码&#xff1a;cuishiyao96/ECE: Dataset and code for COLING2022 paper Event Causality Extraction with Event Argum…

【vue 10 第一个vue-cli程序】

1.什么是vue-cli vue-cli官方提供的一个脚手架&#xff0c;用于快速生成一个vue的项目模版&#xff1b; 预先定义好的目录结构及基础代码&#xff0c;就好比咱们在创建Maven项目时可以选择创建一个骨架项目&#xff0c;这个骨架项目就是脚手架&#xff0c;我们的开发更加的快速…

FC总线知识点小结

FC总线 &#xff08;一&#xff09;特点&#xff1a; FC具备通道和网络双重优势&#xff0c;具备高带宽、高可靠性、高稳定性&#xff0c;抵抗电磁干扰等优点&#xff0c;能够提供非常稳定可靠的光纤连接&#xff0c;容易构建大型的数据传输和通信网络&#xff0c;目前支持1x…

2022阿里,字节跳动,JAVA岗(一线企业校招、社招)面试题合集

前言 以下面试题全属于一线大厂社招以及校招的面试真题&#xff0c;各位在做这些题目对照自己的时候请平凡心对待&#xff0c;不要信心受挫。其实 做为致力于一线企业校招或者社招的你来说&#xff0c;能把每个知识模块的一小部分问题去深入学习和总结&#xff0c;已经很棒了&…

SpringBoot整合阿里云OSS文件上传、下载、查看、删除

SpringBoot整合阿里云OSS文件上传、下载、查看、删除1、开发准备1.1 前置知识1.2 环境参数1.3 你能学到什么2. 使用阿里云OSS2.1 创建Bucket2.2 管理文件2.3 阿里云OSS文档3. 项目初始化3.1 创建SpringBoot项目3.2 Maven依赖3.3 安装lombok插件4. 后端服务编写4.1 阿里云OSS配置…

Ask林曦|来回答,30个你关心的日常问题(三)

在林曦老师的线上书法直播课上&#xff0c;上课前后的聊天时间里&#xff0c;时常有同学向林曦老师提问&#xff0c;这些问题涵盖了日常生活的诸多方面&#xff0c;从身体的保养&#xff0c;到快乐的法门&#xff0c;皆是大家感兴趣的&#xff0c;也都共同关切的。      暄…

Spring Cloud @RefreshScope 原理分析:扫描 Bean 定义

背景 最近读了一下 spring cloud 的 RefreshScope 生效的源码&#xff0c;总结一下该注解的 refresh 类型的类实例化的过程。 关键技术点&#xff1a; 扫描过程中对 RefreshScope 注解做了特殊处理&#xff0c;会额外注册两个BeanDefinition。GenericScope 实现了 BeanDefin…

医疗影像工具LEADTOOLS 入门教程: 从 PDF 中提取附件 - 控制台 C#

LEADTOOLS 是一个综合工具包的集合&#xff0c;用于将识别、文档、医疗、成像和多媒体技术整合到桌面、服务器、平板电脑、网络和移动解决方案中&#xff0c;是一项企业级文档自动化解决方案&#xff0c;有捕捉&#xff0c;OCR&#xff0c;OMR&#xff0c;表单识别和处理&#…

ARM平台搭建Python环境

ARM平台搭建Python环境写在最前常见问题1. 主机&#xff08;Ubuntu&#xff09;安装Python3.8.101.1 安装前的准备1.2 Ubuntu安装Python3.8.101.3 Ubuntu配置Python3.8.102. 宿主机&#xff08;AMR&#xff09;安装Python3.8.102.1 主机安装交叉编译工具2.2 交叉编译zlib库2.3 …

简析RE中python的exe文件

0x00. 前置学习 简单了解 python为什么要打包成exe文件&#xff1f;在日常生活中的应用是因为传输源文件以及源代码给他人是需要配置好一定的环境才能进行编译操作&#xff0c;而打包成exe文件就可以跟电脑软件一样打开就可以运行也可以分享给他人。 而对于ctf比赛来说&…

(三十三)大白话MySQL运行时多个事务同时执行是什么场景?

到目前为止&#xff0c;我们已经给大家深入讲解了MySQL的buffer pool机制、redo log机制和undo log机制&#xff0c;相信大家现在对我们平时执行一些增删改语句的实现原理&#xff0c;都有了一定较为深入的理解了&#xff01; 因为平时我们执行增删改的时候&#xff0c;无非就…

Python|Leetcode刷题日寄Part04

Python|Leetcode刷题日寄Part0401&#xff1a;环形链表02&#xff1a;跳跃游戏03&#xff1a;括号生成04&#xff1a;二分查找05&#xff1a;打家劫舍06&#xff1a;搜索旋转排序数组07&#xff1a;Z字形变换08&#xff1a;买卖股票的最佳时机Ⅱ09&#xff1a;最后一个单词的长…

国产蓝牙耳机300左右哪个牌子的好?三百左右蓝牙耳机推荐

现在的蓝牙耳机样式越来越五花八门&#xff0c;各种性能也越来越好。特别是国产蓝牙耳机&#xff0c;近几年的发展十分快速。那么&#xff0c;三百左右哪个牌子的国产蓝牙耳机好&#xff1f;接下来&#xff0c;我来给大家推荐几款三百左右的蓝牙耳机&#xff0c;一起来看看吧。…

Java加密算法:base64,MD5加密,对称加密,非对称加密

目录 Java&#xff1a;密码算法 1、base64加密方式 2、jdk原生api实现MD5 3、使用codec依赖实现MD5加密 4、SHA加密 5、MAC算法加密 6、对称加密 7、非对称加密 Java&#xff1a;密码算法 1、base64加密方式 public class demo {//设置编码格式private static final S…

搜索引擎的6个技巧

今天看了一期seo优化的视频&#xff0c;其中就有这么一篇关于百度搜索的几个小技巧&#xff0c;这里整理出来&#xff0c;分享给大家。不是标题党&#xff0c;真的99%的人都不知道这个6个小技巧。 搜索引擎一般都会有一些高级的搜索技巧&#xff0c;掌握这些技巧之后就可以过滤…