前端带你学后端系列 ⑥【安全框架Spring Security篇二】

2023-12-18 18:20

本文主要是介绍前端带你学后端系列 ⑥【安全框架Spring Security篇二】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前端带你学后端系列 ⑥【安全框架Spring Security篇二】

  • Ⅰ Spring Security实战一
    • ① Spring Security中的密码加密
    • ② Spring Security四种权限控制方式
    • ③ 关于JWT,以及Spring Security 结合JWT实现登陆验证
      • ① jwt 的组成
      • ② Spring Security 结合JWT登陆验证的流程
        • ① 提前准备,写一个Result返回结果集
        • ② 提前准备,写一个JWT工具类
        • ③ 写LoginSuccessHandler、LoginFailureHandler
        • ④ 验证码相关的配置
          • ① 验证码配置类
          • ② 验证码的controller,返回给前端验证码图片
          • ③ 验证码的filter
        • ⑤ 继承BasicAuthenticationFilter,实现用户验证
        • ⑥ 认证失败的JwtAuthenticationEntryPoint(用户未登录处理类)
        • ⑦ 暂无权限处理类(AccessDeniedHandler,状态码403)
        • ⑧ 登出处理器LogoutSuccessHandler
        • ⑨ 自定义AccountUser类实现UserDetails(拓展原有的UserDetails)
        • ⑩ 实现UserDetailsService,用于和数据库比对
        • ⑩① 密码的加密解密
        • ⑩② SecurityConfig配置类
    • ④ 回顾一下Security的登陆流程

Ⅰ Spring Security实战一

① Spring Security中的密码加密


Spring Security处理密码加密的几种方式
在这里插入图片描述

官方推荐使用BCryptPasswordEncoder

使用方法:

  1. 配置密码加密的方式
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()//对跨域请求伪造进行防护---->csrf:利用用户带有登录状态的cookie进行攻击的手段.csrf().disable();}//配置采用哪种密码加密算法@Beanpublic PasswordEncoder passwordEncoder() {//不使用密码加密//return NoOpPasswordEncoder.getInstance();//使用默认的BCryptPasswordEncoder加密方案return new BCryptPasswordEncoder();//strength=10,即密钥的迭代次数(strength取值在4~31之间,默认为10)//return new BCryptPasswordEncoder(10);//利用工厂类PasswordEncoderFactories实现,工厂类内部采用的是委派密码编码方案.//return PasswordEncoderFactories.createDelegatingPasswordEncoder();}}
  1. 使用
    //对密码进行加密user.setPassword(passwordEncoder.encode(user.getPassword()));

② Spring Security四种权限控制方式

Spring Security 的认证方式有 认证+授权。我们授权的时候,不仅可以使用默认的授权,还可以自定义授权。

在这里插入图片描、述

使用说明:

  1. 利用Ant表达式(主要是在配置类中SecurityConfig中使用)
    在这里插入图片描述
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/user/**").hasRole("USER").antMatchers("/visitor/**").permitAll().anyRequest().authenticated().and().formLogin().permitAll().and()//对跨域请求伪造进行防护---->csrf:利用用户带有登录状态的cookie进行攻击的手段.csrf().disable();
}
  1. 利用授权注解结合SpEl表达式实现权限控制
    在这里插入图片描述
@RestController
public class UserController {@Secured({"ROLE_USER"})//@PreAuthorize("principal.username.equals('user')")@GetMapping("/user/hello")public String helloUser() {return "hello, user";}@PreAuthorize("hasRole('ADMIN')")@GetMapping("/admin/hello")public String helloAdmin() {return "hello, admin";}@PreAuthorize("#age>100")@GetMapping("/age")public String getAge(@RequestParam("age") Integer age) {return String.valueOf(age);}@GetMapping("/visitor/hello")public String helloVisitor() {return "hello, visitor";}}
  1. 利用过滤器注解实现权限控制
    在这里插入图片描述
@RestController
public class FilterController {/*** 只返回结果中id为偶数的user元素。* filterObject是@PreFilter和@PostFilter中的一个内置表达式,表示集合中的当前对象。*/@PostFilter("filterObject.id%2==0")@GetMapping("/users")public List<User> getAllUser() {List<User> users = new ArrayList<>();for (int i = 0; i < 10; i++) {users.add(new User(i, "yyg-" + i));}return users;}}
  1. 利用动态权限实现权限控制

我们一般会使用标准的RABC进行权限控制,Spring Security中的动态权限,主要是通过重写拦截器和决策器来进行实现,一般满足不了我们的需求。

③ 关于JWT,以及Spring Security 结合JWT实现登陆验证

① jwt 的组成

在这里插入图片描述

② Spring Security 结合JWT登陆验证的流程

在这里插入图片描述

① 提前准备,写一个Result返回结果集
@Data
public class Result implements Serializable {private int code;private String msg;private Object data;public static Result succ(Object data) {return succ(200, "操作成功", data);}public static Result fail(String msg) {return fail(400, msg, null);}public static Result succ (int code, String msg, Object data) {Result result = new Result();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}public static Result fail (int code, String msg, Object data) {Result result = new Result();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}
}
② 提前准备,写一个JWT工具类

该工具类需要有3个功能:生成JWT解析JWT判断JWT是否过期

@Data
@Component
@ConfigurationProperties(prefix = "test.jwt")
public class JwtUtils {private long expire;private String secret;private String header;// 生成JWTpublic String generateToken(String username) {Date nowDate = new Date();Date expireDate = new Date(nowDate.getTime() + 1000 * expire);return Jwts.builder().setHeaderParam("type", "JWT").setSubject(username).setIssuedAt(nowDate).setExpiration(expireDate)    // 7天过期.signWith(SignatureAlgorithm.HS512, secret).compact();}// 解析JWTpublic Claims getClaimsByToken(String jwt) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt).getBody();} catch (Exception e) {return null;}}// 判断JWT是否过期public boolean isTokenExpired(Claims claims) {return claims.getExpiration().before(new Date());}}

我们可以配置JWT的有效时间和加密算法所需使用的秘钥,以及返回给前端时在Http response的Header中所叫的名字。这种配置项我们需写入application.yml中,然后使用@ConfigurationProperties注解接收,这样能便于我们日后修改配置。
使用@ConfigurationProperties注解可以读取配置文件中的信息,只要在 Bean 上添加上了这个注解,指定好配置文件中的前缀,那么对应的配置文件数据就会自动填充到 Bean 的属性中
application.yml中的配置如下:

test:jwt:header: Authorizationexpire: 604800 # 7天,s为单位secret: test
③ 写LoginSuccessHandler、LoginFailureHandler

因为我们是前后端分离模式,当成功或者失败以后,需要返回JSON 所以需要写这两个handler。用于成功或者失败的返回

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {@AutowiredJwtUtils jwtUtils;@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {httpServletResponse.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = httpServletResponse.getOutputStream();// 生成JWT,并放置到请求头中String jwt = jwtUtils.generateToken(authentication.getName());httpServletResponse.setHeader(jwtUtils.getHeader(), jwt);Result result = Result.succ("SuccessLogin");outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {httpServletResponse.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = httpServletResponse.getOutputStream();String errorMessage = "用户名或密码错误";Result result;if (e instanceof CaptchaException) {errorMessage = "验证码错误";result = Result.fail(errorMessage);} else {result = Result.fail(errorMessage);}outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
④ 验证码相关的配置
① 验证码配置类
/*配置验证码的大小,宽度等等
*/
@Configuration
public class KaptchaConfig {@BeanDefaultKaptcha producer() {Properties properties = new Properties();properties.put("kaptcha.border", "no");properties.put("kaptcha.textproducer.font.color", "black");properties.put("kaptcha.textproducer.char.space", "4");properties.put("kaptcha.image.height", "40");properties.put("kaptcha.image.width", "120");properties.put("kaptcha.textproducer.font.size", "30");Config config = new Config(properties);DefaultKaptcha defaultKaptcha = new DefaultKaptcha();defaultKaptcha.setConfig(config);return defaultKaptcha;}}
② 验证码的controller,返回给前端验证码图片
@GetMapping("/captcha")
public Result Captcha() throws IOException {String key = UUID.randomUUID().toString();String code = producer.createText();BufferedImage image = producer.createImage(code);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ImageIO.write(image, "jpg", outputStream);BASE64Encoder encoder = new BASE64Encoder();String str = "data:image/jpeg;base64,";String base64Img = str + encoder.encode(outputStream.toByteArray());redisUtil.hset(Const.CAPTCHA_KEY, key, code, 120);return Result.succ(MapUtil.builder().put("userKey", key).put("captcherImg", base64Img).build());
}
③ 验证码的filter
  1. 过滤器将来放到验证用户名密码过滤器前端
  2. 需要先判断请求是否是登录请求,若是登录请求,则进行验证码校验。若不是,则直接跳过这个过滤器。
  3. CaptchaFilter继承了OncePerRequestFilter抽象类,该抽象类在每次请求时只执行一次过滤,即它的作用就是保证一次请求只通过一次filter,而不需要重复执行。
@Component
public class CaptchaFilter extends OncePerRequestFilter {@AutowiredRedisUtil redisUtil;@AutowiredLoginFailureHandler loginFailureHandler;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {String url = httpServletRequest.getRequestURI();if ("/login".equals(url) && httpServletRequest.getMethod().equals("POST")) {// 校验验证码try {validate(httpServletRequest);} catch (CaptchaException e) {// 交给认证失败处理器loginFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);}}filterChain.doFilter(httpServletRequest, httpServletResponse);}// 校验验证码逻辑private void validate(HttpServletRequest httpServletRequest) {   String code = httpServletRequest.getParameter("code");String key = httpServletRequest.getParameter("userKey");if (StringUtils.isBlank(code) || StringUtils.isBlank(key)) {throw new CaptchaException("验证码错误");}if (!code.equals(redisUtil.hget(Const.CAPTCHA_KEY, key))) {throw new CaptchaException("验证码错误");}// 若验证码正确,执行以下语句// 一次性使用redisUtil.hdel(Const.CAPTCHA_KEY, key);}
}
⑤ 继承BasicAuthenticationFilter,实现用户验证
  1. login-form(登录表单认证):使用基于表单的用户界面进行认证。用户在登录页面中输入用户名和密码,提交表单后,Spring Security会验证用户凭据并完成认证过程。
  2. httpBasic(基本身份验证):在HTTP请求头中发送用户名和密码进行认证。客户端会在每个请求中添加Authorization头,其中包含Basic认证信息。
  1. UsernamePasswordAuthenticationFilter过滤器用于处理基于表单方式的登录验证。
  2. BasicAuthenticationFilter用于处理基于HTTP Basic方式的登录验证

JwtAuthenticationFilter继承了BasicAuthenticationFilter,该类用于普通http请求进行身份认证,该类有一个重要属性:AuthenticationManager,表示认证管理器,它是一个接口,它的默认实现类是ProviderManager


public class JwtAuthenticationFilter extends BasicAuthenticationFilter {  @AutowiredJwtUtils jwtUtils;@AutowiredUserDetailServiceImpl userDetailService;@AutowiredSysUserService sysUserService;public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {super(authenticationManager);}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {String jwt = request.getHeader(jwtUtils.getHeader());// 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的// 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口if (StrUtil.isBlankOrUndefined(jwt)) {     chain.doFilter(request, response);return;}Claims claim = jwtUtils.getClaimsByToken(jwt);if (claim == null) {throw new JwtException("token 异常");}if (jwtUtils.isTokenExpired(claim)) {throw new JwtException("token 已过期");}String username = claim.getSubject();// 获取用户的权限等信息SysUser sysUser = sysUserService.getByUsername(username);// 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, userDetailService.getUserAuthority(sysUser.getId()));SecurityContextHolder.getContext().setAuthentication(token);chain.doFilter(request, response);}
}
⑥ 认证失败的JwtAuthenticationEntryPoint(用户未登录处理类)

当BasicAuthenticationFilter认证失败的时候会进入AuthenticationEntryPoint,我们定义JWT认证失败处理器JwtAuthenticationEntryPoint,使其实现AuthenticationEntryPoint接口

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {httpServletResponse.setContentType("application/json;charset=UTF-8");httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);ServletOutputStream outputStream = httpServletResponse.getOutputStream();Result result = Result.fail("请先登录");outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
⑦ 暂无权限处理类(AccessDeniedHandler,状态码403)
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {httpServletResponse.setContentType("application/json;charset=UTF-8");httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);ServletOutputStream outputStream = httpServletResponse.getOutputStream();Result result = Result.fail(e.getMessage());outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
⑧ 登出处理器LogoutSuccessHandler
@Component
public class JWTLogoutSuccessHandler implements LogoutSuccessHandler {@AutowiredJwtUtils jwtUtils;@Overridepublic void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {if (authentication != null) {new SecurityContextLogoutHandler().logout(httpServletRequest, httpServletResponse, authentication);}httpServletResponse.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = httpServletResponse.getOutputStream();httpServletResponse.setHeader(jwtUtils.getHeader(), "");Result result = Result.succ("SuccessLogout");outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
⑨ 自定义AccountUser类实现UserDetails(拓展原有的UserDetails)
public class AccountUser implements UserDetails {private Long userId;private static final long serialVersionUID = 540L;private static final Log logger = LogFactory.getLog(User.class);private String password;private final String username;private final Collection<? extends GrantedAuthority> authorities;private final boolean accountNonExpired;private final boolean accountNonLocked;private final boolean credentialsNonExpired;private final boolean enabled;public AccountUser(Long userId, String username, String password, Collection<? extends GrantedAuthority> authorities) {this(userId, username, password, true, true, true, true, authorities);}public AccountUser(Long userId, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {Assert.isTrue(username != null && !"".equals(username) && password != null, "Cannot pass null or empty values to constructor");this.userId = userId;this.username = username;this.password = password;this.enabled = enabled;this.accountNonExpired = accountNonExpired;this.credentialsNonExpired = credentialsNonExpired;this.accountNonLocked = accountNonLocked;this.authorities = authorities;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}@Overridepublic String getPassword() {return this.password;}@Overridepublic String getUsername() {return this.username;}@Overridepublic boolean isAccountNonExpired() {return this.accountNonExpired;}@Overridepublic boolean isAccountNonLocked() {return this.accountNonLocked;}@Overridepublic boolean isCredentialsNonExpired() {return this.credentialsNonExpired;}@Overridepublic boolean isEnabled() {return this.enabled;}
}
⑩ 实现UserDetailsService,用于和数据库比对
@Service
public class UserDetailServiceImpl implements UserDetailsService {@AutowiredSysUserService sysUserService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {SysUser sysUser = sysUserService.getByUsername(username);if (sysUser == null) {throw new UsernameNotFoundException("用户名或密码错误");}return new AccountUser(sysUser.getId(), sysUser.getUsername(), sysUser.getPassword(), getUserAuthority(sysUser.getId()));}
}
⑩① 密码的加密解密
@NoArgsConstructor
public class PasswordEncoder extends BCryptPasswordEncoder {@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {// 接收到的前端的密码String pwd = rawPassword.toString();// 进行rsa解密try {pwd = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, pwd);} catch (Exception e) {throw new BadCredentialsException(e.getMessage());}if (encodedPassword != null && encodedPassword.length() != 0) {return BCrypt.checkpw(pwd, encodedPassword);} else {return false;}}
}
⑩② SecurityConfig配置类
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@AutowiredLoginFailureHandler loginFailureHandler;@AutowiredLoginSuccessHandler loginSuccessHandler;@AutowiredCaptchaFilter captchaFilter;@AutowiredJwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;@AutowiredJwtAccessDeniedHandler jwtAccessDeniedHandler;@AutowiredUserDetailServiceImpl userDetailService;@AutowiredJWTLogoutSuccessHandler jwtLogoutSuccessHandler;@BeanJwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager());return jwtAuthenticationFilter;}private static final String[] URL_WHITELIST = {"/login","/logout","/captcha","/favicon.ico"};@BeanPasswordEncoder PasswordEncoder() {return new PasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().and().csrf().disable()// 登录配置.httpBasic().successHandler(loginSuccessHandler).failureHandler(loginFailureHandler).and().logout().logoutSuccessHandler(jwtLogoutSuccessHandler)// 禁用session.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)// 配置拦截规则.and().authorizeRequests().antMatchers(URL_WHITELIST).permitAll().anyRequest().authenticated()// 异常处理器.and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).accessDeniedHandler(jwtAccessDeniedHandler)// 配置自定义的过滤器.and().addFilter(jwtAuthenticationFilter())// 验证码过滤器放在UsernamePassword过滤器之前.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class);}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailService);}
}

比较好的代码推荐
https://zhuanlan.zhihu.com/p/585835490

好文推荐

④ 回顾一下Security的登陆流程


在这里插入图片描述

这篇关于前端带你学后端系列 ⑥【安全框架Spring Security篇二】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/509364

相关文章

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.

Java空指针异常NullPointerException的原因与解决方案

《Java空指针异常NullPointerException的原因与解决方案》在Java开发中,NullPointerException(空指针异常)是最常见的运行时异常之一,通常发生在程序尝试访问或... 目录一、空指针异常产生的原因1. 变量未初始化2. 对象引用被显式置为null3. 方法返回null

使用vscode搭建pywebview集成vue项目实践

《使用vscode搭建pywebview集成vue项目实践》:本文主要介绍使用vscode搭建pywebview集成vue项目实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录环境准备项目源码下载项目说明调试与生成可执行文件核心代码说明总结本节我们使用pythonpywebv

一文彻底搞懂Java 中的 SPI 是什么

《一文彻底搞懂Java中的SPI是什么》:本文主要介绍Java中的SPI是什么,本篇文章将通过经典题目、实战解析和面试官视角,帮助你从容应对“SPI”相关问题,赢得技术面试的加分项,需要的朋... 目录一、面试主题概述二、高频面试题汇总三、重点题目详解✅ 面试题1:Java 的 SPI 是什么?如何实现一个

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

SpringCloud中的@FeignClient注解使用详解

《SpringCloud中的@FeignClient注解使用详解》在SpringCloud中使用Feign进行服务间的调用时,通常会使用@FeignClient注解来标记Feign客户端接口,这篇文章... 在Spring Cloud中使用Feign进行服务间的调用时,通常会使用@FeignClient注解

Java Spring 中的监听器Listener详解与实战教程

《JavaSpring中的监听器Listener详解与实战教程》Spring提供了多种监听器机制,可以用于监听应用生命周期、会话生命周期和请求处理过程中的事件,:本文主要介绍JavaSprin... 目录一、监听器的作用1.1 应用生命周期管理1.2 会话管理1.3 请求处理监控二、创建监听器2.1 Ser

JVisualVM之Java性能监控与调优利器详解

《JVisualVM之Java性能监控与调优利器详解》本文将详细介绍JVisualVM的使用方法,并结合实际案例展示如何利用它进行性能调优,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全... 目录1. JVisualVM简介2. JVisualVM的安装与启动2.1 启动JVisualVM2

Java如何从Redis中批量读取数据

《Java如何从Redis中批量读取数据》:本文主要介绍Java如何从Redis中批量读取数据的情况,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一.背景概述二.分析与实现三.发现问题与屡次改进3.1.QPS过高而且波动很大3.2.程序中断,抛异常3.3.内存消

SpringBoot使用ffmpeg实现视频压缩

《SpringBoot使用ffmpeg实现视频压缩》FFmpeg是一个开源的跨平台多媒体处理工具集,用于录制,转换,编辑和流式传输音频和视频,本文将使用ffmpeg实现视频压缩功能,有需要的可以参考... 目录核心功能1.格式转换2.编解码3.音视频处理4.流媒体支持5.滤镜(Filter)安装配置linu