3 springboot+security增加圖形驗證碼

增加圖形驗證碼

本文依據學習編碼而來
增加圖形類ImageCode

public class ImageCode {

    private BufferedImage image;

    private String code;

    private LocalDateTime expireTime;

    public ImageCode(BufferedImage image, String code, int expireIn) {
        this.image = image;
        this.code = code;
        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
    }

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

    public boolean isExpire(){
        return LocalDateTime.now().isAfter(this.expireTime);
    }

    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 getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(LocalDateTime expireTime) {
        this.expireTime = expireTime;
    }


    @Override
    public String toString() {
        return "ImageCode{" +
                "image=" + image +
                ", code='" + code + '\'' +
                ", expireTime=" + expireTime +
                '}';
    }
}

增加校驗驗證類

@RestController
public class ValidateController {

    public final static String SESSION_KEY_IMAGE_CODE="SESSION_KEY_IMAGE_CODE";

    private SessionStrategy sessionStrategy=new HttpSessionSessionStrategy();

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

    private ImageCode createImageCode(){
        //驗證碼圖片寬度
        int width=100;
        //驗證碼圖片高度
        int height=36;
        //驗證碼位數
        int length=4;
        //驗證碼過期時間
        int expireIn=60;
        //生成圖片
        BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        Graphics g=image.createGraphics();

        //產生隨機數
        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 x1=random.nextInt(12);
            int y1=random.nextInt(12);

            g.drawLine(x,y,x1,y1);
        }

        StringBuilder sb=new StringBuilder();

        for(int i=0;i<length;i++){
            String rand=String.valueOf(random.nextInt(10));
            sb.append(rand);
            g.setColor(getRandColor(110,130));
            g.drawString(rand,13+i*12,16);
        }

        g.dispose();
        return new ImageCode(image,sb.toString(),expireIn);

    }

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

}

通過IOImage.wirte方法來實現將圖片綁定到輸出流中

 
     * Writes an image using an arbitrary <code>ImageWriter</code>
     * that supports the given format to an <code>OutputStream</code>.
     *
     * <p> This method <em>does not</em> close the provided
     * <code>OutputStream</code> after the write operation has completed;
     * it is the responsibility of the caller to close the stream, if desired.
     *
     * <p> The current cache settings from <code>getUseCache</code>and
     * <code>getCacheDirectory</code> will be used to control caching.
     *
     * @param im a <code>RenderedImage</code> to be written.
     * @param formatName a <code>String</code> containing the informal
     * name of the format.
     * @param output an <code>OutputStream</code> to be written to.
     *
     * @return <code>false</code> if no appropriate writer is found.
     *
     * @exception IllegalArgumentException if any parameter is
     * <code>null</code>.
     * @exception IOException if an error occurs during writing.
     */
    public static boolean write(RenderedImage im,
                                String formatName,
                                OutputStream output) throws IOException {

將獲取驗證碼方法的Url地址放到免於驗證的地址中

 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
       // http
                .formLogin()//表單認證
                .loginPage("/auth/require")
                .loginProcessingUrl("/login")
                .successHandler(customAuthSuccessHandler)
                .failureHandler(customAuthFailHandler)
                .and()
                .authorizeRequests()//授權配置
                .antMatchers("/login.html",
                        "/auth/require",
                        "/code/image").permitAll()
                .anyRequest()//所有請求
                .authenticated()//都需要認證
                .and()
                .csrf().disable();

增加驗證碼校驗,判斷驗證碼是否有效


@Component
public class ValidateCodeFilter extends OncePerRequestFilter {

   Logger logger= LoggerFactory.getLogger(getClass());

    private AuthenticationFailureHandler authenticationFailureHandler;

    private SessionStrategy sessionStrategy=new HttpSessionSessionStrategy();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        if(StringUtils.pathEquals("/login",request.getRequestURI()) && "post".equalsIgnoreCase(request.getMethod()))
        {
            try {
                validateCode(new ServletWebRequest(request));
            }
            catch (ValidateCodeException err){
                authenticationFailureHandler.onAuthenticationFailure(request,response,err);
                return;
            }
        }

        filterChain.doFilter(request,response);
    }


    private void validateCode(ServletWebRequest request) throws ServletRequestBindingException, ValidateCodeException {
        ImageCode image=(ImageCode) sessionStrategy.getAttribute(request, ValidateController.SESSION_KEY_IMAGE_CODE);

        logger.info("imagecode is ..."+image.getCode());
        logger.info("imagecode expiretime is "+image.getExpireTime());

        String codeInRequest= ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");

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

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

        if(image.isExpire())
        {
            sessionStrategy.removeAttribute(request,ValidateController.SESSION_KEY_IMAGE_CODE);
            throw new ValidateCodeException("驗證碼已過期");
        }

        logger.info("用戶輸入的驗證碼是"+codeInRequest);

        if(!codeInRequest.equalsIgnoreCase(image.getCode()))
        {
            throw new ValidateCodeException("驗證碼不匹配");
        }

        sessionStrategy.removeAttribute(request,ValidateController.SESSION_KEY_IMAGE_CODE);

    }

    public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
        this.authenticationFailureHandler = authenticationFailureHandler;
    }
}

還需要在html中增加圖片顯示

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>登錄</title>
</head>
<body>
<form class="login-page" action="/login" method="post">
    <div class="form">
        <h3>賬戶登錄</h3>
        <input type="text" placeholder="用戶名" name="username" required="required" />
        <input type="password" placeholder="密碼" name="password" required="required" />
        <span style="display: inline">
            <input type="text" name="imageCode" placeholder="驗證碼" style="width: 50%;">
            <img src="/code/image">
        </span>
        <button type="submit">登錄</button>
    </div>
</form>
</body>
</html>

測試
在這裏插入圖片描述
輸入錯誤的驗證碼
在這裏插入圖片描述
日誌打印在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章