Spring Security增加驗證碼校驗


一、使用kaptcha生成驗證碼

kaptcha依賴包

<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

 

kaptcha配置類

@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;
    }
}

 

ValidateCodeController中增加驗證碼圖片的訪問接口

@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());
}

 

二、增加驗證碼校驗過濾器

Spring security的表單驗證是通過過濾器鏈中的 UsernamePasswordAuthenticationFilter 來完成的,我們增加的驗證碼過濾器應該插在 UsernamePasswordAuthenticationFilter 之前,如果驗證碼校驗不通過,直接返回,無需進行賬戶密碼的校驗。

Spring Security本身沒有提供驗證碼校驗的接口或者抽象類,需要開發人員自己去實現。下面實現一個 ValidateCodeFilter 來做驗證碼的過濾器,該過濾器將繼承 OncePerRequestFilter,這個可以保證每次請求只調用一次該過濾器,因為我們調用一次就夠了。

 

@Component
public class ValidateCodeFilter extends OncePerRequestFilter {

    @Autowired
    private MyAuthenticationFailureHandler authenticationFailureHandler;

    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // authentication/form是認證時的請求接口,驗證碼校驗只需要匹配這個接口即可
        if (StringUtils.equals("/authentication/form", request.getRequestURI()) &&
                StringUtils.equalsAnyIgnoreCase(request.getMethod(), "post")) {
            try {
                validate(new ServletWebRequest(request));
            } catch (ValidateCodeException e) {
            // 校驗失敗時,讓失敗的處理器去處理authenticationFailureHandler.onAuthenticationFailure(request, response, e);
                return;
            }
        }
        // 無異常即校驗成功,放行。
        filterChain.doFilter(request, response);

    }

    private void validate(ServletWebRequest request) throws ValidateCodeException {
        // 從session中獲取驗證碼
        Object captcha = sessionStrategy.getAttribute(request, "captcha");
        // 從客戶端接收到的驗證碼
        String captchaParam = request.getParameter("captcha");

        if (StringUtils.isEmpty(captchaParam)) {
            throw new ValidateCodeException("驗證碼不能為空");
        }

        if (captcha == null) {
            throw new ValidateCodeException("驗證碼不存在");
        }

        if (!StringUtils.equalsAnyIgnoreCase(captcha.toString(), captchaParam)) {
            throw new ValidateCodeException("驗證碼不匹配");
        }
        // 校驗成功之后,從session中移除驗證碼
        sessionStrategy.removeAttribute(request,"captcha");
    }
}

 

三、將過濾器插入到 UsernamePasswordAuthenticationFilter 之前

 

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
        .formLogin()
        .loginPage("myLogin.html")   // 登錄頁面
        .loginProcessingUrl("/authentication/form") //前端向后端發起認證的路徑
        .successHandler(authenticationSuccessHandler)
        .failureHandler(authenticationFailureHandler)
        .and()
        .authorizeRequests()
        .antMatchers("myLogin.html", "/captcha.jpg").permitAll()   //當匹配到/authentication/require時,無需身份認證
        .anyRequest()
        .authenticated()
        .and()
        .csrf().disable();
}

 

前端頁面:

 

<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>

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM