一文上手SpringSecurity【二】

书接上回,我们直接引入了spring security的依赖,之后啥也没有干,在访问接口的时候, 就需要认证之后才能访问了 ,咱们没有主动干啥,那肯定有人帮助我们干啥了,这一切都利益出spring boot自动装配机制,下面咱们就看看spring security的自动装配,帮助我们干啥了.

一、Spring Security自动装配

1.1 回顾一下spring boot的自动装配

spring boot会根据类路径中的jar包、类,为jar包里的类自动配置,这样可以极大的减少配置的数量。也就是说spring boot会根据定义在classpath下的类,自动的给你生成一些Bean,并加载到Spring的Context中。

spring boot通过条件注解的配置决定哪些bean可以被自动装配,这些条件定义成具体的xxxAutoConfiguration, 将xxxAutoConfiguration配置到spring.factories文件当中,最好基于spi机制进行加载.但是需要注意的是在spring boot 2.7.0版本及之后的版本,此种方式已经不被推荐使用了,而是加载:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, 如下图所示:
加载
文件内容:

// 部分内容 .... 
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

其核心注解@Import(AutoConfigurationImportSelector.class)读取类路径下的文件需要自动装配的类, 一般都是以xxxAutoConfiguration结尾, 再加载的时候,结合条件注解,过滤一下哪些个bean需要加载.以WebMvcAutoConfiguration为例进行说明.

当读取到了WebMvcAutoConfiguration这个类,开始加载所对应的bean.
webmvc
条件注解
同样在WebMvcAutoConfiguration类当中,观察一下WebMvcAutoConfigurationAdapter类,如下图所示:
属性配置
属性配置

  • 重要的小总结
    • 读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,加载各种各样的Bean
    • 结合条件注解@Condition,决定加载哪些、不加载哪些Bean
    • 一般自动装配的类都是以xxxAutoConfigufation结尾
    • 一般默认属性配置类都会在xxxAutoConfiguration类当中引入,以xxxProperties结尾

以Redis自动装配为例,再体会一下.目前我们的工程当中没有引入spring-boot-starer-data-redis依赖,但是在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件当中,有它的自动装配类,如下图所示:
redis
打开瞅瞅
redis
有了以上的知识作为铺垫,我们直接来看spring security自动装配.

1.2 spring security自动装配

1.2.1 @Conditional(DefaultWebSecurityCondition.class)注解

按照我们之前说的,一般都会有xxxAutoConfiguration,我们直接搜索一下瞅瞅. 费劲扒拉的搜了半天,发现直接搜索SpringSecurityAutoConfiguration没有,得搜SecurityAutoConfiguration才能找到.

@AutoConfiguration(before = UserDetailsServiceAutoConfiguration.class)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {@Bean@ConditionalOnMissingBean(AuthenticationEventPublisher.class)public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {return new DefaultAuthenticationEventPublisher(publisher);}
}

@EnableConfigurationProperties(SecurityProperties.class),表示启用默认装配的各种属性.
@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class }), 表示引入其它的配置.我们看SpringBootWebSecurityConfiguration.class这个类. 核心代码如下所示:


// Web 安全性的默认配置。它依赖于 Spring Security 的内容协商策略来确定要使用的身份验证类型。
// 如果用户指定了自己的 SecurityFilterChain bean,这将完全回退,
// 用户应该指定他们想要作为自定义安全配置的一部分配置的所有位。
@Configuration(proxyBeanMethods = false)@ConditionalOnDefaultWebSecuritystatic class SecurityFilterChainConfiguration {@Bean@Order(SecurityProperties.BASIC_AUTH_ORDER)SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());http.formLogin(withDefaults());http.httpBasic(withDefaults());return http.build();}}

@ConditionalOnDefaultWebSecurity注解用于条件化地配置 Web 安全相关的组件,只有在满足特定条件时才会生效. 其核心代码如下所示:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(DefaultWebSecurityCondition.class)
public @interface ConditionalOnDefaultWebSecurity {
}

@Conditional(DefaultWebSecurityCondition.class),这个注解告诉 Spring,只有当DefaultWebSecurityCondition中定义的条件满足时,才会应用被注解的配置类、方法或 Bean. 终于到关键代码了:

class DefaultWebSecurityCondition extends AllNestedConditions {DefaultWebSecurityCondition() {super(ConfigurationPhase.REGISTER_BEAN);}@ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })static class Classes {}@ConditionalOnMissingBean({ SecurityFilterChain.class })static class Beans {}
}

下边重点解释一下这 DefaultWebSecurityCondition类. 重点就看这两个静态内部类:

	@ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })static class Classes {}
  • @ConditionalOnClass 是一个 Spring Boot 的条件注解。它的意思是:只有在类路径上存在指定的类(SecurityFilterChain 和 HttpSecurity)时,条件才成立。
  • 这里指定了两个类 SecurityFilterChain 和 HttpSecurity,它们属于 Spring Security 的核心类。只有当这些类可用时,Classes 条件才会被满足。
	@ConditionalOnMissingBean({ SecurityFilterChain.class })static class Beans {}
  • @ConditionalOnMissingBean 也是一个条件注解。它的意思是:如果 Spring 容器中不存在指定的 bean(SecurityFilterChain),条件才会成立。
  • 这个注解确保了当 SecurityFilterChain bean 不存在时,Spring 可以根据此条件加载其他配置。换句话说,它防止了重复创建 SecurityFilterChain 的 bean。

综上所述, 得出结论, 使得DefaultWebSecurityCondition的生效条件有两个:

  1. 类路径中必须存在 SecurityFilterChain 和 HttpSecurity(由 Classes 内部类定义)
  2. pring 容器中不能已经存在 SecurityFilterChain bean(由 Beans 内部类定义)

也就是说,如果我们需要自己定义spring security相关配置,只需要破坏这两个条件当中的一个即可

1.2.2 默认SecurityFilterChain配置

由于现在我们并没有自定义配置,所以现在生效的都是默认配置,回到SpringBootWebSecurityConfiguration类当中,查看生效的默认配置.

@Configuration(proxyBeanMethods = false)@ConditionalOnDefaultWebSecurity // 这个注解生效了,则直接开始加载spring security默认配置static class SecurityFilterChainConfiguration {@Bean@Order(SecurityProperties.BASIC_AUTH_ORDER)SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {// 第1行http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());// 第2行http.formLogin(withDefaults());// 第3行http.httpBasic(withDefaults());return http.build();}}
  • 第1行代码: 配置了请求授权规则, requests.anyRequest().authenticated()表示应用程序的所有请求都需要经过身份认证,也就是说,任何请求都不能匿名访问,必须是已登录的用户。
  • 第2行代码: 启用了默认的表单登录(form-based login), 就是咱们在一文上手SpringSecurity一当中看到的那个表单.
  • 第3行代码:启用了默认的HTTP Basic 认证, 这是一种简单的基于用户名和密码的认证机制,通常用于 API 或没有复杂前端的应用中。
  • 【方法入参】: HttpSecurity是配置 HTTP 安全的重要构建器类, 由spring 提供.
  • 【方法出参】: SecurityFilterChain对象, 该对象用于构建和配置安全过滤器链, 整个spring security由n个过滤器组成的

经过以上的分析,可以得出出下的结论:

  • spring security执行了默认配置
  • 默认配置当中,配置了默认的授权规则、默认的登录表单
  • SecurityFilterChain用于构建过滤器链,整个spring security由多个过滤器链组成
  • HttpSecurity核心的构建器类,spring security各种功能都是由它来配置

那么我进一步探讨一个,默认表单是如何生成的,默认的用户名称和密码又是如何创建的呢?

二、默认流程处理

2.1 默认登录页面流程

我们之前看到的SecurityFilterChain接口,它表示过滤器链, 当请求到达服务器的时候,会经过过滤器链,看一眼接口定义:

public interface SecurityFilterChain {// 匹配请求boolean matches(HttpServletRequest request);// 用户存储Filter,即javaee当中的过滤器对象List<Filter> getFilters();
}

其实现类:

public final class DefaultSecurityFilterChain implements SecurityFilterChain {private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);private final RequestMatcher requestMatcher;private final List<Filter> filters;// ...

根据上边说的自动装配,应用程序启动的时候,会自动创建SecurityFilterChain对象,将其放到容器当中,所以我们可以在应用程序的入口,从容器当中取出List,查看一下到底有哪些过滤器.

public class RjSpringSecurityDemo01Application {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(RjSpringSecurityDemo01Application.class, args);DefaultSecurityFilterChain filterChain = context.getBean(DefaultSecurityFilterChain.class);List<Filter> filters = filterChain.getFilters();for (Filter filter : filters) {System.out.println(filter.getClass().getName());}}
}

日志内容如下所示

org.springframework.security.web.session.DisableEncodeUrlFilter
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.SecurityContextHolderFilter
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.authentication.www.BasicAuthenticationFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
org.springframework.security.web.access.ExceptionTranslationFilter
org.springframework.security.web.access.intercept.AuthorizationFilter

稍微看一眼,上边输出的过滤器,这些过滤器共同组成了一个过滤器链,请求打到服务器的时候,依次经过这些过滤器的处理. 从多的过滤器当中,咱们光从字面意思来看,就找咱们认识的,应该能找到:

  • LogoutFilter, 登出过滤器
  • UsernamePasswordAuthenticationFilter, 用户名称和密码认证过滤器
  • DefaultLoginPageGeneratingFilter, 默认的登录页面生成过滤器,【哎哟可以哦,好像就是它啊】
  • DefaultLogoutPageGeneratingFilter, 默认的登出页面生成过滤器
  • ExceptionTranslationFilter, 异常处理过滤器
  • AuthorizationFilter, 授权过滤器

处理流程:

  • 当我们请求/hello接口的时候,由于我们引入了spring security,所以当前的这个请求会经过一个又一个的过滤器
  • 当请求到达AuthorizationFilter过滤器的时候,发现该请求并未认证.请求会被拦截下来,并且抛出AccessDeniedExceptin异常.
  • 当抛出异常之后会被过滤器ExceptionTranslationFilter捕获,这个Filter中会调用LoginUrlAuthenticationEntryPoint#commence方法给客户端返回302,要求客户端重写向到/login页面
  • 客户端发送请求到/login
  • /login会再次被拦截器DefaultLoginPageGeneratingFilter拦截到,并且在该拦截器当中生成登录在页面.

核心源码:
DefaultLoginPageGeneratingFilter过滤器当中

public class DefaultLoginPageGeneratingFilter extends GenericFilterBean {public static final String DEFAULT_LOGIN_PAGE_URL = "/login";// ...
}

doFilter方法

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {boolean loginError = isErrorPage(request);boolean logoutSuccess = isLogoutSuccess(request);if (isLoginUrlRequest(request) || loginError || logoutSuccess) {// 生成默认的登录页面String loginPageHtml = generateLoginPageHtml(request, loginError, logoutSuccess);response.setContentType("text/html;charset=UTF-8");response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);response.getWriter().write(loginPageHtml);return;}chain.doFilter(request, response);}

generateLoginPageHtml, 生成页面方法

private String generateLoginPageHtml(HttpServletRequest request, boolean loginError, boolean logoutSuccess) {String errorMsg = loginError ? getLoginErrorMessage(request) : "Invalid credentials";String contextPath = request.getContextPath();StringBuilder sb = new StringBuilder();sb.append("<!DOCTYPE html>\n");sb.append("<html lang=\"en\">\n");sb.append("  <head>\n");sb.append("    <meta charset=\"utf-8\">\n");sb.append("    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n");sb.append("    <meta name=\"description\" content=\"\">\n");sb.append("    <meta name=\"author\" content=\"\">\n");sb.append("    <title>Please sign in</title>\n");sb.append("    <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" "+ "rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n");sb.append("    <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" "+ "rel=\"stylesheet\" integrity=\"sha384-oOE/3m0LUMPub4kaC09mrdEhIc+e3exm4xOGxAmuFXhBNF4hcg/6MiAXAf5p0P56\" crossorigin=\"anonymous\"/>\n");sb.append("  <// ... 其它的略,自己去源码查看即可
}

2.2 默认认证流程分析

从自动装配的类,找到登录表单的默认配置

@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());http.formLogin(withDefaults()); // 默认表单配置http.httpBasic(withDefaults());return http.build();
}

点进方法

public HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) throws Exception {formLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));return HttpSecurity.this;
}

FormLoginConfigurer<>()方法

public FormLoginConfigurer() {// 调用父类过滤器super(new UsernamePasswordAuthenticationFilter(), null);usernameParameter("username");passwordParameter("password");
}

进入UsernamePasswordAuthenticationFilter(),找到UsernamePasswordAuthenticationFilter#构造方法

public UsernamePasswordAuthenticationFilter() {super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
}

得到常量值:

// 表示登录的请求地址和请求姿势
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login","POST");

构造方法内部调用了父类的构造方法,进入到UsernamePasswordAuthenticationFilter父类AbstractAuthenticationProcessingFilter当中,由于是过滤器,实现了接口Filter,所以必然会执行doFilter方法

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {// 调用本类的成员方法doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
}

UsernamePasswordAuthenticationFilte#当前类的doFiler()

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {if (!requiresAuthentication(request, response)) {chain.doFilter(request, response);return;}try {// 在AbstractAuthenticationProcessingFilter类定义的【模板方法】attemptAuthentication(), 尝试认证, 交给它的子类UsernamePasswordAuthenticationFilte去实现.Authentication authenticationResult = attemptAuthentication(request, response);// 略....
}

AbstractAuthenticationProcessingFilter类定义的【模板方法】attemptAuthentication()

public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException, IOException, ServletException;

UsernamePasswordAuthenticationFilte实现父类AbstractAuthenticationProcessingFilter定义的模板方法attemptAuthentication(),【这就到了认证的核心逻辑】

	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException {if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}// 取出用户名称和密码String username = obtainUsername(request);username = (username != null) ? username.trim() : "";String password = obtainPassword(request);password = (password != null) ? password : "";// 将用户名称和密码封装为UsernamePasswordAuthenticationToken对象UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,password);// Allow subclasses to set the "details" propertysetDetails(request, authRequest);// 认证管理器调用认证方法,即去验证我们输入的用户名称和密码是否正确这件事return this.getAuthenticationManager().authenticate(authRequest);}

this.getAuthenticationManager().authenticate(authRequest)这一块比较复杂,包括两个内容:

  • this.getAuthenticationManager(),返回认证管理器对象AuthenticationManager
  • authenticate(), 接口AuthenticationManager定义认证方法
// 认证管理器接口
public interface AuthenticationManager {// 【核心认证方法】, 可以发现,此处的入参跟出参都是Authentication接口,如果认证失败,抛出认证异常,如果认证成功,则//  返回一个完整的Authentication对象.Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

AuthenticationManager接口的实现类有
4
那么问题来了,接口不能实例化,那么此时this.getAuthenticationManager()指向了它的哪个实现类呢?这里如果有兴趣的伙伴,可以去自己debug一下,我直接说了哈: 使用的是: ProviderManager这个实现类,通过ProviderManager实现类的对象去调用认证方法.

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {// ...
}

ProviderManager当中重写接口AuthenticationManager的authenticate()方法【核心方法实现】

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {// 略去了其它代码....for (AuthenticationProvider provider : getProviders()) {try {// 核心方法result = provider.authenticate(authentication);if (result != null) {copyDetails(authentication, result);break;}}}
}

AuthenticationProvider接口表示认证提供的接口,它的定义和AuthenticationManager一样

public interface AuthenticationProvider {// 认证方法和认证管理器AuthenticationManager一毛一样Authentication authenticate(Authentication authentication) throws AuthenticationException;boolean supports(Class<?> authentication);
}

11

AuthenticationProvider是一个接口,不能直接使用,必须在运行时找它一个它的实现类,然后调用认证方法,默认指向的实现类是DaoAuthenticationProvider,由它实现在AuthenticationProvider接口定义的认证方法, 但是你查看它的类定义如下所示:

// 实现类并没有直接实现接口,而在父类当中实现的接口,在此类查找authenticate()的实现发现向了父类,在父当中实现的抽象方法
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {// ...
}

66
AbstractUserDetailsAuthenticationProvider

// 实现了接口AuthenticationProvider
public abstract class AbstractUserDetailsAuthenticationProviderimplements AuthenticationProvider, InitializingBean, MessageSourceAware {// ...
}

AbstractUserDetailsAuthenticationProvider#authenticate()核心逻辑

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {String username = determineUsername(authentication);boolean cacheWasUsed = true;UserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {// 这里也是使用了【模板】方法,在当前类AbstractUserDetailsAuthenticationProvider定义的模板方法,具体功能// 由子类DaoAuthenticationProvider去实现user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);}
}

AbstractUserDetailsAuthenticationProvider#retrieveUser()模板方法定义

// 检索用户
protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)throws AuthenticationException;

AbstractUserDetailsAuthenticationProvider子类DaoAuthenticationProvider实现父类定义的模板方法retrieveUser
1

@Override
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {prepareTimingAttackProtection();try {// 核心代码// 根据用户名称去数据源当中查找出用户信息,并将用户对象封装为UserDetails UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);if (loadedUser == null) {throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");}return loadedUser;}

this.getUserDetailsService().loadUserByUsername(username);

  • this.getUserDetailsService(), 返回接口UserDetailsService, 即用户详情服务.

接口定义

public interface UserDetailsService {// 根据用户名,去数据源当中查询出用户信息,并将认证的用户信息封装成UserDetails对象// 返回值: UserDetails是一个接口,主要包含三个东西: 用户名称、密码、权限列表UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

3

this.getUserDetailsService()在运行时,返回一个实现了UserDetailsService接口的实现类的对象.这里返回的是: InMemoryUserDetailsManager

public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {// ...
}

InMemoryUserDetailsManager#loadUserByUsername()

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 根据用户名称,取出用户信息,并且封装成了UserDetails对象UserDetails user = this.users.get(username.toLowerCase());if (user == null) {throw new UsernameNotFoundException(username);}// User是个实现了接口UserDetails的实现类return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
}

最返回信息UserDetails .

至此认证流程基本分析完毕.剩下的是验证用户名称和密码的过程、存储缓存等步骤了.下篇再进行分析.
大家初看一大堆接口和实现类,还有各种模板方法,不必慌张,整个流程还是比较清晰的,建议先跟着文章,自己动手实践一下.多看几次也就记住了.这玩意没有捷径,我亦无他,惟手熟尔矣.

三、总结

3.1 重点内容总结

  • spring boot 3.3.4 自动装配核心内容
  • spring security 过滤器链
  • spring security自动装配
  • spring security默认流程处理之默认的登录页面处理流程
  • spring security 认证流程分析

3.2 下篇内容

  • spring security认证流程图解
  • 默认密码生成策略
  • 自定义认证页面和密码

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

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

相关文章

如何查看上网记录及上网时间?5种按步操作的方法分享!【小白也能学会!】

“知己知彼&#xff0c;百战不殆”&#xff0c;在数字时代&#xff0c;了解自己的上网行为和时长&#xff0c;不仅能帮助我们更好地管理时间&#xff0c;还能提升工作效率和生活质量。 今天&#xff0c;我们就来分享五种简单易懂的方法&#xff0c;即便是网络小白也能轻松学会…

某系统超级管理员密码重置通用型

故事的起因是意外发现某站点系统存在接口泄露&#xff0c;并且此接口可直接实现超级管理员密码重置&#xff0c;查ico找到用这个系统的站点&#xff0c;发现均存在此漏洞 首先打开系统站点&#xff0c;F12或者鼠标右键检查&#xff0c;然后刷新页面&#xff0c;在网络这里找到…

ECCV`24 | 高保真目标修复新SOTA!复旦智象开源CAT-Diffusion,语义视觉双一致

文章链接&#xff1a;https://arxiv.org/pdf/2409.08260 Github链接&#xff1a;https://github.com/Nnn-s/CATdiffusion 总结速览 解决的问题: 单一U-Net在所有去噪步骤中对齐文本提示和视觉对象不足以生成期望的对象。 扩散模型的复杂采样空间中无法保证对对象生成的可控性…

物流货运托运发货单二联三联打印软件定制 佳易王物流单管理系统操作教程

一、前言 物流货运托运发货单二联三联打印软件定制 佳易王物流单管理系统操作教程 1、软件为绿色免安装版&#xff0c;解压即可使用&#xff0c;已经内置数据库&#xff0c;不需再安装。 2、软件下载可以到本文章最后点击官网卡片下。 二、软件程序教程 1、如图&#xff0c;…

Html 转为 MarkDown

在 RAG 中,通常需要将 HTML 转为 Markdown,有很多第三方 API 都支持 HTML 的转换,本文使用一个代码文档的例子 https://www.joinquant.com/help/api/help#name:Stock,将聚宽 API 转为 Markdown。本文通过两种方式进行实现,使用收费和开源的解决方案。聚宽 API 格式转为 Ma…

crypto-js解密报错malformed utf-8 data

在进行加解密处理时出现这个问题。 但是当在一个完整程序运行环境内加密字符串&#xff0c;解密字符串是没问题的。 当把加密的字符存储到txt文件&#xff0c;在读取解密时出现错误无法解密。 最后&#xff0c;使用res.replace(/\s/g,‘’)正则过滤掉txt文件内的空格就成功了。…

爆火!大模型算法岗 100 道面试题全解析,赶紧收藏!

大模型应该是目前当之无愧的最有影响力的AI技术&#xff0c;它正在革新各个行业&#xff0c;包括自然语言处理、机器翻译、内容创作和客户服务等等&#xff0c;正在成为未来商业环境的重要组成部分。 截至目前大模型已经超过200个&#xff0c;在大模型纵横的时代&#xff0c;不…

【Kubernetes】常见面试题汇总(四十四)

目录 100.什么是容器资源监视&#xff1f; 101.副本集和复制控制器之间有什么区别&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-113 …

mov视频怎么转换成mp4?这几种转换方法值得收藏起来!

mov视频怎么转换成mp4&#xff1f;MOV格式&#xff0c;作为苹果专属的产物&#xff0c;它在非苹果体系下的兼容性常常受限&#xff0c;导致用户可能在非苹果软件平台上遭遇播放难题&#xff0c;甚至无法顺利加载视频内容&#xff0c;而且&#xff0c;MOV格式以其独特的压缩技术…

干货 | 2024制造业数字化现状调查白皮书(免费下载)

导读&#xff1a;在这本白皮书中&#xff0c;我们询问了制造商有关数字化转型的工作情况、2024 年的优先事项和可持续性。研究结果清楚地表明&#xff0c;在数字化方面处于领先地位的制造商转型项目比那些没有规划或刚刚起步的项目实现的价值要大得多。 加入知识星球或关注下方…

青动CRM-E售后V2.0.4

CRM售后管理系统&#xff0c;旨在助力企业销售售后全流程精细化、数字化管理&#xff0c;主要功能&#xff1a;客户、合同、工单、任务、报价、产品、库存、出纳、收费&#xff0c;适用于&#xff1a;服装鞋帽、化妆品、机械机电、家具装潢、建材行业、快销品、母婴用品、办公用…

只需要两步制作GIF动态图,方便快捷,制作动态表情包的利器!

推荐阅读&#xff1a;Python制作进度条&#xff0c;18种方式全网最全&#xff01;&#xff08;不全去你家扫厕所&#xff01;&#xff09; 在日常生活中肯定会接触到gif&#xff0c;例如在写文章的时候&#xff0c;有时需要将自己的代码的运行结果展示出来&#xff0c;如果放一…

05-成神之路_ambari_Ambari实战-013-代码生命周期-metainfo-configFiles详解

1.Redis 集群 metainfo.xml 示例 <?xml version"1.0"?> <metainfo><schemaVersion>2.0</schemaVersion><services><service><!-- Redis 集群服务的基本信息 --><name>REDIS</name><displayName>Redi…

VulnHub-SickOs1.1靶机笔记

SickOs1.1靶机笔记 概述 Vulnhub的靶机sickos1.1 主要练习从互联网上搜取信息的能力&#xff0c;还考察了对代理使用&#xff0c;目录爆破的能力&#xff0c;很不错的靶机 靶机地址&#xff1a; 链接: https://pan.baidu.com/s/1JOTvKbfT-IpcgypcxaCEyQ?pwdytad 提取码: yt…

Kali Linux上安装远程桌面服务VNC

在Kali Linux上安装远程桌面服务VNC&#xff0c;可以按照以下步骤进行&#xff1a; 一、安装VNC Server 更新软件包列表&#xff1a; 在终端中运行以下命令&#xff0c;以确保你的软件包列表是最新的。 sudo apt update在执行更新之前会先验证当前账号密码&#xff0c;输入密码…

一种路径敏感的数据依赖分析算法

Falcon 1.方法1.1.Basic Rule1.2.改进算法1.3.跨函数分析 2.Evaluation2.1.设置2.2.value-flow分析2.3.Thin Slicing2.4.Bug Detection 参考文献 这篇工作发表于PLDI 24&#xff0c;提出了一种context- 以semi-path-sensitive的数据依赖分析算法&#xff0c;解决path-sensitive…

大数据毕业设计选题推荐-广东旅游数据分析系统-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…

【牛Y】3DMAX快速构建低多边形城市建筑和道路插件CityBlocks教程

3DMAX快速构建低多边形城市建筑和道路插件CityBlocks&#xff0c;该插件功能主要分为两部分&#xff1a;一键城市建筑生成和一键城市道路生成。可用于城市配景建模、地图三维建模等使用。内置多种建筑组合方式&#xff0c;可使生成的建筑配景更加丰富、富于变换&#xff01; 【…

C++友元和运算符重载

目录 一. 友元 friend 1.1 概念 1.2 友元函数 1.3 友元类 1.4 友元成员函数 二. 运算符重载 2.1 概念 2.2成员函数运算符重载 2.3 成员函数运算符重载 2.4 特殊运算符重载 2.4.1 赋值运算符重载 2.4.2 类型转换运算符重载 2.5 注意事项 三、std::string 字符串类…

sentinel原理源码分析系列(一)-总述

背景 微服务是目前java主流开发架构&#xff0c;微服务架构技术栈有&#xff0c;服务注册中心&#xff0c;网关&#xff0c;熔断限流&#xff0c;服务同学&#xff0c;配置中心等组件&#xff0c;其中&#xff0c;熔断限流主要3个功能特性&#xff0c;限流&#xff0c;熔断&…