欢迎回家
我们一直在改变

SpringSecurity登录添加验证码

登录添加验证码是一个非常常见的需求,网上也有非常成熟的解决方案,其实,要是自己自定义登录实现这个并不难,但是如果需要在SpringSecurity框架中实现这个功能,还得稍费一点功夫,本文就和小伙伴来分享下在SpringSecurity框架中如何添加验证码。

准备验证码

要有验证码,首先得先准备好验证码,本文采用Java自画的验证码,代码如下:

/**
 * 生成验证码的工具类
 */
public class VerifyCode {
    private int width = 100;// 生成验证码图片的宽度    private int height = 50;// 生成验证码图片的高度    private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };    private Color bgColor = new Color(255, 255, 255);// 定义验证码图片的背景颜色为白色    private Random random = new Random();    private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";    private String text;// 记录随机字符串

    /**
     * 获取一个随意颜色     *      * @return
     */
    private Color randomColor() {
        int red = random.nextInt(150);
        int green = random.nextInt(150);
        int blue = random.nextInt(150);
        return new Color(red, green, blue);
    }

    /**
     * 获取一个随机字体     *      * @return
     */
    private Font randomFont() {
        String name = fontNames[random.nextInt(fontNames.length)];
        int style = random.nextInt(4);
        int size = random.nextInt(5) + 24;
        return new Font(name, style, size);
    }

    /**
     * 获取一个随机字符     *      * @return
     */
    private char randomChar() {
        return codes.charAt(random.nextInt(codes.length()));
    }

    /**
     * 创建一个空白的BufferedImage对象     *      * @return
     */
    private BufferedImage createImage() {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        g2.setColor(bgColor);// 设置验证码图片的背景颜色        g2.fillRect(0, 0, width, height);        return image;    }
        public BufferedImage getImage () {
            BufferedImage image = createImage();
            Graphics2D g2 = (Graphics2D) image.getGraphics();
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 4; i++) {
                String s = randomChar() + "";
                sb.append(s);
                g2.setColor(randomColor());
                g2.setFont(randomFont());
                float x = i * width * 1.0f / 4;
                g2.drawString(s, x, height - 15);
            }
            this.text = sb.toString();
            drawLine(image);
            return image;
        }
        /**     * 绘制干扰线     *      * @param image     */private void drawLine (BufferedImage image){
            Graphics2D g2 = (Graphics2D) image.getGraphics();
            int num = 5;
            for (int i = 0; i < num; i++) {
                int x1 = random.nextInt(width);
                int y1 = random.nextInt(height);
                int x2 = random.nextInt(width);
                int y2 = random.nextInt(height);
                g2.setColor(randomColor());
                g2.setStroke(new BasicStroke(1.5f));
                g2.drawLine(x1, y1, x2, y2);
            }
        }
        public String getText () {
            return text;
        }
        public static void output (BufferedImage image, OutputStream out) throws IOException {
            ImageIO.write(image, "JPEG", out);
        }
    }

这个工具类很常见,网上也有很多,就是画一个简单的验证码,通过流将验证码写到前端页面,提供验证码的Controller如下:

@RestControllerpublic
class VerifyCodeController {
    @GetMapping("/vercode")
    public void code(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        VerifyCode vc = new VerifyCode();
        BufferedImage image = vc.getImage();
        String text = vc.getText();
        HttpSession session = req.getSession();
        session.setAttribute("index_code", text);
        VerifyCode.output(image, resp.getOutputStream());
    }
}

这里创建了一个VerifyCode对象,将生成的验证码字符保存到session中,然后通过流将图片写到前端,img标签如下:

<img src="/vercode" alt="">

展示效果如下:

自定义过滤器

在登陆页展示验证码这个就不需要我多说了,接下来我们来看看如何自定义验证码处理器:

@Componentpublic
class VerifyCodeFilter extends GenericFilterBean {
    private String defaultFilterProcessUrl = "/doLogin";

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        if ("POST".equalsIgnoreCase(request.getMethod()) && defaultFilterProcessUrl.equals(request.getServletPath())) {            // 验证码验证            String requestCaptcha = request.getParameter("code");            String genCaptcha = (String) request.getSession().getAttribute("index_code");            if (StringUtils.isEmpty(requestCaptcha))                throw new AuthenticationServiceException("验证码不能为空!");            if (!genCaptcha.toLowerCase().equals(requestCaptcha.toLowerCase())) {                throw new AuthenticationServiceException("验证码错误!");            }        }        chain.doFilter(request, response);    }
        }
    }

自定义过滤器继承自GenericFilterBean,并实现其中的doFilter方法,在doFilter方法中,当请求方法是POST,并且请求地址是 /doLogin时,获取参数中的code字段值,该字段保存了用户从前端页面传来的验证码,然后获取session中保存的验证码,如果用户没有传来验证码,则抛出验证码不能为空异常,如果用户传入了验证码,则判断验证码是否正确,如果不正确则抛出异常,否则执行 chain.doFilter(request,response);使请求继续向下走。

配置

最后在SpringSecurity的配置中,配置过滤器,如下:

@Configurationpublic
class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    VerifyCodeFilter verifyCodeFilter;    ......

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.authorizeRequests().antMatchers("/admin/**").hasRole("admin")                ...                ...                .
        permitAll().and().csrf().disable();
    }
}

这里只贴出了部分核心代码,即 http.addFilterBefore(verifyCodeFilter,UsernamePasswordAuthenticationFilter.class);,如此之后,整个配置就算完成了。
接下来在登录中,就需要传入验证码了,如果不传或者传错,都会抛出异常,例如不传的话,抛出如下异常:

阅读原文

赞(0)
未经允许不得转载:91coding » SpringSecurity登录添加验证码
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

立即登录   注册

91CODING 小白轻松上手,大牛稳健进步

关于我们免责声明