SpringBoot + SpringSecurity 實現圖形驗證碼功能

生成圖形驗證碼

  1. 根據隨機數生成圖片
  2. 將隨機數存到Session中
  3. 將生成的圖片寫到接口的響應中

生成圖形驗證碼的過程比較簡單,和SpringSecurity也沒有什麼關係。所以就直接貼出代碼了

根據隨機數生成圖片

/**
 * 生成圖形驗證碼
 * @param request
 * @return
 */
private ImageCode generate(ServletWebRequest request) {
    int width = 64;
    int height = 32;
    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 < 4; 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, 60);

}

/**
 * 生成隨機背景條紋
 *
 * @param fc
 * @param bc
 * @return
 */
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);
}

將隨機數存到Session中 && 將生成的圖片寫到接口的響應中

@RestController
public class ValidateCodeController {

    public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";

    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ImageCode imageCode = generate(new ServletWebRequest(request));
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }
}

在認證流程中加入圖形驗證碼

SpringSecurity認證流程詳解中,我們有講到,SpringSecurity是通過過濾器鏈來進行校驗的,我們想要驗證圖形驗證碼,所以可以在認證流程之前,也就是UsernamePasswordAuthenticationFilter之前進行校驗。

自定義圖形驗證碼的過濾器

@Component
public class ValidateCodeFilter extends OncePerRequestFilter {

    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    private AuthenticationFailureHandler authenticationFailureHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        if(StringUtils.equals("/user/login", httpServletRequest.getRequestURI())
                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), "post")) {

            try {
                // 1. 進行驗證碼的校驗
                validate(new ServletWebRequest(httpServletRequest));
            } catch (ValidateCodeException e) {
                // 2. 如果校驗不通過,調用SpringSecurity的校驗失敗處理器
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
                return ;
            }
        }
        // 3. 校驗通過,就放行
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}    

這裏驗證碼校驗的過程比較簡單,主要就是判斷傳過來的參數和Session中保存的是否一致,以及Session中的驗證碼是否過期了。

有了自己的驗證碼過濾器之後,我們還需要將它配置在UsernamePasswordAuthenticationFilter之前:

@Override
protected void configure(HttpSecurity http) throws Exception {
    ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
    validateCodeFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
    // 將我們自定義的過濾器,配置到UsernamePasswordAuthenticationFilter之前
    http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
            .formLogin()                    //  定義當需要用戶登錄時候,轉到的登錄頁面。
            // 後面的配置省略
}                

代碼下載

Spring-Security

發佈了98 篇原創文章 · 獲贊 97 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章