1. 程式人生 > >Spring Security 實現圖片驗證碼登陸(一)

Spring Security 實現圖片驗證碼登陸(一)

生成圖形驗證碼
根據隨機數生成圖片
將隨機數存到session中
在將生成的圖片寫到介面的響應中

在收到服務請求之後 根據隨機數生成圖片 ,然後把隨機數存入到session中,在提交表單的時候從session中把隨機數拿出來,和使用者填寫的驗證碼做一個比對

/**
 * 封裝驗證碼資訊
 * Created by ZhuPengWei on 2017/12/1.
 */
public class ImageCode {

    /**
     * 圖片 展示用
     */
    private BufferedImage image;
    /**
     * 隨機數
     */
private String code; /** * 過期時間 */ private LocalDateTime localDateTime; public ImageCode(BufferedImage image, String code, int second) { this.image = image; this.code = code; // 多少秒後 this.localDateTime = LocalDateTime.now().plusSeconds(second); } public
ImageCode(BufferedImage image, String code, LocalDateTime localDateTime) { this.image = image; this.code = code; this.localDateTime = localDateTime; } public BufferedImage getImage() { return image; } public void setImage(BufferedImage image) { this
.image = image; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public LocalDateTime getLocalDateTime() { return localDateTime; } public void setLocalDateTime(LocalDateTime localDateTime) { this.localDateTime = localDateTime; } }
@RestController
public class ValidateCodeController {
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    private static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
    @GetMapping("/code/image")
        public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1.根據隨機數生成圖片
        ImageCode imageCode = createImageCode(request);
        // 2.將圖片存入session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
        // 3.將生成的圖片寫入到介面響應中
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }
    private ImageCode createImageCode(HttpServletRequest request) {
        int width = 67;
        int height = 23;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        Random random = new Random();
        // 設定圖片背景色
        g.setColor(getRandColor(200, 500));
        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);
}
}

自定義過濾器

/**
 * 封裝驗證碼資訊
 * Created by ZhuPengWei on 2017/12/1.
 */
public class ImageCode {

    /**
     * 圖片 展示用
     */
    private BufferedImage image;
    /**
     * 隨機數
     */
    private String code;
    /**
     * 過期時間
     */
    private LocalDateTime localDateTime;

    public ImageCode(BufferedImage image, String code, int second) {
        this.image = image;
        this.code = code;
        // 多少秒後
        this.localDateTime = LocalDateTime.now().plusSeconds(second);
    }

    public ImageCode(BufferedImage image, String code, LocalDateTime localDateTime) {
        this.image = image;
        this.code = code;
        this.localDateTime = localDateTime;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        this.image = image;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public LocalDateTime getLocalDateTime() {
        return localDateTime;
    }

    public void setLocalDateTime(LocalDateTime localDateTime) {
        this.localDateTime = localDateTime;
    }
}

@RestController
public class ValidateCodeController {
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    private static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
    @GetMapping("/code/image")
        public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1.根據隨機數生成圖片
        ImageCode imageCode = createImageCode(request);
        // 2.將圖片存入session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
        // 3.將生成的圖片寫入到介面響應中
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }
    private ImageCode createImageCode(HttpServletRequest request) {
        int width = 67;
        int height = 23;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        Random random = new Random();
        // 設定圖片背景色
        g.setColor(getRandColor(200, 500));
        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);
    }
}
@RestController
public class ValidateCodeController {
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    private static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
    @GetMapping("/code/image")
        public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1.根據隨機數生成圖片
        ImageCode imageCode = createImageCode(request);
        // 2.將圖片存入session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
        // 3.將生成的圖片寫入到介面響應中
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }
    private ImageCode createImageCode(HttpServletRequest request) {
        int width = 67;
        int height = 23;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        Random random = new Random();
        // 設定圖片背景色
        g.setColor(getRandColor(200, 500));
        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);
    }
}

自定義過濾器

/**
 * OncePerRequestFilter顧名思義,他能夠確保在一次請求只通過一次filter,而不需要重複執行
 * Created by ZhuPengWei on 2017/12/1.
 */
public class ValidationCodeFilter extends OncePerRequestFilter {
    private AuthenticationFailureHandler authenticationFailureHandler;
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (StringUtils.equals("/code/image", request.getRequestURI())
        && StringUtils.equalsIgnoreCase("post", request.getMethod())) {
            try {
                validate(new ServletWebRequest(request));
            }
            catch (ValidateCodeException e) {
                authenticationFailureHandler.onAuthenticationFailure(request, response, e);
                // 不繼續執行
                return;
            }
        }
        // 繼續執行下一步
        filterChain.doFilter(request, response);
    }
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        // 從Session中獲取imageCode物件
        ImageCode imageCode = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY);
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException("驗證碼為空或者不存在");
        }
        if (imageCode.isExpire()) {
            throw new ValidateCodeException("驗證碼過期");
        }
        if (!StringUtils.equals(codeInRequest, imageCode.getCode())) {
            throw new ValidateCodeException("驗證碼不匹配");
        }
        // session 中移除key
        sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
    }
}

自定義異常

/**
 * 自定義異常
 * Created by ZhuPengWei on 2017/12/1.
 */
public class ValidateCodeException extends AuthenticationException {
    public ValidateCodeException(String msg) {
        super(msg);
    }
}
/**
 * security配置
 * Created by ZhuPengWei on 2017/11/27.
 */
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    /* 處理密碼加密解密邏輯 */
    @Bean
    public PasswordEncoder passwordEncoder()
    {
        return(new BCryptPasswordEncoder() );
    }


    @Autowired
    private SecurityProperties securityProperties;
    @Autowired
    private SelfDefineAuthenticationFailureHandler selfDefineAuthenticationFailureHandler;
    @Autowired
    private SelfDefineAuthenticationSuccessHandler selfDefineAuthenticationSuccessHandler;
    @Override
    protected void configure( HttpSecurity http ) throws Exception
    {
        ValidationCodeFilter validationCodeFilter = new ValidationCodeFilter();
        /* 設定錯誤失敗處理器 */
        validationCodeFilter.setAuthenticationFailureHandler( selfDefineAuthenticationFailureHandler );
        http.addFilterBefore( validationCodeFilter, UsernamePasswordAuthenticationFilter.class )
        .formLogin()            /* 表單登陸 */
        /* .loginPage("/index_standard.html") // 自定義登陸頁面 */
        .loginPage( "/authentication/require" ).permitAll()
        .loginProcessingUrl( "/authentication/form" )
        .successHandler( selfDefineAuthenticationSuccessHandler )
        .failureHandler( selfDefineAuthenticationFailureHandler )
        .and()
        .authorizeRequests()    /* 請求授權 */
        .antMatchers( "/index_standard.html"
                  , securityProperties.getBrowserProperties().getLoginPage()
                  , "/code/image" )
        .permitAll()            /* 匹配頁面 */
        .anyRequest()           /* 任何請求 */
        .authenticated()        /* 都需要認證 */
        .and().csrf().disable();
        /* 關閉跨站請求攻擊 */
    }
}