Spring Security自定义认证授权过滤器

自定义认证授权过滤器

  • 自定义认证授权过滤器
  • 1、SpringSecurity内置认证流程
  • 2、自定义Security认证过滤器
    • 2.1 自定义认证过滤器
    • 2.2 定义获取用户详情服务bean
    • 2.3 定义SecurityConfig类
    • 2.4 自定义认证流程测试
  • 3、 基于JWT实现无状态认证
    • 3.1 认证成功响应JWT实现
    • 3.2 SpringSecurity基于Jwt实现认证小结
  • 4、自定义Security授权过滤
    • 4.1 授权流程说明
    • 4.2 授权实现流程
    • 4.3 配置自定义授权过滤器
  • 5、自定义权限拒绝处理
    • 5.1 自定义认证用户权限拒绝处理器
    • 5.2 自定义匿名用户拒绝处理器
  • 6、自定义认证授权整体流程小结

自定义认证授权过滤器

如果对 Spring Security 了解不够请看这篇。 https://blog.csdn.net/m0_62943934/article/details/134815311

需求:

SpringSecurity 内置的认证只能接收 form 表单提交的账户、密码信息。并且内部是使用 HttpServletRequest 的 getParameter 方法获取账户信息的,同时如果需要对需求进行扩展,内置的授权方式就不够看了。

@Nullable
protected String obtainUsername(HttpServletRequest request) {
    return request.getParameter(this.usernameParameter);
}

但是,我们需要清楚现在的大部分项目都是基于前后端分离的架构来进行实现的。而这种情况下前段基本都会使用类似于axios这种发送异步请求,而这时账户信息就不能使用 getParameter 方法获取了。需要使用获取流数据的方式,此时就需要我们自己定义认证授权过滤器

1、SpringSecurity内置认证流程

通过研究SpringSecurity内置基于form表单认证的UsernamePasswordAuthenticationFilter过滤器,我们可以仿照自定义认证过滤器:

在这里插入图片描述

内置认证过滤器的核心流程:

在这里插入图片描述

核心流程梳理如下:

  • 认证过滤器(UsernamePasswordAuthentionFilter)接收 form 表单提交的账户、密码信息,并封装成UsernamePasswordAuthenticationToken认证凭对象;
  • 认证过滤器调用认证管理器AuthenticationManager进行认证处理;
  • 认证管理器通过调用用户详情服务获取用户详情UserDetails;
  • 认证管理器通过密码匹配器PasswordEncoder进行匹配,如果密码一致,则将用户相关的权限信息一并封装到Authentication认证对象中;
  • 认证过滤器将Authentication认证过滤器放到认证上下文,方便请求从上下文获取认证信息;

2、自定义Security认证过滤器

SpringSecurity 内置的认证过滤器是基于post请求且为form表单的方式获取认证数据的,那如何接收前端Json异步提交的数据据实现认证操作呢?

显然,我们可仿照UsernamePasswordAuthentionFilter类自定义一个过滤器并实现认证过滤逻辑;

2.1 自定义认证过滤器

UsernamePasswordAuthentionFilter过滤器继承了模板认证过滤器AbstractAuthenticationProcessingFilter抽象类,我们也可仿照实现:

package com.shen.security.security_test.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author shenyang
 * @version 1.0
 * @Date 2024/3/11 16:29
 * 认证过滤器
 *
 */
public class MyUserNamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public static final String USERNAME_KEY = "username";
    public static final String PASSWORD_KEY = "password";
    /**
     * 自定义构造器,传入登录认证的url地址
     *
     * @param defaultFilterProcessesUrl
     */
    public MyUserNamePasswordAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    /**
     * 尝试去认证的方法
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        //请求必须是post请求 并且请求类型必须是json
        if (!request.getMethod().equalsIgnoreCase("POST")//忽略大小写
                &
                ! MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(request.getContentType())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        //获取post请求ajax的数据流 并且将其数据流中的数据反序列化成Map
        HashMap<String,String> userInfo = new ObjectMapper().readValue(request.getInputStream(), HashMap.class);
        String username = userInfo.get(USERNAME_KEY);
        username = (username != null) ? username : "";
        username = username.trim();
        String password = userInfo.get(PASSWORD_KEY);
        password = password != null ? password : "";
        //组装认证的票据对象 将用户名和密码信息封装到认证票据对象下
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        // Allow subclasses to set the "details" property
		//setDetails(request, authRequest);
        //调用认证管理器认证指定的票据对象   交给认证管理器认证票据对象
        return this.getAuthenticationManager().authenticate(authRequest);
    }


    public MyUserNamePasswordAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
        super(requiresAuthenticationRequestMatcher);
    }

    /**
     * 认证成功后执行的方法
     * @param request
     * @param response
     * @param chain 过滤器链
     * @param authResult 认证对象信息
     * @throws IOException
     * @throws ServletException
     */

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        User principal = (User) authResult.getPrincipal();
        String username = principal.getUsername();
        Collection<GrantedAuthority> authorities = principal.getAuthorities();
        String password = principal.getPassword();
        //响应数据格式json
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //编码格式
        response.setCharacterEncoding("UTF-8");
        Map<String,String> infos = new HashMap<>();
        infos.put("msg","登录成功");
        infos.put("data","");
        infos.put("code","1");
        //响应
        response.getWriter().write(new ObjectMapper().writeValueAsString(infos));
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        //响应数据格式json
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //编码格式
        request.setCharacterEncoding("UTF-8");
        Map<String,String> infos = new HashMap<>();
        infos.put("msg","登录失败");
        infos.put("data","");
        infos.put("code","1");
        //响应
        response.getWriter().write(new ObjectMapper().writeValueAsString(infos));
    }
}

2.2 定义获取用户详情服务bean

package com.shen.service;

import com.shen.entity.TbUser;
import com.shen.mapper.TbUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author by shen
 * @Date 2024/1/23
 * @Description
 */
@Service("userDetailsService")
public class MyUserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private TbUserMapper tbUserMapper;
    /**
     * 使用security当用户认证时,会自动将用户的名称注入到该方法中
     * 然后我们可以自己写逻辑取加载用户的信息,然后组装成UserDetails认证对象
     * @param userName
     * @return 用户的基础信息,包含密码和权限集合,security底层会自动比对前端输入的明文密码
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        //1.根据用户名称获取用户的账户信息
        TbUser dbUser=tbUserMapper.findUserInfoByName(userName);
        //判断该用户是否存在
        if (dbUser==null) {
            throw new UsernameNotFoundException("用户名输入错误!");
        }
        //2.组装UserDetails对象
        //获取当前用户对应的权限集合(自动将以逗号间隔的权限字符串封装到权限集合中)
        List<GrantedAuthority> list = AuthorityUtils.commaSeparatedStringToAuthorityList(dbUser.getRoles());
        /*
            参数1:账户
            参数2:密码
            参数3:权限集合
         */
        User user = new User(dbUser.getUsername(), dbUser.getPassword(), list);
        return user;
    }
}

2.3 定义SecurityConfig类

配置默认认证过滤器,保证自定义的认证过滤器要在默认的认证过滤器之前;

package com.shen.security.config;

import com.shen.security.filter.MyUserNamePasswordAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity//开启web安全设置生效
//开启SpringSecurity相关注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 配置授权策略
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();//禁用跨站请求伪造
  		http.authorizeRequests()//对资源进行认证处理
    	.antMatchers("/authentication/form").permitAll()//登录路径无需拦截
    	.anyRequest().authenticated();  //除了上述资源外,其它资源,只有 认证通过后,才能有权访问
     	http
          		//坑-过滤器要添加在默认过滤器之前,否则,登录失效
                .addFilterBefore(myUserNamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public MyUserNamePasswordAuthenticationFilter myUserNamePasswordAuthenticationFilter() throws Exception {
      	构造认证过滤器对象,并设置默认登录路径
        MyUserNamePasswordAuthenticationFilter myUserNamePasswordAuthenticationFilter =
                new MyUserNamePasswordAuthenticationFilter("/authentication/form");
        myUserNamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
        return myUserNamePasswordAuthenticationFilter;
    }
}

2.4 自定义认证流程测试

在这里插入图片描述

3、 基于JWT实现无状态认证

JWT是无状态的,所以在服务器端无需存储和维护认证信息,这样会大大减轻服务器的压力,所以我们可在自定义的认证过滤器认证成功后通过successfulAuthentication方法向前端颁发token认证字符串;

3.1 认证成功响应JWT实现

测试工程导入Jwt工具类,集成流程如下:

    /**
     * 认证工程处理方法
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        //认证主体信息,此时以填充用户权限信息
        UserDetails principal = (UserDetails) authResult.getPrincipal();
        //组装响应前端的信息
        String username = principal.getUsername();
        Collection<? extends GrantedAuthority> authorities = principal.getAuthorities();
        //构建JwtToken 加入权限信息是为了将来访问时,jwt解析获取当前用户对应的权限,做授权的过滤
      	//权限字符串格式:[P5, ROLE_ADMIN]
        String token = JwtTokenUtil.createToken(username, authorities.toString());
        HashMap<String, String> info = new HashMap<>();
        info.put("name",username);
        info.put("token",token);
        //设置响应格式
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //内容编码格式
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(new ObjectMapper().writeValueAsString(info));
    }

测试获取认证Token

3.2 SpringSecurity基于Jwt实现认证小结

在这里插入图片描述

4、自定义Security授权过滤

上一小结认证成功后向请求方响应了token信息,那么请求方访问其它系统资源时,就需要带着这个token到后台,后台需要一个授权过滤器获取token信息,并解析用户权限信息,将信息封装到UsernamePasswordAuthentionToken对象存入安全上下文,方便请求时安全过滤处理;

4.1 授权流程说明

在这里插入图片描述

4.2 授权实现流程

在正式开始之前先了解一个 Spring Security 提供的抽象类 OncePerRequestFilter。这是 Spring Security 框架中的一个特殊过滤器,用于确保在一次请求中只通过一次过滤器链,不管请求被转发到哪里。这个特性尤其重要,因为它可以防止在同一请求中多次执行相同的安全逻辑。

OncePerRequestFilter 的主要特点:

  • 单次执行:确保在一次请求的生命周期内,不管请求如何被转发或重定向,过滤器仅被执行一次。
  • 自定义安全逻辑:它经常被用来实现自定义的安全逻辑,如认证、授权、或者检查请求中的某些特定信息等。
  • 扩展点:作为 Spring Security 过滤器链的一部分,它提供了一个扩展点,允许开发者在标准的过滤器执行流程中插入自己的逻辑。

定义授权过滤器:

package com.shen.security.config;

import com.google.gson.Gson;
import com.shen.security.utils.JwtTokenUtil;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;

/**
 * @author by shen
 * @Date 2024/1/23
 * @Description 权限认证filter
 */
public class AuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        //1.获取http请求头中携带的jwt票据字符串(注意:如果用户尚未认证,则jwt票据字符串不存在)
        String jwtToken = request.getHeader(JwtTokenUtil.TOKEN_HEADER);
        //2.判断请求中的票据是否存在
        if (StringUtils.isBlank(jwtToken)) {
            //如果票据为空,可能用户准备取认证,所以不做拦截,但是此时UsernamePasswordAuthenticationToken对象未生成,那么即使放行本次请求
            //后续的过滤器链中也会校验认证票据对象
            filterChain.doFilter(request,response);
            return;
        }
        //3.校验票据
        Claims claims = JwtTokenUtil.checkJWT(jwtToken);
        //票据失效
        if (claims==null) {
            //票据失效则提示前端票据已经失效,需要重新认证
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setContentType("utf-8");
            response.getWriter().write("jwt token failure!!");
            return;
        }
        //4.从合法的票据中获取用户名和权限信息
        //用户名
        String username = JwtTokenUtil.getUsername(jwtToken);
        //权限信息 [P5, ROLE_ADMIN]
        String roles = JwtTokenUtil.getUserRole(jwtToken);
        //将数组格式的字符串转化成权限对象集合
        String comStr = StringUtils.strip(roles, "[]");
        List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(comStr);
        //5.组装认证成功的票据对象(认证成功时,密码位置null)
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, authorityList);
        //6.将认证成功的票据对象保存到安全上下文中,方便后续的过滤器直接获取权限信息
        SecurityContextHolder.getContext().setAuthentication(token);
        //7.发行过滤器
        filterChain.doFilter(request,response);
    }
}

4.3 配置自定义授权过滤器

/**
  * 给访问的资源配置权限过滤
  * @param http
  * @throws Exception
  */
@Override
protected void configure(HttpSecurity http) throws Exception {
  http.csrf().disable();//禁用跨站请求伪造
  http.authorizeRequests()//对资源进行认证处理
    .antMatchers("/myLogin").permitAll()//登录路径无需拦截
    .anyRequest().authenticated();  //除了上述资源外,其它资源,只有 认证通过后,才能有权访问
  //添加自定义的认证过滤器:UsernamePasswordAuthenticationFilter是默认的登录认证过滤器,而我们重写了该过滤器,所以访问时,应该先走我们
  //自定义的过滤器
  http.addFilterBefore(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
  //配置授权过滤器,过滤一切资源
  http.addFilterBefore( authenticationFilter(),MyUsernamePasswordAuthenticationFilter.class);
}

/**
  * 自定义授权过滤器
  * 过滤一切被访问的资源
  * @return
  */
@Bean
public AuthenticationFilter authenticationFilter(){
  AuthenticationFilter filter = new AuthenticationFilter();
  return filter;
}

访问测试:

​ 略

5、自定义权限拒绝处理

上一小结当用户未认证访问资源或者认证成功后访问没有权限的资源时,响应给前端的信息不友好,我们可通过自定义权限访问拒绝的处理器来友好提醒用户;

5.1 自定义认证用户权限拒绝处理器

通过实现AccessDeniedHandler接口实现:

    /**
     * 自定义登录认证策略配置授权策略 -1
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
		//......
        http.exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest request,
                                       HttpServletResponse response,
                                       AccessDeniedException e) throws IOException, ServletException {
                        //认证用户访问资源时权限拒绝处理策略
                        response.getWriter().write("no permission......reject....");
                    }
                });
    }

效果:

在这里插入图片描述

5.2 自定义匿名用户拒绝处理器

同样通过实现AuthenticationEntryPoint接口实现:

    /**
     * 自定义登录认证策略配置授权策略 -1
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
		//......
				http.exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest request,
                                       HttpServletResponse response,
                                       AccessDeniedException e) throws IOException, ServletException {
                        //认证用户访问资源时权限拒绝处理策略
                        response.getWriter().write("no permission......reject....");
                    }
                })
          		.authenticationEntryPoint(new AuthenticationEntryPoint(){
                    @Override
    				public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {
        				 response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        				 response.setCharacterEncoding("UTF-8");
        				 response.getWriter().write("匿名用户无权访问");
          });
    }
}

6、自定义认证授权整体流程小结

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从大厂到高校,鸿蒙人才“红透半边天”!

截至目前&#xff0c;继清华大学、北京航空航天大学、武汉大学等985高校开设鸿蒙相关课程后&#xff0c;已经或将要开设鸿蒙相关课程的985、211高校达到近百所&#xff0c;为鸿蒙人才培养提供沃土。 随着鸿蒙系统即将摒弃安卓&#xff0c;鸿蒙原生应用将全面启动的背景下&…

4款实用性前端动画特效分享(附在线演示)

分享4款非常不错的项目动画特效 其中有jQuery特效、canvas特效、CSS动画等等 下方效果图可能不是特别的生动 那么你可以点击在线预览进行查看相应的动画特效 同时也是可以下载该资源的 全屏图片视差旋转切换特效 基于anime.js制作全屏响应式的图片元素布局&#xff0c;通过左…

javaweb学习(day09-Web开发会话技术)

一、会话 1 基本介绍 1.1 什么是会话&#xff1f; 会话可简单理解为&#xff1a;用户开一个浏览器&#xff0c;点击多个超链接&#xff0c;访问服务器多个 web 资源&#xff0c;然 后关闭浏览器&#xff0c;整个过程称之为一个会话。 1.2 会话过程中要解决的一些问题&#…

[LVGL]:MACOS下使用LVGL模拟器

如何在MACOS下使用lvgl模拟器 1.安装必要环境 brew install sdl2查看sdl2安装位置&#xff1a; (base) ➜ ~ brew list sdl2 /opt/homebrew/Cellar/sdl2/2.30.1/bin/sdl2-config /opt/homebrew/Cellar/sdl2/2.30.1/include/SDL2/ (78 files) /opt/homebrew/Cellar/sdl2/2.3…

权威发布!艾媒咨询最新报告显示65.1%的消费者还在睡硬床

一直以来,“睡硬床好”观念在中国盛行,国民较多青睐硬床,床垫厂商为迎合大众,大批量生产硬床垫。日前,全球新经济产业第三方数据挖掘和分析机构iiMedia Research(艾媒咨询)发布《2024年中国硬床垫使用现状及潜在危害调研》(以下简称:报告),数据显示,75.1%的消费者对“睡硬床对腰…

DM数据库安装及使用(Windows、Linux、docker)

Windows 先解压安装包 点击setup安装 下一步 勾选接受然后下一步 下一步 选择典型安装下一步 下一步 搜索DM数据库配置助手然后一直下一步 然后搜索DM管理工具 登录 登录成功 widows版本安装成功 Linux安装 操作系统CPU数据库CentOS7x86_64 架构dm8_20230418_x86_rh6_64 …

Nacos源码流程图

1.Nacos1.x版本服务注册与发现源码 流程图地址&#xff1a;https://www.processon.com/view/link/634695eb260d7157a7bc6adb 2.Nacos2.x版本服务注册与发现源码 流程图地址&#xff1a;https://www.processon.com/view/link/634695fb260d7157a7bc6ae0 3.Nacos2.x版本GRPC…

【深度学习笔记】10_11 注意力机制

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 10.11 注意力机制 在10.9节&#xff08;编码器—解码器&#xff08;seq2seq&#xff09;&#xff09;里&#xff0c;解码器在各个时间…

Leet code 1658 将x减到0的最小操作数

解题思路&#xff1a;滑动窗口 主要思想&#xff1a;正难逆简 题目需要左找一个数 右找一个数 我们不如直接找中间最长的一连串子数让这串子树和为 数组子树和减去X 找不到就返回 -1 滑动窗口双指针从左端出发&#xff0c;进行 进窗口 判断 出窗口 更新结果四个步骤 代码…

用机床测头为什么能提升加工中心精度?提高生产效率?

制造业的蓬勃发展为企业提出了更高的精度和效率要求。在现代制造业中&#xff0c;机床测头作为一种关键的检测装置&#xff0c;能够实时监控加工过程中的误差&#xff0c;及时调整&#xff0c;保证加工质量的稳定性&#xff0c;提高加工中心的精度&#xff0c;进而提升生产效率…

QT 如何在QPushButton上播放gif(终极版)

在平时浏览网站&#xff0c;或者使用软件的时候&#xff0c;经常可以见到&#xff1a;在点击了某个按钮之后&#xff0c;按钮上会显示动图以及提示文字。在QT中&#xff0c;比较常见且简单的做法就是&#xff1a;给按钮设置一个layout&#xff0c;然后在这个layout里面添加QLab…

openssl3.2 - note - Getting Started with OpenSSL

文章目录 openssl3.2 - note - Getting Started with OpenSSL概述笔记openssl 历史版本Configure 选项开关支持的OSopenssl 文档简介安装新闻每个平台的安装文档支持的命令列表配置文件格式环境变量 END openssl3.2 - note - Getting Started with OpenSSL 概述 看到官方文档…

三款内衣洗衣机的顶级较量:希亦、小吉、由利,谁才是性价比之王?

洗衣机在我们的生活中可谓是非常常见的了&#xff0c;几乎每家每户都具备着一台。即便是有洗衣机&#xff0c;也有不少人不会将自己我贴身衣物直接扔在洗衣机里清洗&#xff0c;而是会自己手工手洗。这跟我们传统上的观念有很大的关系&#xff0c;认为把内衣、内裤等贴身衣物放…

代码随想录算法训练营三刷day24 | 回溯算法 之 理论基础 77. 组合

三刷day24 理论基础77. 组合递归函数的返回值以及参数回溯函数终止条件单层搜索的过程 理论基础 回溯法解决的问题都可以抽象为树形结构。 因为回溯法解决的都是在集合中递归查找子集&#xff0c;集合的大小就构成了树的宽度&#xff0c;递归的深度&#xff0c;都构成的树的深…

【Java 并发】AbstractQueuedSynchronizer

1 AQS 简介 在同步组件的实现中, AQS 是核心部分, 同步组件的实现者通过使用 AQS 提供的模板方法实现同步组件语义。 AQS 则实现了对同步状态的管理, 以及对阻塞线程进行排队, 等待通知等一些底层的实现处理。 AQS 的核心也包括了这些方面: 同步队列, 独占式锁的获取和释放, 共…

2024三掌柜赠书活动第十六期:AI时代Python金融大数据分析实战

目录 前言 AI时代Python金融大数据分析实战 关于《AI时代Python金融大数据分析实战》 编辑推荐 内容简介 作者简介 图书目录 书中前言/序言 《AI时代Python金融大数据分析实战》全书速览 结束语 前言 随着人工智能技术的发展和金融行业的不断进步&#xff0c;大数据分…

Linux下的多线程编程:原理、工具及应用(1)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;Flower of Life—陽花 0:34━━━━━━️&#x1f49f;──────── 4:46 &#x1f504; ◀️ ⏸ ▶️ ☰ …

记一次Spring事务失效的发现与解决过程

一、事情起因是这样的 首先&#xff0c;我们是使用Spring mybatis 进行开发。 某功能在测试环境看到报错日志&#xff0c; 但是数据库里面的数据发生了变化&#xff0c;没有回滚。 执行数据库update 操作的方法上明确有 Transactional(rollbackFor Exception.class)的注解。…

【数学建模】熵权法

之前我们学了层次分析法和topsis法&#xff0c;但是主观性十分强&#xff0c;有没有科学的方法得出权重呢&#xff1f;今天&#xff0c;我们来学习熵权法&#xff01; 基本概念&#xff1a; 熵权法&#xff0c;物理学名词&#xff0c;按照信息论基本原理的解释&#xff0c;信息…

Spring状态机简单实现

一、什么是状态机 状态机&#xff0c;又称有限状态自动机&#xff0c;是表示有限个状态以及在这些状态之间的转移和动作等行为的计算模型。状态机的概念其实可以应用的各种领域&#xff0c;包括电子工程、语言学、哲学、生物学、数学和逻辑学等&#xff0c;例如日常生活中的电…
最新文章