Spring Security基于过滤器实现图形验证码功能

目录
  • 前言
  • 一. 验证码简介
  • 二. 基于过滤器实现图形验证码
    • 1. 实现概述
    • 2. 创建新模块
    • 3. 添加依赖包
    • 4. 创建Producer对象
    • 5. 创建生成验证码的接口
    • 6. 自定义异常
    • 7. 创建拦截验证码的过滤器
    • 8. 编写SecurityConfig
    • 9. 编写测试页面
    • 10. 代码结构
    • 11. 启动项目测试

前言

在前两个章节中,一一哥 带大家学习了Spring Security内部关于认证授权的核心API,以及认证授权的执行流程和底层原理。掌握了这些之后,对于Spring Security,我们不仅做到了 "知其然",而且也做到了 "知其所以然"

在现在的求职环境下,只知道某个技能点的用法是远远不够的,面试官会要求我们研究某个技术的底层实现原理,所以虽然前面的两章内容掌握起来很有难度,但是还是希望各位小伙伴结合源码认真研读,这样你才能在编程之路上走的更远更高!

总是研究底层,对于我们初学者来说,既有难度,也会影响咱们的学习积极性,所以从本篇文章开始,咱们继续学习Spring Security的其他用法,比如我们学习一下如何在Spring Security中添加执行自定义的过滤器,进而实现验证码校验功能。

一. 验证码简介

在进行验证码编码实现之前,壹哥 先给各位介绍一下验证码的概念和作用。

验证码(CAPTCHA):全称是Completely Automated Public Turing test to tell Computers and Humans Apart,翻译过来就是“全自动区分计算机和人类的图灵测试”。通俗的讲,验证码就是为了防止恶意用户采用暴力重试的攻击手段而设置的一种防护措施。我们在进行用户注册、登录、或者论坛发帖时都可以利用验证码,对某些恶意用户利用计算机发起无限重试进行必要的拦截限制。

接下来在Spring Security的环境中,我们可以用如下两种方案实现图形验证码:

  • 基于自定义的过滤器来实现图形验证码;
  • 基于自定义的认证器来实现图形验证码。

在本篇文章中,壹哥 先利用第一种方案,也就是基于自定义的过滤器的方式,来教会大家如何实现图形验证码,请继续往下看哦。

二. 基于过滤器实现图形验证码

1. 实现概述

在Spring Security中,实现验证码校验的方式有很多种,其中基于过滤器来实现图形验证码是最简单的方式。小伙伴可能会问,过滤器如何实现验证码校验功能呢?基于什么原理?这个其实很简单,流程原理就是我们先自定义一个过滤器Filter,处理验证码的验证逻辑,然后把该过滤器添加到Spring Security过滤器链的某个合适位置。当匹配到登录请求时,过滤器会对验证码进行校验,如果成功则放行,如果失败则结束当前验证请求。

明白了实现流程和原理后,接下来我们就在之前项目的基础之上,进行验证码功能的代码实现。

2. 创建新模块

我们可以创建一个新的项目model,创建过程与之前一样,这里就略过了。

3. 添加依赖包

本案例中,我们采用github上的开源验证码解决方案kaptcha,所以需要在原有项目的基础上添加kaptcha的依赖包。

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

4. 创建Producer对象

在准备好依赖包之后,接下来我们创建一个CaptchaConfig配置类,在该类中创建一个Producer对象,对验证码对象进行必要的配置,比如设置验证码背景框的宽度和高度,验证码的字符集,验证码的个数等。

@Configuration
public class CaptchaConfig {

    @Bean
    public Producer captcha() {
        // 配置图形验证码的基本参数
        Properties properties = new Properties();
        // 图片宽度
        properties.setProperty("kaptcha.image.wth", "150");
        // 图片长度
        properties.setProperty("kaptcha.image.height", "50");
        // 字符集
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
        // 字符长度
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        Config config = new Config(properties);
        // 使用默认的图形验证码实现,当然也可以自定义实现
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

}

5. 创建生成验证码的接口

在上面创建了Producer对象后,接着我们再创建一个生成验证码的接口,该接口中负责生成验证码图片,并且记得要把生成的验证码存放到session中,以便后面发起登录请求时进行比对验证码。

@Controller
public class CaptchaController {

    @Autowired
    private Producer captchaProducer;

    @GetMapping("/captcha.jpg")
    public vo getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 设置内容类型
        response.setContentType("image/jpeg");
        // 创建验证码文本
        String capText = captchaProducer.createText();

        // 将验证码文本设置到session
        request.getSession().setAttribute("captcha", capText);

        // 创建验证码图片
        BufferedImage bi = captchaProducer.createImage(capText);
        // 获取响应输出流
        ServletOutputStream out = response.getOutputStream();
        // 将图片验证码数据写到响应输出流
        ImageIO.write(bi, "jpg", out);

        // 推送并关闭响应输出流
        try {
            out.flush();
        } finally {
            out.close();
        }
    }

}

6. 自定义异常

我们可以自定义一个运行时异常,用于处理验证码校验失败时抛出异常提示信息,当然这个并不是必须的。

public class VerificationCodeException extends AuthenticationException {

    public VerificationCodeException () {
        super("图形验证码校验失败");
    }

}

7. 创建拦截验证码的过滤器

创建验证码并保存到session之后,另一个很重要的工作就是比对用户从前端传递过来的验证码是否相同,这个比对工作我们就在自定义的过滤器中进行实现。所以接着我们就创建一个过滤器,负责对用户发来的验证码进行拦截校验,看看request请求中传递过来的验证码,与我们生成并保存在session中的验证码是否一致。

/**
 * 基于过滤器实现图形验证码的验证功能,这属于Servlet层面,简单易理解.
 */
public class VerificationCodeFilter extends OncePerRequestFilter {

    private AuthenticationFailureHandler authenticationFailureHandler = new SecurityAuthenticationFailureHandler();

    @Overre
    protected vo doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        // 非登录请求不校验验证码,直接放行
        if (!"/login".equals(httpServletRequest.getRequestURI())) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        } else {
            try {
                //校验验证码
                verificationCode(httpServletRequest);

                //验证码校验通过后,对请求进行放行
                filterChain.doFilter(httpServletRequest, httpServletResponse);
            } catch (VerificationCodeException e) {
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
            }
        }
    }

    public vo verificationCode (HttpServletRequest httpServletRequest) throws VerificationCodeException {
        HttpSession session = httpServletRequest.getSession();
        String savedCode = (String) session.getAttribute("captcha");
        if (!StringUtils.isEmpty(savedCode)) {
            // 随手清除验证码,不管是失败还是成功,所以客户端应在登录失败时刷新验证码
            session.removeAttribute("captcha");
        }

        String requestCode = httpServletRequest.getParameter("captcha");
        // 校验不通过抛出异常
        if (StringUtils.isEmpty(requestCode) || StringUtils.isEmpty(savedCode) || !requestCode.equals(savedCode)) {
            throw new VerificationCodeException();
        }
    }

}

8. 编写SecurityConfig

接下来我们需要编写一个SecurityConfig配置类,在该配置类中,把咱们前面编写的验证码过滤器添加在默认的UsernamePasswordAuthenticationFilter过滤器之前来执行,可以利用http.addFilterBefore()方法来实现。

对于UsernamePasswordAuthenticationFilter过滤器,你还能想起来吗?如果想不起来,请看我前一篇文章,里面有详细讲解哦!

/**
 * @Author: 一一哥
 * 增加图形验证码功能.
 */
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Overre
    protected vo configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/api/**")
                .hasRole("ADMIN")
                .antMatchers("/user/api/**")
                .hasRole("USER")
                .antMatchers("/app/api/**", "/captcha.jpg")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .failureHandler(new SecurityAuthenticationFailureHandler())
                .successHandler(new SecurityAuthenticationSuccessHandler())
                .loginPage("/myLogin.html")
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .csrf()
                .disable();

        //将过滤器添加在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

}

在这里,我们把自定义的验证码校验过滤器VerificationCodeFilter,添加到了Spring Security自带的UsernamePasswordAuthenticationFilter过滤器前面。

注:

关于我们测试的web接口,数据库配置、认证成功、失败时的Handler处理器等,请参考前面《基于Spring Security前后端分离的权限控制系统问题》案例,此处略过。

9. 编写测试页面

既然要实现验证码功能,为了方便测试,我们需要编写一个自定义的登录页面,并在这里添加对验证码接口的引用,这里列出html页面的核心代码。

<body>
    <div class="login">
        <h2>Access Form</h2>
        <div class="login-top">
            <h1>登录验证</h1>
            <form action="/login" method="post">
                 <input type="text" name="username" placeholder="username" />
                 <input type="password" name="password" placeholder="password" />
                 <div style="display: flex;">
                     <!-- 新增图形验证码的输入框 -->
                     <input type="text" name="captcha" placeholder="captcha" />
                     <!-- 图片指向图形验证码API -->
                     <img src="/captcha.jpg" alt="captcha" height="50px" wth="150px" style="margin-left: 20px;">
                 </div>
                 <div class="forgot">
                     <a href="#">忘记密码</a>
                     <input type="submit" value="登录" >
                 </div>
            </form>
       </div>
       <div class="login-bottom">
           <h3>新用户&nbsp;<a href="#">注&nbsp;册</a></h3>
       </div>
   </div>
</body>

10. 代码结构

整个项目的代码结构如下,请参考下图进行实现。

11. 启动项目测试

接下来我们启动项目,在访问受限接口时,会重定向到myLogin.html登录页面,可以看到我们的验证码效果已经显示出来了。

接下来我们输入正确的用户名、密码、验证码后,就可以成功的登录进去访问web接口了。

至此,基于自定义过滤器实现的验证码功能,壹哥 就带各位实现完毕,你学会了吗?如有疑问,可以在评论区留言。

到此这篇关于Spring Security基于过滤器实现图形验证码功能的文章就介绍到这了,更多相关Spring Security图形验证码内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Security基于自定义的认证提供器实现图形验证码流程解析

    目录 前言 一. 认证提供器简介 1. 认证提供器AuthenticationProver 2. WebAuthenticationDetails类介绍 二. 实现图形验证码 1. 添加依赖包 2. 创建Producer对象 3. 创建生成验证码的接口 4. 自定义异常 5. 自定义WebAuthenticationDetails 6. 自定义AuthenticationDetailsSource 7. 自定义DaoAuthenticationProver 8. 添加SecurityConfig

  • SpringBoot结合SpringSecurity实现图形验证码功能

    本文介绍了SpringBoot结合SpringSecurity实现图形验证码功能,分享给大家,具体如下: 生成图形验证码 根据随机数生成图片 将随机数存到Session中 将生成的图片写到接口的响应中 生成图形验证码的过程比较简单,和SpringSecurity也没有什么关系.所以就直接贴出代码了 根据随机数生成图片 /** * 生成图形验证码 * @param request * @return */ private ImageCode generate(ServletWebRequest r

  • SpringSecurity实现图形验证码功能实例代码

    Spring Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作. 本文重点给大家介绍Spri

  • Spring Security基于过滤器实现图形验证码功能

    目录 前言 一. 验证码简介 二. 基于过滤器实现图形验证码 1. 实现概述 2. 创建新模块 3. 添加依赖包 4. 创建Producer对象 5. 创建生成验证码的接口 6. 自定义异常 7. 创建拦截验证码的过滤器 8. 编写SecurityConfig 9. 编写测试页面 10. 代码结构 11. 启动项目测试 前言 在前两个章节中,一一哥 带大家学习了Spring Security内部关于认证授权的核心API,以及认证授权的执行流程和底层原理.掌握了这些之后,对于Spring Secu

  • Spring Security基于散列加密方案实现自动登录功能

    目录 前言 一. 自动登录简介 1. 为什么要自动登录 2. 自动登录的实现方案 二. 基于散列加密方案实现自动登录 1. 配置加密令牌的key 2. 配置SecurityConfig类 3. 添加测试接口 4. 启动项目测试 三. 散列加密方案实现原理 1. cookie的加密原理分析 2. cookie的解码原理分析 3. 自动登录的源码分析 3.1 令牌生成的源码分析 3.2 令牌解析的源码分析 前言 在前面的2个章节中,一一哥 带大家实现了在Spring Security中添加图形验证码

  • 一个注解搞定Spring Security基于Oauth2的SSO单点登录功能

    目录 一.说明 二.原理说明 2.1. 同域单点登录 2.2. 跨域单点登录 2.3. 基于Oauth2的跨域单点登录流程 三.Spring Security实现 四.demo下载地址 一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼.本文主要介绍 同域 和 跨域 两种不同场景单点登录的实现原理,并使用 Spring Security 来实现一个最简单的跨域 SSO客户端 . 二.原理说明 单点登录主流都是基于共享 cookie

  • PHP实现生成带背景的图形验证码功能

    本文实例讲述了PHP实现生成带背景的图形验证码功能.分享给大家供大家参考,具体如下: 以前我们利用php生成的都是无背景或同一色彩背景的验证码了,但这种验证容易给机器识别了,这里就来介绍一些生成带背景的图形验证码实例. 1.产生一张png的图片, 2.为图片设置背景色, 3.设置字体颜色和样式, 4.产生4位数的随机的验证码, 5.把产生的每个字符调整旋转角度和位置画到png图片上, 6.加入噪点和干扰线防止注册机器分析原图片来恶意注册, 7.输出图片, 8.释放图片所占内存 authcode.

  • Spring security 自定义过滤器实现Json参数传递并兼容表单参数(实例代码)

    依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId&g

  • Spring Security 基于URL的权限判断源码解析

    目录 1. FilterSecurityInterceptor 源码阅读 2. 自定义基于url的授权 1. FilterSecurityInterceptor 源码阅读 org.springframework.security.web.access.intercept.FilterSecurityInterceptor 通过过滤器实现对HTTP资源进行安全处理. 该安全拦截器所需的 SecurityMetadataSource 类型为 FilterInvocationSecurityMetad

  • Spring Security使用单点登录的权限功能

    目录 背景 Spring Security 实现 已经有了单点登录页面,Spring Security怎么登录,不登录可以拿到权限吗 Authentication 继续分析 结论 如何手动登录Spring Security 结论 总结 附 参考 背景 在配置中心增加权限功能 目前配置中心已经包含了单点登录功能,可以通过统一页面进行登录,登录完会将用户写入用户表 RBAC的用户.角色.权限表CRUD.授权等都已经完成 希望不用用户再次登录,就可以使用SpringSecurity的权限控制 Spri

随机推荐