软件编程
位置:首页>> 软件编程>> java编程>> Spring Security添加验证码的两种方式小结

Spring Security添加验证码的两种方式小结

作者:周杰倫本人  发布时间:2021-08-05 17:24:25 

标签:spring,security,验证码
目录
  • 一、自定义认证逻辑

  • 二、自定义过滤器

  • 总结

一、自定义认证逻辑

生成验证码工具


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

添加Kaptcha配置


@Configuration
public class KaptchaConfig {
   @Bean
   Producer kaptcha() {
       Properties properties = new Properties();
       properties.setProperty("kaptcha.image.width", "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;
   }
}

生成验证码文本,放入HttpSession中

根据验证码文本生成图片 通过IO流写出到前端。


@RestController
public class LoginController {
   @Autowired
   Producer producer;
   @GetMapping("/vc.jpg")
   public void getVerifyCode(HttpServletResponse resp, HttpSession session) throws IOException {
       resp.setContentType("image/jpeg");
       String text = producer.createText();
       session.setAttribute("kaptcha", text);
       BufferedImage image = producer.createImage(text);
       try(ServletOutputStream out = resp.getOutputStream()) {
           ImageIO.write(image, "jpg", out);
       }
   }
   @RequestMapping("/index")
   public String index() {
       return "login success";
   }
   @RequestMapping("/hello")
   public String hello() {
       return "hello spring security";
   }
}

form表单


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
   <meta charset="UTF-8">
   <title>登录</title>
   <link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="external nofollow"  rel="stylesheet" id="bootstrap-css">
   <script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
   <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<style>
   #login .container #login-row #login-column #login-box {
       border: 1px solid #9C9C9C;
       background-color: #EAEAEA;
   }
</style>
<body>
<div id="login">
   <div class="container">
       <div id="login-row" class="row justify-content-center align-items-center">
           <div id="login-column" class="col-md-6">
               <div id="login-box" class="col-md-12">
                   <form id="login-form" class="form" action="/doLogin" method="post">
                       <h3 class="text-center text-info">登录</h3>
                       <div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>
                       <div class="form-group">
                           <label for="username" class="text-info">用户名:</label><br>
                           <input type="text" name="uname" id="username" class="form-control">
                       </div>
                       <div class="form-group">
                           <label for="password" class="text-info">密码:</label><br>
                           <input type="text" name="passwd" id="password" class="form-control">
                       </div>
                       <div class="form-group">
                           <label for="kaptcha" class="text-info">验证码:</label><br>
                           <input type="text" name="kaptcha" id="kaptcha" class="form-control">
                           <img src="/vc.jpg" alt="">
                       </div>
                       <div class="form-group">
                           <input type="submit" name="submit" class="btn btn-info btn-md" value="登录">
                       </div>
                   </form>
               </div>
           </div>
       </div>
   </div>
</div>
</body>

验证码图片地址为我们在Controller中定义的验证码接口地址。

身份认证是AuthenticationProvider的authenticate方法完成,因此验证码可以在此之前完成:


public class KaptchaAuthenticationProvider extends DaoAuthenticationProvider {

@Override
   public Authentication authenticate(Authentication authentication) throws AuthenticationException {
       HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
       String kaptcha = req.getParameter("kaptcha");
       String sessionKaptcha = (String) req.getSession().getAttribute("kaptcha");
       if (kaptcha != null && sessionKaptcha != null && kaptcha.equalsIgnoreCase(sessionKaptcha)) {
           return super.authenticate(authentication);
       }
       throw new AuthenticationServiceException("验证码输入错误");
   }
}

配置AuthenticationManager:


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Bean
   AuthenticationProvider kaptchaAuthenticationProvider() {
       InMemoryUserDetailsManager users = new InMemoryUserDetailsManager(User.builder()
               .username("xiepanapn").password("{noop}123").roles("admin").build());
       KaptchaAuthenticationProvider provider = new KaptchaAuthenticationProvider();
       provider.setUserDetailsService(users);
       return provider;
   }

@Override
   @Bean
   public AuthenticationManager authenticationManagerBean() throws Exception {
       ProviderManager manager = new ProviderManager(kaptchaAuthenticationProvider());
       return manager;
   }

@Override
   protected void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests()
               .antMatchers("/vc.jpg").permitAll()
               .anyRequest().authenticated()
               .and()
               .formLogin()
               .loginPage("/mylogin.html")
               .loginProcessingUrl("/doLogin")
               .defaultSuccessUrl("/index.html")
               .failureForwardUrl("/mylogin.html")
               .usernameParameter("uname")
               .passwordParameter("passwd")
               .permitAll()
               .and()
               .csrf().disable();
   }
}
  1. 配置UserDetailsService提供的数据源

  2. 提供AuthenticationProvider实例并配置UserDetailsService

  3. 重写authenticationManagerBean方法提供一个自己的ProviderManager并自定义AuthenticationManager实例。

二、自定义过滤器

LoginFilter继承UsernamePasswordAuthenticationFilter 重写attemptAuthentication方法:


public class LoginFilter extends UsernamePasswordAuthenticationFilter {

@Override
   public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
       if (!request.getMethod().equals("POST")) {
           throw new AuthenticationServiceException(
                   "Authentication method not supported: " + request.getMethod());
       }
       String kaptcha = request.getParameter("kaptcha");
       String sessionKaptcha = (String) request.getSession().getAttribute("kaptcha");
       if (!StringUtils.isEmpty(kaptcha) && !StringUtils.isEmpty(sessionKaptcha) && kaptcha.equalsIgnoreCase(sessionKaptcha)) {
           return super.attemptAuthentication(request, response);
       }
       throw new AuthenticationServiceException("验证码输入错误");
   }
}

在SecurityConfig中配置LoginFilter


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   @Override
   protected void configure(AuthenticationManagerBuilder auth)
           throws Exception {
       auth.inMemoryAuthentication()
               .withUser("javaboy")
               .password("{noop}123")
               .roles("admin");
   }

@Override
   @Bean
   public AuthenticationManager authenticationManagerBean()
           throws Exception {
       return super.authenticationManagerBean();
   }

@Bean
   LoginFilter loginFilter() throws Exception {
       LoginFilter loginFilter = new LoginFilter();
       loginFilter.setFilterProcessesUrl("/doLogin");
       loginFilter.setAuthenticationManager(authenticationManagerBean());
       loginFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/hello"));
       loginFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/mylogin.html"));
       return loginFilter;
   }

@Override
   protected void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests()
               .antMatchers("/vc.jpg").permitAll()
               .anyRequest().authenticated()
               .and()
               .formLogin()
               .loginPage("/mylogin.html")
               .permitAll()
               .and()
               .csrf().disable();
       http.addFilterAt(loginFilter(),
               UsernamePasswordAuthenticationFilter.class);
   }
}

显然第二种比较简单

来源:https://juejin.cn/post/7016269020861038623

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com