First, the codes generated using kaptcha
kaptcha dependencies
<dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>
kaptcha configuration class
@Configuration public class KaptchaConfig { @Bean public Producer captcha(){ Properties properties = new Properties(); properties.setProperty("kaptcha.image.width", "120"); properties.setProperty("kaptcha.image.height", "45"); properties.setProperty("kaptcha.textproducer.char.string", "0123456789"); properties.setProperty("kaptcha.textproducer.char.length", "4"); Config config = new Config(properties); DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); defaultKaptcha.setConfig(config); return defaultKaptcha; } }
In ValidateCodeController
the verification picture to increase access interface
@RestController public class ValidateCodeController { @Autowired private Producer captchaProducer; @GetMapping("/captcha.jpg") public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("image/jpeg"); String text = captchaProducer.createText(); request.getSession().setAttribute("captcha", text); BufferedImage image = captchaProducer.createImage(text); ImageIO.write(image, "JPEG", response.getOutputStream()); }
Second, increasing the filter check codes
Spring security verification form is through a filter chain to complete, we add a filter codes should be inserted before, if the PIN verification is not passed, return directly without checking the account password. UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilter
Spring Security itself does not provide a verification code verification interface or an abstract class, developers need to realize themselves. Below implement a do verification code filter, the filter will inherit , this can ensure that each request the filter is called only once, because we call once is enough. ValidateCodeFilter
OncePerRequestFilter
@Component public class ValidateCodeFilter extends OncePerRequestFilter { @Autowired private MyAuthenticationFailureHandler authenticationFailureHandler; private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @Override protected void doFilterInternal (the HttpServletRequest Request, the HttpServletResponse Response, the FilterChain filterChain) throws ServletException, IOException { // authentication / request interface is the form, check codes only need to match the time of authentication interfaces IF (StringUtils.equals ( "/ authentication / form ", Request.getRequestURI ()) && StringUtils.equalsAnyIgnoreCase (request.getMethod (), " POST " )) { the try { validate(new ServletWebRequest(request)); } The catch (ValidateCodeException E) { // when the validation fails, the failure of the processor to handle authenticationFailureHandler.onAuthenticationFailure (Request, Response, E); return ; } } // no abnormal That verification is successful, release. filterChain.doFilter (request, response); } Private void the validate (ServletWebRequest Request) throws ValidateCodeException { // Get the session codes from the Object captcha = sessionStrategy.getAttribute (Request, "captcha" ); // received from the client's codes String captchaParam = request.getParameter ( " captcha " ); IF (StringUtils.isEmpty (captchaParam)) { the throw new new ValidateCodeException ( "codes can not be empty" ); } IF (captcha == null ) { the throw new new ValidateCodeException ( "codes does not exist" ); } IF (! StringUtils.equalsAnyIgnoreCase (captcha.toString (), captchaParam)) { the throw new new ValidateCodeException ( "authentication code mismatch" ); } // After the verification is successful, is removed from the session codes sessionStrategy.removeAttribute (Request, "captcha" ); } }
Third, the filter is inserted before UsernamePasswordAuthenticationFilter
@Override protected void configure(HttpSecurity http) throws Exception { http .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) .formLogin () .loginPage ( "myLogin.html") // login page .loginProcessingUrl ( "/ authentication / form") // front-end to back-end initiates an authentication path .successHandler (authenticationSuccessHandler) .failureHandler(authenticationFailureHandler) .and() .authorizeRequests() .antMatchers ( "myLogin.html", "/captcha.jpg"). permitAll () // When a match to / authentication / require, without having to authentication .anyRequest () .authenticated() .and() .csrf().disable(); }
Front page:
<form action="/authentication/form" method="post"> 账户:<input type="text" name="username" /> <br> 密码:<input type="text" name="password" /> <br> 验证码:<input type="text" name="captcha"> <img src="/captcha.jpg" alt="#"> <br> <input type="submit" value="登录"> </form>