SpringBoot整合SpringSecurity實(shí)現(xiàn)圖形驗(yàn)證碼功能
1、圖形驗(yàn)證碼的作用
圖形驗(yàn)證碼(CAPTCHA,Completely Automated Public Turing test to tell Computers and Humans Apart)是一種用于區(qū)分用戶是人類還是計(jì)算機(jī)程序的自動(dòng)化測試。它通常用于防止自動(dòng)化軟件(如機(jī)器人或爬蟲程序)進(jìn)行惡意操作,如濫用在線服務(wù)、暴力破 解密碼或進(jìn)行垃圾郵件發(fā)送等。
圖形驗(yàn)證碼的工作原理基于一個(gè)假設(shè):計(jì)算機(jī)程序難以自動(dòng)識(shí)別和處理復(fù)雜的圖像或模式,而人類則相對(duì)容易。因此,圖形驗(yàn)證碼通常包含扭曲的文字、數(shù)字、圖像或它們的組合,這些元素對(duì)人類來說相對(duì)容易辨認(rèn),但對(duì)計(jì)算機(jī)程序來說則非常困難。
下面將介紹 Spring Boot 整合 Spring Security 實(shí)現(xiàn)圖形驗(yàn)證碼功能,執(zhí)行結(jié)果如下如:
(1)登錄頁面

(2)登錄成功后,跳轉(zhuǎn)至首頁

2、創(chuàng)建項(xiàng)目
【示例】SpringBoot 整合 SpringSecurity 使用過濾器實(shí)現(xiàn)圖形驗(yàn)證碼功能。
2.1 創(chuàng)建 Spring Boot 項(xiàng)目
創(chuàng)建 SpringBoot 項(xiàng)目,項(xiàng)目結(jié)構(gòu)如下圖:

2.2 添加 Maven 依賴
在 pom.xml 配置文件中添加 Spring Security、谷歌 Kaptcha 圖形驗(yàn)證碼。
<!-- Spring Security 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.7.18</version>
</dependency>
<!-- 谷歌 Kaptcha 圖形驗(yàn)證碼 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>3、整合 Spring Security 框架實(shí)現(xiàn)認(rèn)證與授權(quán)
3.1 配置類(Config 層)
創(chuàng)建 WebSecurityConfig 類(Spring Security 配置類),并添加 @EnableWebSecurity 注解和繼承 WebSecurityConfigurerAdapter 類。
package com.pjb.securitydemo.config;
import com.pjb.securitydemo.filter.VerificationCodeFilter;
import com.pjb.securitydemo.handler.LoginFailureHandler;
import com.pjb.securitydemo.handler.LoginSuccessHandler;
import com.pjb.securitydemo.handler.PermissionDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* Spring Security 配置類
* @author pan_junbiao
**/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Autowired
private LoginFailureHandler loginFailureHandler;
@Autowired
private PermissionDeniedHandler permissionDeniedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests() //返回一個(gè)URL攔截注冊器
.antMatchers("/captcha.jpg").permitAll() //公開其權(quán)限
.anyRequest() //匹配所有的請求
.authenticated() //所有匹配的URL都需要被認(rèn)證才能訪問
.and() //結(jié)束當(dāng)前標(biāo)簽,讓上下文回到 HttpSecurity
.formLogin() //啟動(dòng)表單認(rèn)證
.loginPage("/myLogin.html") //自定義登錄頁面
.loginProcessingUrl("/auth/form") //指定處理登錄請求路徑
.permitAll() //使登錄頁面不設(shè)限訪問
//.defaultSuccessUrl("/index") //登錄認(rèn)證成功后的跳轉(zhuǎn)頁面
.successHandler(loginSuccessHandler) //指定登錄成功時(shí)的處理
.failureHandler(loginFailureHandler) //指定登錄失敗時(shí)的處理
.and()
.exceptionHandling().accessDeniedHandler(permissionDeniedHandler) //403無權(quán)時(shí)的返回操作
.and().csrf().disable(); //關(guān)閉CSRF的防御功能
//圖形驗(yàn)證碼過濾器(核心代碼):將自定義過濾器添加在UsernamePasswordAuthenticationFilter之前
http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
}
/**
* 內(nèi)存中添加登錄賬號(hào)
*/
@Bean
public UserDetailsService userDetailsService()
{
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("admin").password("123456").roles("ADMIN").build());
manager.createUser(User.withUsername("user").password("123456").roles("USER").build());
manager.createUser(User.withUsername("panjunbiao").password("123456").roles("USER").build());
return manager;
}
/**
* 密碼編譯器
* 由于5.x版本之后默認(rèn)啟用了委派密碼編譯器,
* 因而按照以往的方式設(shè)置內(nèi)存密碼將會(huì)讀取異常,
* 所以需要暫時(shí)將密碼編碼器設(shè)置為 NoOpPasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder()
{
return NoOpPasswordEncoder.getInstance();
}
}3.2 處理類(Handler 層)
(1)登錄成功處理類
package com.pjb.securitydemo.handler;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登錄成功處理類
*/
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler
{
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException
{
//重定向至首頁
httpServletResponse.sendRedirect("/");
}
}(2)登錄失敗處理類
package com.pjb.securitydemo.handler;
import com.pjb.securitydemo.exception.VerificationCodeException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 登錄失敗處理類
*/
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler
{
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException authenticationException) throws IOException, ServletException
{
//獲取登錄失敗原因
String errorMessage = "";
if(authenticationException instanceof BadCredentialsException){
errorMessage = "用戶名或密碼不正確";
}else if(authenticationException instanceof DisabledException){
errorMessage = "賬號(hào)被禁用";
}else if(authenticationException instanceof UsernameNotFoundException){
errorMessage = "用戶名不存在";
}else if(authenticationException instanceof CredentialsExpiredException){
errorMessage = "密碼已過期";
}else if(authenticationException instanceof LockedException) {
errorMessage = "賬號(hào)被鎖定";
}else if(authenticationException instanceof VerificationCodeException){
errorMessage = "無效的圖形驗(yàn)證碼";
}else{
errorMessage = "未知異常";
}
//設(shè)置響應(yīng)編碼
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write(errorMessage);
}
}(3)403無權(quán)限處理類
package com.pjb.securitydemo.handler;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 403無權(quán)限處理類
*/
@Component
public class PermissionDeniedHandler implements AccessDeniedHandler
{
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException
{
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write("403無權(quán)限");
}
}4、整合 Kaptcha 框架實(shí)現(xiàn)圖形驗(yàn)證碼
4.1 配置類(Config 層)
創(chuàng)建 KaptchaConfig 類(Kaptcha 圖形驗(yàn)證碼配置類),設(shè)置圖形驗(yàn)證碼相關(guān)屬性。
package com.pjb.securitydemo.config;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
/**
* 谷歌Kaptcha圖形驗(yàn)證碼配置類
*/
@Configuration
public class KaptchaConfig
{
@Bean
public Producer captcha()
{
//配置圖形驗(yàn)證碼的基本參數(shù)
Properties properties = new Properties();
//圖片寬度
properties.setProperty("kaptcha.image.width","150");
//圖片長度
properties.setProperty("kaptcha.image.height","50");
//字符集(從哪些字符中產(chǎn)生)
properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
//字符長度
properties.setProperty("kaptcha.textproducer.char.length", "4");
//字體顏色
properties.put("kaptcha.textproducer.font.color", "red");
// 文字間隔,這里設(shè)置為10px
properties.put("kaptcha.textproducer.char.space", "10");
// 背景顏色漸變開始
properties.put("kaptcha.background.clear.from", "yellow");
// 背景顏色漸變結(jié)束
properties.put("kaptcha.background.clear.to", "green");
//初始化配置
Config config = new Config(properties);
//使用默認(rèn)的圖形驗(yàn)證碼實(shí)現(xiàn),當(dāng)然也可以自定義實(shí)現(xiàn)
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}圖形驗(yàn)證碼配置屬性表
| 屬性名 | 屬性作用 | 默認(rèn)值 |
|---|---|---|
| kaptcha.border | 圖片邊框,合法值:yes , no | yes |
| kaptcha.border.color | 邊框顏色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. | black |
| kaptcha.image.width | 圖片寬 | 200 |
| kaptcha.image.height | 圖片高 | 50 |
| kaptcha.producer.impl | 圖片實(shí)現(xiàn)類 | com.google.code.kaptcha.impl.DefaultKaptcha |
| kaptcha.textproducer.impl | 文本實(shí)現(xiàn)類 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
| kaptcha.textproducer.char.string | 文本集合,驗(yàn)證碼值從此集合中獲取 | abcde2345678gfynmnpwx |
| kaptcha.textproducer.char.length | 驗(yàn)證碼長度 | 5 |
| kaptcha.textproducer.font.names | 字體 | Arial, Courier |
| kaptcha.textproducer.font.size | 字體大小 | 40px. |
| kaptcha.textproducer.font.color | 字體顏色,合法值: r,g,b 或者 white,black,blue. | black |
| kaptcha.textproducer.char.space | 文字間隔 | 2 |
| kaptcha.noise.impl | 干擾實(shí)現(xiàn)類 | com.google.code.kaptcha.impl.DefaultNoise |
| kaptcha.noise.color | 干擾 顏色,合法值: r,g,b 或者 white,black,blue. | black |
| kaptcha.obscurificator.impl | 圖片樣式:<br />水紋 com.google.code.kaptcha.impl.WaterRipple <br /> 魚眼 com.google.code.kaptcha.impl.FishEyeGimpy <br /> 陰影 com.google.code.kaptcha.impl.ShadowGimpy | com.google.code.kaptcha.impl.WaterRipple |
| kaptcha.background.impl | 背景實(shí)現(xiàn)類 | com.google.code.kaptcha.impl.DefaultBackground |
| kaptcha.background.clear.from | 背景顏色漸變,開始顏色 | light grey |
| kaptcha.background.clear.to | 背景顏色漸變, 結(jié)束顏色 | white |
| kaptcha.word.impl | 文字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
| kaptcha.session.key | session key | KAPTCHA_SESSION_KEY |
| kaptcha.session.date | session date |
4.2 控制器層(Controller 層)
創(chuàng)建 CaptchaController 類(驗(yàn)證碼控制器),實(shí)現(xiàn)生成驗(yàn)證碼圖片方法。
package com.pjb.securitydemo.controller;
import com.google.code.kaptcha.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* 驗(yàn)證碼控制器
*/
@Controller
public class CaptchaController
{
@Autowired
private Producer captchaProducer;
@GetMapping("/captcha.jpg")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException
{
//設(shè)置內(nèi)容類型
response.setContentType("image/jpeg");
//創(chuàng)建驗(yàn)證碼文本
String capText = captchaProducer.createText();
//將驗(yàn)證碼文本保存到Session中
request.getSession().setAttribute("captcha", capText);
//創(chuàng)建驗(yàn)證碼圖片
BufferedImage bufferedImage = captchaProducer.createImage(capText);
//獲取響應(yīng)輸出流
ServletOutputStream out = response.getOutputStream();
//將圖片驗(yàn)證碼數(shù)據(jù)寫入響應(yīng)輸出流
ImageIO.write(bufferedImage,"jpg",out);
//推送并關(guān)閉響應(yīng)輸出流
try
{
out.flush();
}
finally
{
out.close();
}
}
}4.3 自定義異常類(Exception 層)
自定義異常類 VerificationCodeException(驗(yàn)證碼校驗(yàn)失敗的異常類),繼承 AuthenticationException 類。
package com.pjb.securitydemo.exception;
import org.springframework.security.core.AuthenticationException;
/**
* 驗(yàn)證碼校驗(yàn)失敗的異常類
*/
public class VerificationCodeException extends AuthenticationException
{
public VerificationCodeException()
{
super("圖形驗(yàn)證碼校驗(yàn)失敗");
}
}4.4 自定義過濾器(Filter 層)
自定義過濾器類 VerificationCodeFilter (驗(yàn)證碼校驗(yàn)過濾器),繼承 OncePerRequestFilter 類。
有了圖形驗(yàn)證碼的 API 之后,就可以自定義驗(yàn)證碼校驗(yàn)過濾器了。雖然 Spring Security 的過濾器鏈對(duì)過濾器沒有特殊要求,只要繼承 Filter 接口即可,但是在 Spring 體系中,推薦使用 OncePerRequestFilter 類來實(shí)現(xiàn),它可以確保一次請求只會(huì)通過一次該過濾器(Filter 實(shí)際上并不能保證這一點(diǎn))。
package com.pjb.securitydemo.filter;
import com.pjb.securitydemo.exception.VerificationCodeException;
import com.pjb.securitydemo.handler.LoginFailureHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.util.StringUtils;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 驗(yàn)證碼校驗(yàn)過濾器
*/
public class VerificationCodeFilter extends OncePerRequestFilter
{
private AuthenticationFailureHandler authenticationFailureHandler = new LoginFailureHandler();
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException
{
//非登錄請求不校驗(yàn)驗(yàn)證碼
String requestURI = httpServletRequest.getRequestURI();
if(!"/auth/form".equals(requestURI))
{
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
else
{
try
{
//驗(yàn)證碼校驗(yàn)
verificationCode(httpServletRequest);
//驗(yàn)證成功
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
catch (VerificationCodeException ex)
{
//驗(yàn)證失敗
authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, ex);
}
}
}
/**
* 驗(yàn)證碼校驗(yàn)
*/
public void verificationCode(HttpServletRequest httpServletRequest) throws VerificationCodeException
{
String requestCode = httpServletRequest.getParameter("captcha");
HttpSession session = httpServletRequest.getSession();
String savedCode = (String)session.getAttribute("captcha");
if(!StringUtils.isEmpty(savedCode))
{
//隨手清除驗(yàn)證碼,無論是失敗,還是成功。客戶端應(yīng)在登錄失敗時(shí)刷新驗(yàn)證碼
session.removeAttribute("captcha");
}
//驗(yàn)證不通過,拋出異常
if(StringUtils.isEmpty(requestCode) || StringUtils.isEmpty(savedCode) || !requestCode.equals(savedCode))
{
throw new VerificationCodeException();
}
}
}至此整合 Kaptcha 框架實(shí)現(xiàn)圖形驗(yàn)證碼已完成,最后注意,一定要把自定義過濾器類 VerificationCodeFilter 添加到 Spring Security 的過濾器鏈中。
打開 WebSecurityConfig 類(Spring Security 配置類),將自定義過濾器類 VerificationCodeFilter 添加到過濾器鏈中,如下:

5、前端頁面
5.1 控制器層(Controller 層)
創(chuàng)建 IndexController 類(首頁控制器),實(shí)現(xiàn)獲取當(dāng)前登錄用戶名并跳轉(zhuǎn)至首頁。
package com.pjb.securitydemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
/**
* 首頁控制器
* @author pan_junbiao
**/
@Controller
public class IndexController
{
/**
* 首頁
*/
@RequestMapping("/")
public String index(HttpServletRequest request)
{
//獲取當(dāng)前登錄人
String userName = "未登錄";
Principal principal = request.getUserPrincipal();
if(principal!=null)
{
userName = principal.getName();
}
//返回頁面
request.setAttribute("userName",userName);
return "/index.html";
}
}5.2 編寫登錄頁面
在 resources\static 靜態(tài)資源目錄下,創(chuàng)建 myLogin.html 頁面。
注意:myLogin.html 頁面必須放在 resources\static 靜態(tài)資源目錄下,否則頁面無法加載。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登錄</title>
<meta name="author" content="pan_junbiao的博客">
</head>
<body>
<form name="myForm" action="/auth/form" method="post">
<table align="center">
<caption>用戶登錄</caption>
<tr>
<td>登錄賬戶:</td>
<td>
<input type="text" name="username" placeholder="請輸入登錄賬戶" value="panjunbiao" />
</td>
</tr>
<tr>
<td>登錄密碼:</td>
<td>
<input type="password" name="password" placeholder="請輸入登錄密碼" value="123456" />
</td>
</tr>
<tr>
<td>驗(yàn)證碼:</td>
<td>
<!-- 新增圖形驗(yàn)證碼的輸入框 -->
<input type="text" name="captcha" placeholder="請輸入驗(yàn)證碼" />
<!-- 圖片指向圖形驗(yàn)證碼API -->
<img src="/captcha.jpg" alt="captch" height="50px" width="150px" style="margin-left:20px;" >
</td>
</tr>
<!-- 以下是提交、取消按鈕 -->
<tr>
<td colspan="2" style="text-align: center; padding: 5px;">
<input type="submit" value="提交" />
<input type="reset" value="重置" />
</td>
</tr>
</table>
</form>
</body>
</html>5.3 編寫首頁
在 resources\templates 資源目錄下,創(chuàng)建 index.html 頁面。
注意:首頁 index.html 頁面中使用 Thymeleaf 模板 。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首頁</title>
<meta name="author" content="pan_junbiao的博客">
</head>
<body>
<h1 style="color: red">Hello,Spring Security</h1>
<p>博客信息:您好,歡迎訪問 pan_junbiao的博客</p>
<p>博客地址:https://blog.csdn.net/pan_junbiao</p>
<p th:text="'當(dāng)前登錄人:' + ${userName}"></p>
<a href="/logout" rel="external nofollow" onclick="return confirm('確認(rèn)注銷嗎?');">登出</a>
</body>
</html>6、運(yùn)行項(xiàng)目
6.1 登錄頁面

6.2 圖形驗(yàn)證碼校驗(yàn)失敗

6.3 登錄成功后,跳轉(zhuǎn)至首頁

以上就是SpringBoot整合SpringSecurity實(shí)現(xiàn)圖形驗(yàn)證碼功能的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot SpringSecurity圖形驗(yàn)證碼的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)方式
這篇文章主要介紹了SpringBoot validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Spring Cloud Alibaba 本地調(diào)試介紹及方案設(shè)計(jì)
為了解決 本地調(diào)試 的問題,本文實(shí)現(xiàn)了一種簡單實(shí)用的策略,可以通過 Nacos 動(dòng)態(tài)配置服務(wù)路由,還可以基于用戶,部門,組織等級(jí)別配置服務(wù)路由,實(shí)現(xiàn) 本地調(diào)試 的同時(shí),實(shí)際上也實(shí)現(xiàn) 灰度發(fā)布,感興趣的朋友跟隨小編一起看看吧2021-07-07
java批量下載將多個(gè)文件(minio中存儲(chǔ))壓縮成一個(gè)zip包代碼示例
在Java應(yīng)用程序中有時(shí)我們需要從多個(gè)URL地址下載文件,并將這些文件打包成一個(gè)Zip文件進(jìn)行批量處理或傳輸,這篇文章主要給大家介紹了關(guān)于java批量下載將多個(gè)文件(minio中存儲(chǔ))壓縮成一個(gè)zip包的相關(guān)資料,需要的朋友可以參考下2023-11-11
Java中l(wèi)ambda表達(dá)式實(shí)現(xiàn)aop切面功能
本文主要介紹了Java中l(wèi)ambda表達(dá)式實(shí)現(xiàn)aop切面功能,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02

