Spring Security實現(xiàn)驗證碼登錄功能
這篇文章主要介紹了Spring Security實現(xiàn)驗證碼登錄功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
在spring security實現(xiàn)登錄注銷功能的基礎(chǔ)上進(jìn)行開發(fā)。
1、添加生成驗證碼的控制器。
(1)、生成驗證碼
/**
* 引入 Security 配置屬性類
*/
@Autowired
private SecurityProperties securityProperties;
@Override
public ImageCode createCode(HttpServletRequest request ) {
//如果請求中有 width 參數(shù),則用請求中的,否則用 配置屬性中的
int width = ServletRequestUtils.getIntParameter(request,"width",securityProperties.getWidth());
//高度(寬度)
int height = ServletRequestUtils.getIntParameter(request,"height",securityProperties.getHeight());
//圖片驗證碼字符個數(shù)
int length = securityProperties.getLength();
//過期時間
int expireIn = securityProperties.getExpireIn();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
Random random = new Random();
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
g.setFont(new Font("Times New Roman", Font.ITALIC, 20));
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
String sRand = "";
for (int i = 0; i < length; i++) {
String rand = String.valueOf(random.nextInt(10));
sRand += rand;
g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
g.drawString(rand, 13 * i + 6, 16);
}
g.dispose();
return new ImageCode(image, sRand, expireIn);
}
/**
* 生成隨機背景條紋
*/
private Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
(2)、驗證碼控制器
public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
@Autowired
private ValidateCodeGenerator imageCodeGenerator;
/**
* Session 對象
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
@GetMapping("/code/image")
public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
ImageCode imageCode = imageCodeGenerator.createCode(request);
//將隨機數(shù) 放到Session中
sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);
request.getSession().setAttribute(SESSION_KEY,imageCode);
//寫給response 響應(yīng)
response.setHeader("Cache-Control", "no-store, no-cache");
response.setContentType("image/jpeg");
ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
}
(3)、其它輔助類
@Data
public class ImageCode {
/**
* 圖片
*/
private BufferedImage image;
/**
* 隨機數(shù)
*/
private String code;
/**
* 過期時間
*/
private LocalDateTime expireTime;
public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
this.image = image;
this.code = code;
this.expireTime = expireTime;
}
public ImageCode(BufferedImage image, String code, int expireIn) {
this.image = image;
this.code = code;
//當(dāng)前時間 加上 設(shè)置過期的時間
this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
}
public boolean isExpried(){
//如果 過期時間 在 當(dāng)前日期 之前,則驗證碼過期
return LocalDateTime.now().isAfter(expireTime);
}
}
@ConfigurationProperties(prefix = "sso.security.code.image")
@Component
@Data
public class SecurityProperties {
/**
* 驗證碼寬度
*/
private int width = 67;
/**
* 高度
*/
private int height = 23;
/**
* 長度(幾個數(shù)字)
*/
private int length = 4;
/**
* 過期時間
*/
private int expireIn = 60;
/**
* 需要圖形驗證碼的 url
*/
private String url;
}
(4)、驗證

2、添加過濾器,進(jìn)行驗證碼驗證
@Component
@Slf4j
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
/**
* 登錄失敗處理器
*/
@Autowired
private AuthenticationFailureHandler failureHandler;
/**
* Session 對象
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
/**
* 創(chuàng)建一個Set 集合 存放 需要驗證碼的 urls
*/
private Set<String> urls = new HashSet<>();
/**
* spring的一個工具類:用來判斷 兩字符串 是否匹配
*/
private AntPathMatcher pathMatcher = new AntPathMatcher();
@Autowired
private SecurityProperties securityProperties;
/**
* 這個方法是 InitializingBean 接口下的一個方法, 在初始化配置完成后 運行此方法
*/
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
//將 application 配置中的 url 屬性進(jìn)行 切割
String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getUrl(), ",");
//添加到 Set 集合里
urls.addAll(Arrays.asList(configUrls));
//因為登錄請求一定要有驗證碼 ,所以直接 add 到set 集合中
urls.add("/authentication/form");
}
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
boolean action = false;
for (String url:urls){
//如果請求的url 和 配置中的url 相匹配
if (pathMatcher.match(url,httpServletRequest.getRequestURI())){
action = true;
}
}
//攔截請求
if (action){
logger.info("攔截成功"+httpServletRequest.getRequestURI());
//如果是登錄請求
try {
validate(new ServletWebRequest(httpServletRequest));
}catch (ValidateCodeException exception){
//返回錯誤信息給 失敗處理器
failureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,exception);
return;
}
}
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
private void validate(ServletWebRequest request) throws ServletRequestBindingException {
//從session中取出 驗證碼
ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY);
//從request 請求中 取出 驗證碼
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");
if (StringUtils.isBlank(codeInRequest)){
logger.info("驗證碼不能為空");
throw new ValidateCodeException("驗證碼不能為空");
}
if (codeInSession == null){
logger.info("驗證碼不存在");
throw new ValidateCodeException("驗證碼不存在");
}
if (codeInSession.isExpried()){
logger.info("驗證碼已過期");
sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
throw new ValidateCodeException("驗證碼已過期");
}
if (!StringUtils.equals(codeInSession.getCode(),codeInRequest)){
logger.info("驗證碼不匹配"+"codeInSession:"+codeInSession.getCode() +", codeInRequest:"+codeInRequest);
throw new ValidateCodeException("驗證碼不匹配");
}
//把對應(yīng) 的 session信息 刪掉
sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
}
3、在核心配置BrowserSecurityConfig中添加過濾器配置
@Autowired
private ValidateCodeFilter validateCodeFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
//在UsernamePasswordAuthenticationFilter 過濾器前 加一個過濾器 來搞驗證碼
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
//表單登錄 方式
.formLogin()
.loginPage("/authentication/require")
//登錄需要經(jīng)過的url請求
.loginProcessingUrl("/authentication/form")
.passwordParameter("pwd")
.usernameParameter("user")
.successHandler(mySuccessHandler)
.failureHandler(myFailHandler)
.and()
//請求授權(quán)
.authorizeRequests()
//不需要權(quán)限認(rèn)證的url
.antMatchers("/authentication/*","/code/image").permitAll()
//任何請求
.anyRequest()
//需要身份認(rèn)證
.authenticated()
.and()
//關(guān)閉跨站請求防護
.csrf().disable();
//默認(rèn)注銷地址:/logout
http.logout().
//注銷之后 跳轉(zhuǎn)的頁面
logoutSuccessUrl("/authentication/require");
}
4、異常輔助類
public class ValidateCodeException extends AuthenticationException {
public ValidateCodeException(String msg, Throwable t) {
super(msg, t);
}
public ValidateCodeException(String msg) {
super(msg);
}
}
5、測試
(1)、不輸入驗證碼

(2)、添加驗證碼

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java中對AtomicInteger和int值在多線程下遞增操作的測試
這篇文章主要介紹了Java中對AtomicInteger和int值在多線程下遞增操作的測試,本文得出AtomicInteger操作 與 int操作的效率大致相差在50-80倍上下的結(jié)論,需要的朋友可以參考下2014-09-09
Java多線程中不同條件下編寫生產(chǎn)消費者模型方法介紹
這篇文章主要介紹了Java多線程中不同條件下編寫生產(chǎn)消費者模型方法介紹,介紹了生產(chǎn)消費者模型,然后分享了相關(guān)代碼示例,具有一定參考價值,需要的朋友可以了解下。2017-11-11
Mybatis-Plus接口BaseMapper與Services使用詳解
這篇文章主要為大家介紹了Mybatis-Plus接口BaseMapper與Services使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
Spring?+?ECharts實現(xiàn)數(shù)據(jù)可視化的案例詳解
Apache?ECharts是一個基于?JavaScript?的開源可視化圖表庫,在網(wǎng)頁上實現(xiàn)數(shù)據(jù)的可視化,非常好用,本文將通過一個簡單的demo來給大家介紹一下Spring?+?ECharts如何數(shù)據(jù)可視化,需要的朋友可以參考下2023-07-07

