Spring Security 图片验证码功能的实例代码

下面我会给出关于“Spring Security 图片验证码功能的实例代码”的详细攻略。首先,我们需要思考一下问题,什么是图片验证码,为什么要使用它。

图片验证码就是在需要用户输入验证码时,生成一张随机的图片,用户需要识别图片中的验证码才能通过验证。由于图片验证码中的验证码是随机生成的,所以可以有效地避免机器人或爬虫等自动化程序的攻击。

在Spring Security中,我们可以使用项目中已经集成的验证码组件,或是自定义生成验证码的方法。下面给出两个示例,帮助大家更好地理解Spring Security中如何实现验证码功能。

示例一:使用项目中集成的验证码组件

首先,我们编写一个自定义的 AuthenticationProvider,用来验证用户输入的用户名和密码是否正确。

public class MyAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        // 省略从数据库中读取用户信息的代码

        // 如果用户不存在,或者密码不正确,抛出异常
        if(user == null || !password.equals(user.getPassword())) {
            throw new BadCredentialsException("用户名或密码不正确");
        }

        // 验证通过,返回一个认证对象
        return new UsernamePasswordAuthenticationToken(username, password, user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

接着,在Spring Security的配置文件中,我们需要添加验证码过滤器。这个过滤器会在用户登录时生成一个验证码,并将验证码放到Session中。用户输入的验证码会与Session中的验证码进行对比,从而验证验证码的正确性。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationProvider authenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll()
                .and()
            .addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class) // 添加验证码过滤器
            .authenticationProvider(authenticationProvider);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
    }
}

最后,我们需要编写验证码生成和验证码校验的代码。这里我们使用Spring Security项目中集成的验证码组件,只需要在配置文件中添加下面的代码即可。需要注意的是,这里的 verifyCodeParameterverifyCodeSessionAttribute 配置为了与默认值保持一致。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 省略其他配置代码

    @Bean
    public DefaultKaptcha captchaProducer() {
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", "no");
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.image.width", "100");
        properties.setProperty("kaptcha.image.height", "40");
        Config config = new Config(properties);
        DefaultKaptcha captchaProducer = new DefaultKaptcha();
        captchaProducer.setConfig(config);
        return captchaProducer;
    }

    @Autowired
    private DefaultKaptcha captchaProducer;

    @Bean
    public VerifyCodeFilter verifyCodeFilter() {
        return new VerifyCodeFilter();
    }

    private class VerifyCodeFilter extends OncePerRequestFilter {

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            if(!"/login".equals(request.getRequestURI())) {
                filterChain.doFilter(request, response);
                return;
            }

            // 生成验证码
            String text = captchaProducer.createText();
            BufferedImage image = captchaProducer.createImage(text);

            // 将验证码放到Session中
            request.getSession().setAttribute("verifyCode", text);
            response.setHeader("Cache-Control", "no-store");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setContentType("image/jpeg");

            // 输出验证码图片
            try(ServletOutputStream out = response.getOutputStream()) {
                ImageIO.write(image, "jpeg", out);
                out.flush();
            }
        }
    }

    @Bean
    public VerifyCodeService verifyCodeService() {
        return new VerifyCodeService();
    }

    private class VerifyCodeService implements InitializingBean {

        private AbstractHttpSessionApplicationInitializer httpSessionApplicationInitializer;

        public VerifyCodeService() {}

        public boolean validate(HttpServletRequest request, HttpServletResponse response) {
            String verifyCode = (String)request.getSession().getAttribute("verifyCode");
            String inputVerifyCode = request.getParameter("verifyCode");
            request.getSession().removeAttribute("verifyCode");
            return verifyCode != null && verifyCode.equalsIgnoreCase(inputVerifyCode);
        }

        @Override
        public void afterPropertiesSet() throws Exception {  
            // 获取httpSessionApplicationInitializer实例
            ApplicationContext context = ApplicationContextProvider.getApplicationContext(); 
            httpSessionApplicationInitializer = context.getBean(AbstractHttpSessionApplicationInitializer.class); }
        }
    }

示例二:自定义生成验证码

如果我们不想使用Spring Security项目中集成的验证码组件,我们也可以编写自定义的验证码生成和验证码校验的代码。

首先,我们需要定义一个 CaptchaGenerator 类,用来生成验证码。这个类的实现方式可以很简单,比如下面的代码:

public class CaptchaGenerator {

    public static final String DEFAULT_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    private static final Random rand = new Random();

    public static String generate(int length) {
        char[] chars = DEFAULT_CHARACTERS.toCharArray();
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < length; i++) {
            char c = chars[rand.nextInt(chars.length)];
            sb.append(c);
        }
        return sb.toString();
    }
}

接着,我们需要编写验证码生成和验证码校验的过滤器。这里的代码与示例一大致相同,只是验证码的生成和校验方式有所不同。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 省略其他配置代码

    @Autowired
    private CaptchaGenerator captchaGenerator;

    @Bean
    public VerifyCodeFilter verifyCodeFilter() {
        return new VerifyCodeFilter();
    }

    private class VerifyCodeFilter extends OncePerRequestFilter {

        public static final String SESSION_ATTRIBUTE_NAME = "captcha";

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            if(!"/login".equals(request.getRequestURI())) {
                filterChain.doFilter(request, response);
                return;
            }

            // 生成验证码
            String captcha = captchaGenerator.generate(4);
            byte[] captchaBytes = captcha.getBytes();
            request.getSession().setAttribute(SESSION_ATTRIBUTE_NAME, captchaBytes);
            response.setContentType("image/png");

            // 输出验证码图片
            try (ServletOutputStream out = response.getOutputStream()) {
                ImageIO.write(createImage(captchaBytes), "png", out);
                out.flush();
            }
        }

        private BufferedImage createImage(byte[] captcha) {
            BufferedImage image = new BufferedImage(100, 40, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            Font font = new Font("Georgia", Font.PLAIN, 18);

            // 绘制验证码文本
            for(int i = 0, x = 10; i < captcha.length; i++, x += 20) {
                g.setFont(font);
                g.setColor(new Color((int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255)));
                g.drawString(String.valueOf((char)captcha[i]), x, 20);
            }

            // 绘制干扰线
            for(int i = 0; i < 6; i++) {
                g.setColor(new Color((int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255)));
                g.drawLine(rand.nextInt(80), rand.nextInt(30) + 5, rand.nextInt(80), rand.nextInt(30) + 5);
            }

            return image;
        }
    }

    @Bean
    public VerifyCodeService verifyCodeService() {
        return new VerifyCodeService();
    }

    private class VerifyCodeService implements InitializingBean {

        private AbstractHttpSessionApplicationInitializer httpSessionApplicationInitializer;

        public VerifyCodeService() {}

        public boolean validate(HttpServletRequest request, HttpServletResponse response) {
            byte[] captcha = (byte[])request.getSession().getAttribute(VerifyCodeFilter.SESSION_ATTRIBUTE_NAME);
            String inputCaptcha = request.getParameter("captcha");

            return captcha != null && inputCaptcha != null && Arrays.equals(captcha, inputCaptcha.getBytes());
        }

        @Override
        public void afterPropertiesSet() throws Exception {  
            // 获取httpSessionApplicationInitializer实例
            ApplicationContext context = ApplicationContextProvider.getApplicationContext(); 
            httpSessionApplicationInitializer = context.getBean(AbstractHttpSessionApplicationInitializer.class); }   
        }
    }

最后需要注意的是,在配置文件中添加验证码过滤器时,需要将它加到用户名密码过滤器的前面。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationProvider authenticationProvider;

    @Autowired
    private VerifyCodeFilter verifyCodeFilter;

    // 省略其他配置代码

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll()
                .and()
            .addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码过滤器
            .authenticationProvider(authenticationProvider);
    }

    // 省略其他配置代码
}

以上就是关于“Spring Security 图片验证码功能的实例代码”的详细攻略,希望能够对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Security 图片验证码功能的实例代码 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • java局域网聊天小程序

    Java局域网聊天小程序攻略 简介 本攻略介绍如何使用Java编写一个局域网聊天小程序。可以在同一局域网内的多台计算机之间进行聊天。 步骤 1. 创建Java项目 创建一个新的Java项目,命名为“ChatApp”。 2. 添加GUI 在项目中创建一个新的GUI类,命名为“ChatWindow”。在界面中添加一个文本区域用于显示聊天记录,一个文本框用于输入聊…

    Java 2023年5月23日
    00
  • Java+swing实现抖音上的表白程序详解

    Java+Swing实现抖音上的表白程序详解 介绍 本文介绍如何使用Java语言和Swing库实现一个类似于抖音表白程序的小程序。本文会对如何使用Java和Swing实现图形用户界面进行详细讲解,并提供代码示例,帮助初学者了解Java和Swing图形用户界面开发的基础知识。 准备工作 在开始之前,确保你已经安装好了Java开发环境和Swing库。如果尚未安装…

    Java 2023年5月19日
    00
  • Java使用POI实现导出Excel的方法详解

    首先我们来讲解一下Java使用POI实现导出Excel的方法详解。 一、POI介绍 Apache POI是Apache软件基金会的开源项目,是用于Java编程语言处理Microsoft Office格式文件的开源库。POI提供API给用户对Excel、Word和PowerPoint等文件进行读和写的功能。POI提供了对Excel 97-2003及Excel …

    Java 2023年5月26日
    00
  • 使用SpringBoot自定义starter的完整步骤

    使用SpringBoot自定义starter可以方便我们在多个项目中重复使用一些公共的依赖或配置。下面是使用SpringBoot自定义starter的完整步骤: 1. 创建maven项目 <groupId>com.example</groupId> <artifactId>custom-starter</artifa…

    Java 2023年5月15日
    00
  • 数据库中经常用到的操作和管理数据库的语句总结

    下面是数据库中经常用到的操作和管理数据库的语句总结的攻略。 数据库的操作 创建数据库 创建数据库的语句如下: CREATE DATABASE db_name; 其中,db_name 为数据库的名称。在执行此命令时,数据库的名称必须是唯一的。 删除数据库 删除数据库的语句如下: DROP DATABASE db_name; 其中,db_name 为要删除的数据…

    Java 2023年6月15日
    00
  • 什么是Java字节码增强?

    Java字节码增强是指在不改变Java源代码的情况下,通过修改字节码文件的方式来增强Java程序的功能。这种方式比直接修改源代码更加灵活,可以在运行时动态改变程序的行为。常见的Java字节码增强工具有:AspectJ、CGLib、Javassist等。 下面,我们来介绍一下如何使用字节码增强工具。 1. 安装字节码增强工具 首先,需要安装相应的字节码增强工具…

    Java 2023年5月11日
    00
  • Java中static变量能继承吗

    Java中的static变量是类级别的变量,即使类还没有实例化,它也已经存在了。因此,它的值对于类中定义的所有方法和对象实例是相同的。那么,Java中的static变量能否被继承呢?答案是可以。 当一个子类继承一个父类时,它包含了父类的所有非私有成员变量和方法。这些变量和方法可以被直接访问,但是对于static变量,Java有一些额外的规则需要遵循。下面通过…

    Java 2023年5月26日
    00
  • Java中字符串常见的一些拼接方式总结

    Java 中字符串的拼接是一个较为常见的操作,也是 Java 语言重要组成部分。本篇攻略将为大家详细讲解 Java 中字符串常见的拼接方式以及相应的示例说明。 字符串拼接方式总结 在 Java 中,字符串的拼接方式有以下几种: 1. 使用 “+” 号拼接 String str1 = "Hello,"; String str2 = &quo…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部