• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Spring Security二—— 实现图形验证码

武飞扬头像
小小印z
帮助1

目录

一. 使用过滤器实现图形验证码

1. 自定义过滤器

2. 图形验证码过滤器

(1)引入kaptcha依赖

(2)配置一个 kaptcha 实例

(3)创建一个CaptchaController,用于获取图形验证码

(4)用于校验验证码的过滤器

(5)spring security 配置过滤器链

(6)修改 login.html 

(7)debug调试:

二、使用自定义认证实现图形验证码

1. 认识 AuthenticationProvider

2. 自定义AuthenticationProvider

 2. 实现图形验证码的AuthenticationProvider


一. 使用过滤器实现图形验证码

        验证码是为了防止恶意用户暴力重试而设置的。

1. 自定义过滤器

        在Spring Security中,实现验证码校验的方式有很多种,最简单的方式就是自定义一个专门处理验证码逻辑的过滤器,将其添加到Spring Security过滤器链的合适位置。当匹配到登录请求时,立刻对验证码进行校验,成功则放行,失败则提前结束整个验证请求。

(1)自定义一个过滤器

在该过滤器执行的时候在控制台输出一句日志

  1.  
    public class CustomFilter implements Filter {
  2.  
     
  3.  
    @Override
  4.  
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  5.  
    throws IOException, ServletException {
  6.  
    // 在这里添加自定义逻辑
  7.  
    chain.doFilter(request, response);
  8.  
    System.out.println("自定义的过滤器执行了!");
  9.  
    }
  10.  
     
  11.  
    // 可以在这里实现 Filter 接口的其他方法
  12.  
    }

(2)把该过滤器加在UsernamePasswordAuthenticationFilter之后

  1.  
    @Override
  2.  
    protected void configure(HttpSecurity http) throws Exception {
  3.  
    http
  4.  
    .addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
  5.  
    .authorizeRequests()
  6.  
    .antMatchers("/admin/api/**").hasRole("ADMIN")
  7.  
    .antMatchers("/user/api/**").hasRole("USER")
  8.  
    .anyRequest().authenticated()
  9.  
    .and()
  10.  
    .formLogin()
  11.  
    .loginPage("/login.html")
  12.  
    .permitAll()
  13.  
    .and()
  14.  
    .csrf().disable();
  15.  
    }
学新通

(3)测试

登录前后,可以看到控制台会多次打印该过滤器内容

这可能是因为在 CustomFilter 的 doFilter() 方法中打印了一条日志,而该方法在请求到达服务器时就会被调用,而不是只有在登录时才执行。因此,无论是在登录前还是登录后,只要有请求到达服务器,CustomFilter 的 doFilter() 方法都会被调用,并输出一条日志。

  1.  
    2023-04-14 23:57:00.362 INFO 35992 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
  2.  
    自定义的过滤器执行了!
  3.  
    自定义的过滤器执行了!
  4.  
    2023-04-14 23:57:07.250 INFO 35992 --- [nio-8080-exec-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
  5.  
    2023-04-14 23:57:07.410 INFO 35992 --- [nio-8080-exec-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
  6.  
    自定义的过滤器执行了!
  7.  
    自定义的过滤器执行了!

2. 图形验证码过滤器

        想要实现图形验证码校验功能,首先应当有一个获取图形验证码的API,绘制图形验证码的方法有很多,使用开源的验证码组件即可。例如kaptcha

(1)引入kaptcha依赖

  1.  
    <dependency>
  2.  
    <groupId>com.github.penggle</groupId>
  3.  
    <artifactId>kaptcha</artifactId>
  4.  
    <version>2.3.2</version>
  5.  
    </dependency>

(2)配置一个 kaptcha 实例

  1.  
    @Bean
  2.  
    public Producer captcha() {
  3.  
    Properties properties = new Properties();
  4.  
    properties.setProperty("kaptcha.image.width","150");
  5.  
    properties.setProperty("kaptcha.image.hright","150");
  6.  
    properties.setProperty("kaptcha.textproducer.char.string","0123456789");
  7.  
    properties.setProperty("kaptcha.textproducer.char.length","4");
  8.  
    Config config = new Config(properties);
  9.  
    //使用默认的图形验证码实现
  10.  
    DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
  11.  
    defaultKaptcha.setConfig(config);
  12.  
    return defaultKaptcha;
  13.  
    }

这个代码片段是一个Spring的Java配置方法。它使用Kaptcha库创建一个实现验证码服务的Java Bean。下面是代码块的各部分解释:

  1. @Bean - 这是Spring注解,用于告诉Spring容器,要将此方法返回的对象作为bean注册到容器中。

  2. Properties - 这是一个Java类,用于管理一组键值对。这里我们使用Properties来存储Kaptcha配置属性。

  3. Config - 这是Kaptcha库中的一个Java类,它需要从上述Properties对象中加载一组Kaptcha配置属性。

  4. DefaultKaptcha - 这是Kaptcha库中的一个Java类,它实现了默认的验证码生成算法。

  5. setConfig - 这是DefaultKaptcha类中的一个setter方法,用于将从Config对象中读取的属性设置到DefaultKaptcha实例中。

  6. return defaultKaptcha - 最后,这个方法返回一个DefaultKaptcha实例,它包含了我们所需的所有配置信息,可以生成图形验证码。

(3)创建一个CaptchaController,用于获取图形验证码

  1.  
    @Controller
  2.  
    public class CaptchaController {
  3.  
     
  4.  
    @Autowired
  5.  
    protected Producer captchaProducer;
  6.  
     
  7.  
    @GetMapping("/captcha.jpg")
  8.  
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
  9.  
    //设置内容类型
  10.  
    response.setContentType("image/jpeg");
  11.  
    //创建验证码文本
  12.  
    String capText = captchaProducer.createText();
  13.  
    //将验证码文本设置到 session
  14.  
    request.getSession().setAttribute("captcha",capText);
  15.  
    //创建验证码图片
  16.  
    BufferedImage bi = captchaProducer.createImage(capText);
  17.  
    //获取响应输出流
  18.  
    ServletOutputStream out = response.getOutputStream();
  19.  
    //将图形验证码数据写道响应输出流
  20.  
    ImageIO.write(bi,"jpg",out);
  21.  
    try {
  22.  
    out.flush();
  23.  
    } finally {
  24.  
    out.close();
  25.  
    }
  26.  
    }
  27.  
    }
学新通
  •  @GetMapping("/captcha.jpg") - 这是Spring MVC注解,用于将控制器方法映射到GET请求"/captcha.jpg"。
  • response.setContentType("image/jpeg"); - 这一行设置响应的内容类型为JPEG图像。
  • String capText = captchaProducer.createText(); - 这一行使用captchaProducer对象创建一个包含随机验证码文本的字符串capText。
  • request.getSession().setAttribute("captcha",capText); - 这一行将capText保存到当前HTTP会话的属性“captcha”中,以便稍后验证输入。
  • BufferedImage bi = captchaProducer.createImage(capText); - 这一行使用captchaProducer对象创建一个包含capText文本的验证码图片bi。
  • ServletOutputStream out = response.getOutputStream(); - 这一行获取HTTP响应输出流对象out,以便我们可以将验证码图像数据写入响应流。
  • ImageIO.write(bi,"jpg",out); - 这一行将bi对象的图像数据写入out输出流中。
  • out.flush(); - 这一行刷新输出流。
  • out.close(); - 最后,使用out.close()关闭输出流。

        当用户访问 /captcha.jpg时,即可得到一张携带验证码的图片,验证码文本则被存放到session中,用于后续校验。现在我们可以启动项目先看一下:

学新通

(4)用于校验验证码的过滤器

        虽然Spring Security 的过滤器链对过滤器没有特殊要求,只要继承了Filter即可,但是在Spring体系中,推荐使用OncePerRequestFilter来实现。它可以确保一次请求只会通过一次该过滤器(Filter实际上并不能保证这一点)

  1.  
    public class CaptchaFilter extends OncePerRequestFilter {
  2.  
     
  3.  
    private static final String CAPTCHA_SESSION_KEY = "captcha";
  4.  
    private static final String CAPTCHA_PARAM_NAME = "captcha";
  5.  
     
  6.  
    @Override
  7.  
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  8.  
    if (request.getMethod().equalsIgnoreCase("POST")) { //只对POST请求进行验证码验证
  9.  
    HttpSession session = request.getSession(false);
  10.  
    if (session != null) {
  11.  
    //拿到session中存放的 captcha 属性
  12.  
    String captcha = (String) session.getAttribute(CAPTCHA_SESSION_KEY);
  13.  
    if (captcha == null) {
  14.  
    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "验证码已过期,请重新获取。");
  15.  
    return;
  16.  
    }
  17.  
    //获取输入的验证码信息
  18.  
    String inputCaptcha = request.getParameter(CAPTCHA_PARAM_NAME);
  19.  
    if (inputCaptcha == null || !captcha.equals(inputCaptcha.trim())) {
  20.  
    response.sendError(HttpServletResponse.SC_BAD_REQUEST, "验证码错误,请重新输入。");
  21.  
    return;
  22.  
    }
  23.  
    } else {
  24.  
    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "无法验证验证码,因为HTTP会话不存在。");
  25.  
    return;
  26.  
    }
  27.  
    }
  28.  
    filterChain.doFilter(request, response);
  29.  
    }
  30.  
    }
学新通

 这是一个验证码过滤器类,用于在用户登录等需要输入验证码的场景下,对用户输入的验证码进行验证。具体来说:

  • 在doFilterInternal()方法中,首先通过request对象获取用户请求的方法,如果是POST请求,则对验证码进行验证。
  • 然后通过request.getSession(false)获取当前的session对象,如果session对象不为null,则继续进行验证码验证;否则返回错误信息,提示无法验证验证码,因为HTTP会话不存在。
  • 验证码的具体验证流程是:先通过CAPTCHA_SESSION_KEY获取session中存放的验证码信息,如果获取到的验证码为null,则返回错误信息,提示验证码已过期,请重新获取;否则获取用户输入的验证码信息,如果用户输入的验证码为null或者与session中的验证码信息不一致,则返回错误信息,提示验证码错误,请重新输入。
  • 最后,如果验证码验证通过,则通过filterChain.doFilter()方法继续执行后续的过滤器或请求处理。 总之,这个验证码过滤器类的作用是保证用户输入的验证码正确,从而增强了系统的安全性和防范机制。

(5)spring security 配置过滤器链

  1.  
    @Override
  2.  
    protected void configure(HttpSecurity http) throws Exception {
  3.  
    http
  4.  
    .addFilterBefore(new CaptchaFilter(),UsernamePasswordAuthenticationFilter.class)
  5.  
    .authorizeRequests()
  6.  
    .antMatchers("/admin/api/**").hasRole("ADMIN")
  7.  
    .antMatchers("/user/api/**").hasRole("USER")
  8.  
    //开放验证码的访问权限
  9.  
    .antMatchers("/captcha.jpg").permitAll()
  10.  
    .anyRequest().authenticated()
  11.  
    .and()
  12.  
    .formLogin()
  13.  
    .loginPage("/login.html")
  14.  
    .permitAll()
  15.  
    .and()
  16.  
    .csrf().disable();
  17.  
    }
学新通

(6)修改 login.html 

  1.  
    <!DOCTYPE html>
  2.  
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3.  
    <head>
  4.  
    <meta charset="UTF-8">
  5.  
    <title>Login Page</title>
  6.  
    <style>
  7.  
    /* 样式可以自行修改 */
  8.  
    body {
  9.  
    background-color: cadetblue;
  10.  
    }
  11.  
     
  12.  
    .login-form {
  13.  
    width: 350px;
  14.  
    margin: 150px auto;
  15.  
    background-color: #fff;
  16.  
    padding: 20px;
  17.  
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
  18.  
    }
  19.  
     
  20.  
    h1 {
  21.  
    font-size: 24px;
  22.  
    text-align: center;
  23.  
    margin-bottom: 30px;
  24.  
    }
  25.  
     
  26.  
    input[type="text"], input[type="password"], input[type="number"] {
  27.  
    width: 100%;
  28.  
    padding: 10px;
  29.  
    margin-bottom: 20px;
  30.  
    border: 2px solid #ccc;
  31.  
    border-radius: 4px;
  32.  
    box-sizing: border-box;
  33.  
    }
  34.  
     
  35.  
    button {
  36.  
    background-color: darksalmon;
  37.  
    color: white;
  38.  
    padding: 12px 20px;
  39.  
    border: none;
  40.  
    border-radius: 4px;
  41.  
    cursor: pointer;
  42.  
    width: 100%;
  43.  
    }
  44.  
     
  45.  
    button:hover {
  46.  
    background-color: #45a049;
  47.  
    }
  48.  
     
  49.  
    .captcha-container {
  50.  
    text-align: center;
  51.  
    margin-bottom: 20px;
  52.  
    }
  53.  
     
  54.  
    .captcha-img {
  55.  
    width: 150px;
  56.  
    height: 50px;
  57.  
    }
  58.  
    </style>
  59.  
    </head>
  60.  
    <body>
  61.  
    <div class="login-form">
  62.  
    <h1>Login Page</h1>
  63.  
    <form th:action="@{/login}" method="post">
  64.  
    <label for="username">Username</label>
  65.  
    <input type="text" id="username" name="username" placeholder="Enter username" required>
  66.  
     
  67.  
    <label for="password">Password</label>
  68.  
    <input type="password" id="password" name="password" placeholder="Enter password" required>
  69.  
     
  70.  
    <div class="captcha-container">
  71.  
    <img class="captcha-img" th:src="@{/captcha.jpg}" onclick="this.src='https://blog.csdn.net/weixin_49561506/article/details/captcha.jpg?' Math.random()">
  72.  
    </div>
  73.  
     
  74.  
    <label for="captcha">Verification Code</label>
  75.  
    <input type="number" id="captcha" name="captcha" placeholder="Enter verification code" required>
  76.  
     
  77.  
    <button type="submit">Login</button>
  78.  
    </form>
  79.  
    </div>
  80.  
    </body>
  81.  
    </html>
学新通

(7)debug调试:

学新通 

学新通

二、使用自定义认证实现图形验证码

        上面使用过滤器方式实现类带图形验证码的验证功能,属于Servlet层面,Spring Security还提供了一种更优雅的实现图形验证码的方式,即自定义认证。

1. 认识 AuthenticationProvider

        我们所面对的系统中的用户,在Spring Security中被称为主体(principal),主体包含了所有能够经过验证而获得系统访问权限的用户、设备或其他系统。Spring Security 通过一层将其定义为一个Authentication。

  1.  
    public interface Authentication extends Principal, Serializable {
  2.  
     
  3.  
    //获取主体权限列表
  4.  
    Collection<? extends GrantedAuthority> getAuthorities();
  5.  
     
  6.  
    //获取主体凭据,通常为用户密码
  7.  
    Object getCredentials();
  8.  
     
  9.  
    //获取主体详细信息
  10.  
    Object getDetails();
  11.  
     
  12.  
    //获取主体,通常为一个用户名
  13.  
    Object getPrincipal();
  14.  
     
  15.  
    //主体是否验证成功
  16.  
    boolean isAuthenticated();
  17.  
     
  18.  
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
  19.  
    }
学新通

学新通        UsernamePasswordAuthenticationToken也是Authentication的一个实现类。         

        大部分情况下身份验证都是基于用户名和密码进行的,所以 Spring Security提供了一个UsernamePasswordAuthenticationToken 用于代指这一类证明,例如用SSH KEY也可以登录,但它不属于用户名和密码登录这个范畴。

        在前面使用的表单登录中,每一个登录用户被包装为一个UsernamePasswordAuthenticationToken,从而在Spring Security的各个AuthenticationProvider中流动。

        AuthenticationProvider 被Spring Security定义为一个验证过程,一次完整的认证可以包含多个AuthenticationProvider,一般由ProviderManager管理。

  1.  
    public interface AuthenticationProvider {
  2.  
    //验证过程,成功的话返回一个验证完成的Authentication
  3.  
    Authentication authenticate(Authentication authentication) throws AuthenticationException;
  4.  
     
  5.  
    boolean supports(Class<?> authentication);
  6.  
    }

2. 自定义AuthenticationProvider

        Spring Security提供了多种常见的认证技术,包括但不限于以下几种:

  • HTTP层面的认证技术,包括HTTP基本认证和HTTP摘要认证两种
  • 基于LDAP的认证技术
  • 聚焦于证明用户身份的OpenID认证技术
  • 聚焦于授权的OAuth认证技术
  • 系统内维护的用户名和密码认证技术(最广泛)

         系统内维护的用户名和密码认证技术使用最为广泛,通常会涉及数据库访问。为了更好的按需定制,Spring Security并没有直接糅合整个认证过程,而是提供了一个抽象的AuthenticationProvider,那就是 AbstractUserDetailsAuthenticationProvider

AbstractUserDetailsAuthenticationProvider是Spring Security提供的抽象类,用于支持验证用户身份,并从用户详细信息中构建身份验证对象

该类实现了AuthenticationProvider接口,可以作为身份验证的提供者。同时,它还实现了UserDetailsService接口,用于从数据源中获取用户详细信息。

在具体的实现中,AbstractUserDetailsAuthenticationProvider的主要作用是将Authentication对象中的用户名和密码提取出来,然后通过UserDetailsService获取用户详细信息,并将其与用户名和密码进行比较,以确定用户的身份是否正确。

如果身份验证成功,AbstractUserDetailsAuthenticationProvider会将用户详细信息封装到一个新的身份验证对象中,并将其返回。如果身份验证失败,则会抛出一个AuthenticationException异常。

此外,AbstractUserDetailsAuthenticationProvider还可以处理密码加密和解密的逻辑,以及支持定制化的身份验证逻辑。

实现:自定义AuthenticationProvider示例

  1.  
    public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
  2.  
     
  3.  
    @Autowired
  4.  
    private CustomUserDetailsService userDetailsService;
  5.  
     
  6.  
    @Override
  7.  
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
  8.  
    // 在此处实现密码验证逻辑
  9.  
    if (!authentication.getCredentials().equals(userDetails.getPassword())) {
  10.  
    throw new BadCredentialsException("Invalid username or password");
  11.  
    }
  12.  
    }
  13.  
     
  14.  
    @Override
  15.  
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
  16.  
    // 在此处从自定义的用户详情服务中获取用户信息
  17.  
    UserDetails userDetails = userDetailsService.loadUserByUsername(username);
  18.  
    if (userDetails == null) {
  19.  
    throw new UsernameNotFoundException("User not found with username: " username);
  20.  
    }
  21.  
    return userDetails;
  22.  
    }
  23.  
     
  24.  
    }
学新通

 2. 实现图形验证码的AuthenticationProvider

        现在重新回到自定义认证实现图形验证码登录的这个案例中,由于只是在常规的认证之上增加了图形验证码的校验,其他流程并没有变化,所以只需要继承DaoAuthenticationProvider并稍微修改即可。

  1.  
    public class MyAuthenticationProvider extends DaoAuthenticationProvider {
  2.  
     
  3.  
    public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
  4.  
    this.setUserDetailsService(userDetailsService);
  5.  
    this.setPasswordEncoder(passwordEncoder);
  6.  
    }
  7.  
     
  8.  
    @Override
  9.  
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
  10.  
    //实现图形验证码的校验逻辑
  11.  
     
  12.  
    //调用父类方法完成密码验证
  13.  
    super.additionalAuthenticationChecks(userDetails, authentication);
  14.  
    }
  15.  
    }
学新通

        用户提交的验证码和session存储的验证码都需要从用户的请求中获取,但是传入的对象只有userDetails 和 authentication。是否还需要一个HttpServletRequest对象呢?并非如此,Authentication实际上还可以携带账号信息之外的数据。

  1.  
    public interface Authentication extends Principal, Serializable {
  2.  
     
  3.  
    //允许携带任意对象 Object类型
  4.  
    Object getDetails();
  5.  
     
  6.  
    }

        前面提到过,一次完整的认证可以包含多个AuthenticationProvider,这些AuthenticationProvider都由ProviderManager管理,ProviderManager是由UsernamePasswordAuthenticationFilter调用的。

AuthenticationProviderProviderManager介绍:

        AuthenticationProvider和ProviderManager是Spring Security框架中用来实现认证的两个关键组件。

        AuthenticationProvider是一个接口,它定义了认证的核心逻辑,即对用户提供的认证信息进行验证。其中包含一个方法authenticate(),用于执行认证操作。在Spring Security中,一般情况下需要自定义实现AuthenticationProvider接口,并将其添加到ProviderManager中,以完成对认证请求的处理。

        ProviderManager是一个认证管理器,它负责协调多个AuthenticationProvider实现,实现对认证请求的分派、调度和结果处理。当认证请求到达ProviderManager时,它会遍历其内部持有的AuthenticationProvider实现列表,逐一尝试调用各个AuthenticationProvider的authenticate()方法,直到其中一个AuthenticationProvider能够成功认证该请求,或者所有AuthenticationProvider都无法认证成功。

        总体来说,AuthenticationProvider和ProviderManager是Spring Security框架中非常重要的两个组件,它们共同协作,实现了对用户身份的验证和认证。通过实现AuthenticationProvider接口,可以对认证逻辑进行个性化定制,而通过使用ProviderManager,可以方便地协调多个AuthenticationProvider实现,以实现更加复杂的认证方案。

AuthenticationProviderProviderManagerUsernamePasswordAuthenticationFilter的关系:

        AuthenticationProvider和ProviderManager都是与身份验证相关的组件,而UsernamePasswordAuthenticationFilter是处理身份验证请求的过滤器。

        当用户提交身份验证请求时,UsernamePasswordAuthenticationFilter拦截请求并从请求中获取用户名和密码等身份验证信息,然后创建一个UsernamePasswordAuthenticationToken对象来表示这些信息,并将其传递给ProviderManager的authenticate()方法进行身份验证。

         ProviderManager通过遍历已配置的AuthenticationProvider列表来查找可以处理该UsernamePasswordAuthenticationToken的AuthenticationProvider。如果找到了匹配的AuthenticationProvider,则该提供程序会对身份验证信息进行验证,并返回一个经过身份验证的Authentication对象,ProviderManager将该Authentication对象传递给UsernamePasswordAuthenticationFilter,以表明身份验证已成功。

        如果ProviderManager无法找到匹配的AuthenticationProvider,或者已找到但是无法验证身份验证信息,则ProviderManager将抛出一个AuthenticationException异常,UsernamePasswordAuthenticationFilter将捕获该异常并处理身份验证失败的情况。

如图:

学新通 学新通

         Authentication中有了HttpServletRequest之后,一切变得非常顺畅,基于图形验证码的场景,我们可以继承WebAuthenticationDetails,并扩展需要的信息。

  1.  
    public class MyWebAuthenticationDetails extends WebAuthenticationDetails {
  2.  
     
  3.  
    private boolean imageCodeIsRight;
  4.  
     
  5.  
    public boolean getImageCodeIsRight() {
  6.  
    return this.imageCodeIsRight;
  7.  
    }
  8.  
    //补充用户提交的验证码和session保存的验证码
  9.  
    public MyWebAuthenticationDetails(HttpServletRequest request) {
  10.  
    super(request);
  11.  
    String imageCode = request.getParameter("captcha");
  12.  
    HttpSession session = request.getSession();
  13.  
    String savedImageCode = (String)session.getAttribute("captcha");
  14.  
    if (!StringUtils.isEmpty(savedImageCode)) {
  15.  
    session.removeAttribute("captcha");
  16.  
    //当验证码正确时设置状态
  17.  
    if (!StringUtils.isEmpty(imageCode) && imageCode.equals(savedImageCode)) {
  18.  
    this.imageCodeIsRight = true;
  19.  
    }
  20.  
    }
  21.  
    }
  22.  
    }
学新通

将它提供给一个自定义的AuthenticationDetailsSource

  1.  
    public class MyWebAuthenticationDetailsSource implements
  2.  
    AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
  3.  
    @Override
  4.  
    public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
  5.  
    return new MyWebAuthenticationDetails(context);
  6.  
    }
  7.  
    }

 接下来实现自定义的AuthenticationProvider

  1.  
    public class MyAuthenticationProvider extends DaoAuthenticationProvider {
  2.  
     
  3.  
    public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
  4.  
    this.setUserDetailsService(userDetailsService);
  5.  
    this.setPasswordEncoder(passwordEncoder);
  6.  
    }
  7.  
     
  8.  
    @SneakyThrows
  9.  
    @Override
  10.  
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
  11.  
    //实现图形验证码的校验逻辑
  12.  
    //获取详细信息
  13.  
    MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) authentication.getDetails();
  14.  
    //一旦发现验证码不正确,就立刻抛出异常信息
  15.  
    if (!details.getImageCodeIsRight()) {
  16.  
    throw new VerificationCodeException();
  17.  
    }
  18.  
    //调用父类方法完成密码验证
  19.  
    super.additionalAuthenticationChecks(userDetails, authentication);
  20.  
    }
  21.  
    }
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhibgaba
系列文章
更多 icon
同类精品
更多 icon
继续加载