Spring Security登入時加上圖形驗證碼
                    
                    
            
                        把驗證碼存到Session來簡單防範攻擊
                    
                    
                    
                Spring Security登入加上圖形驗證碼
- 效果
 

前端
- 
帳號密碼+驗證碼很單純不多說
 - 
點圖片刷新:
 - 
function reloadCaptcha() { $("#captcha").attr("src", "{登入的url}/getCaptcha?t=" + new Date().getTime()); } 
獲取驗證碼圖片
- 將密碼存到Session,也可以存到Redis,這邊用最簡單的方法
 
	/**
	 * 獲取驗證碼圖片
	 */
	@GetMapping("/getCaptcha")
	public void getCaptcha(HttpServletRequest request,HttpServletResponse response) throws IOException {
		// 生成驗證碼圖片,寬 高
		ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(160, 45, 4, 3);
		// 4位純數字,去除0跟7
		RandomGenerator randomGenerator = new RandomGenerator("12345689", 4);
		captcha.setGenerator(randomGenerator);
		// Console.log("驗證碼code: "+ captcha.getCode());
		// 指定響應頭給瀏覽器
		response.setContentType("image/png"); // 告訴瀏覽器輸出內容為圖片
		response.setHeader("Pragma", "No-cache"); // 禁止瀏覽器緩存
		response.setHeader("Cache-Control", "no-cache");
		request.getSession().setAttribute("captchaCode", captcha.getCode());
		// 输出流给前端
		captcha.write(response.getOutputStream());
	}
Exception
public class VerificationCodeException extends AuthenticationException {
    public VerificationCodeException(){
        super("驗證碼校驗失敗");
    }
}
Filter
/**
 * 登入時的圖形驗證碼過濾器
 */
@Component
public class VerificationCodeFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (!"/login".equals(request.getRequestURI())) {
            filterChain.doFilter(request, response);
        } else {
            try {
                verificationCode(request);
                filterChain.doFilter(request, response);
            } catch (VerificationCodeException e) {
                new FormLoginFailureHandler().onAuthenticationFailure(request, response, e);
            }
        }
    }
    private void verificationCode(HttpServletRequest request){
        String captcha = request.getParameter("captchaCode");
        HttpSession session = request.getSession();
        String saveCaptcha = (String) session.getAttribute("captchaCode");
        session.removeAttribute("captchaCode");
        if (ObjectUtils.isEmpty(captcha) || ObjectUtils.isEmpty(saveCaptcha) || !captcha.equals(saveCaptcha)) {
            throw new VerificationCodeException();
        }
    }
}
Handler
@Component
@Slf4j
public class VerificationCodeFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        log.info("login fail, msg: {}", exception.getMessage());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSON.toJSONString(ResultVO.error(10000, exception.getMessage())));
    }
}
SecurityConfig
- 注意至少要先設定登入頁與getCaptcha是可以無條件通過的
 
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests() //
                    .antMatchers( //
                    ).permitAll().anyRequest().authenticated();
            http.csrf() //
                    .ignoringAntMatchers( //
                    ).csrfTokenRepository(csrfTokenRepository());
            http.sessionManagement() //
                    .sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
            http.rememberMe();
            // 檢查圖形驗證碼
            if (LOGIN_CAPTCHA){
                http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
            }
上次修改於 2023-01-15