目录
- 1、生成图片验证码
- 2、创建验证码过滤器
- 3、将过滤器加入SpringSecurity过滤链
- 4、修改登录页
SpringSecurity是通过过滤器链来完成的,接下来的验证码,可以尝试创建一个过滤器放到Security的过滤器链中,在自定义的过滤器中比较验证码。
1、生成图片验证码
引入hutool依赖,用于生成验证码。(这个单词怎么读?糊涂?)
<!--引入hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.3.9</version> </dependency>
写个用于生成验证码的接口:
//HttpServletRequest和HttpServletResponse对象使用自动注入或者写在controller方法的形参都行 @Controller @Slf4j public class CaptchaController { @GetMapping("/code/image") public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException { //创建一个验证码 CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(200, 100, 2, 20); //获取生成的图片二维码中的值,并放到session中 String captchaCode=circleCaptcha.getCode(); log.info("生成的验证码为:{}",captchaCode); request.getSession().setAttribute("LOGIN_CAPTCHA_CODE",captchaCode); //将图片写到响应流中,参数一是图片。参数二是图片格式,参数三是响应流 ImageIO.write(circleCaptcha.getImage(),"JPEG",response.getOutputStream()); } }
此时,调用这个接口会在响应里返回一个图片。调用下接口看看效果:
2、创建验证码过滤器
很明显,校验验证码要先于校验用户名密码,验证码都不对,就不用往下验证用户名和密码了。新建自定义过滤器类ValidateCodeFilter,继承抽象类OncePerRequestFilter
。右键看下继承关系:
可以看到最终是实现了Filter接口。但这里别直接实现Filter,继承OncePerRequestFilter,里面的好多东西直接用,能省一点是一点。过滤器的实现思路为:
- 从前端获取验证码
- 从session中获取验证码(生成验证码的时候塞session里了)
- 判断是否相等
@Component @Slf4j public class ValidateCodeFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //因为最后是Filter接口,所以所有请求都过这个过滤器 //这里要先判断接口是不是登录接口,不是就别对比session和前端传来的验证码了 String requestURI = request.getRequestURI(); //URI即去掉IP、PORT那串 log.info("请求的URI为:{}", requestURI); if (!requestURI.equals("/login/doLogin")) { doFilter(request, response, filterChain); //不是登录接口直接放行 } else { validateCode(request, response,filterChain); //是登录接口则校验 } } private void validateCode(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain) throws IOException, ServletException { String enterCaptchaCode = request.getParameter("code"); //从请求中拿传过来的验证码的值(dto好些) HttpSession session = request.getSession(); String captchaCodeInSession = (String) session.getAttribute("LOGIN_CAPTCHA_CODE"); //保存在session中的验证码 log.info("用户输入的验证码为:{},session中的验证码为:{}",enterCaptchaCode,captchaCodeInSession); //移除session中之前可能存在的错误信息 session.removeAttribute("captchaCodeErrorMsg"); if (!StringUtils.hasText(captchaCodeInSession)) { session.removeAttribute("LOGIN_CAPTCHA_CODE"); } if (!StringUtils.hasText(enterCaptchaCode) || !StringUtils.hasText(captchaCodeInSession) || !enterCaptchaCode.equalsIgnoreCase(captchaCodeInSession)) { //说明验证码不正确,返回登陆页面 session.setAttribute("captchaCodeErrorMsg", "验证码不正确"); //验证失败,重定向到登录页面 response.sendRedirect("/login/toLogin"); }else{ filterChain.doFilter(request,response); //验证成功,放行 } } }
关于requset.getParameter("code")
关于在controller中接收前端数据的方式
:
关于直接在请求中获取参数的建议:
3、将过滤器加入SpringSecurity过滤链
修改WebSecurityConfig:
@EnableGlobalMethodSecurity(prePostEnabled = true) @Slf4j public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private ValidateCodeFilter validateCodeFilter; //自动注入我定义的验证码过滤器 @Override / * Security的http请求配置 * * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { //设置登陆方式 http.formLogin()//使用用户名和密码的登陆方式 .usernameParameter("uname") //页面表单的用户名的name .passwordParameter("pwd")//页面表单的密码的name .loginPage("/login/toLogin") //自己定义登陆页面的地址 .loginProcessingUrl("/login/doLogin")//配置登陆的url .successForwardUrl("/index/toIndex") //登陆成功跳转的页面 .failureForwardUrl("/login/toLogin")//登陆失败跳转的页面 .permitAll(); //配置退出方式 http.logout() .logoutUrl("/logout") .logoutSuccessUrl("/login/toLogin") .permitAll(); //配置路径拦截 的url的匹配规则 http.authorizeRequests().antMatchers("/code/image").permitAll() //任何路径要求必须认证之后才能访问 .anyRequest().authenticated(); // 禁用csrf跨站请求,注意不要写错了 http.csrf().disable(); // 配置登录之前添加一个验证码的过滤器 http.addFilterBefore(validateCodeFilter,UsernamePasswordAuthenticationFilter.class); } / * 资源服务匹配放行【静态资源文件】 * * @param web * @throws Exception */ // @Override //public void configure(WebSecurity web) throws Exception { // web.ignoring().mvcMatchers("/resources/"); //} @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
以上注意两点,一是别忘了放行生成二维码的接口,这个不需要登录鉴权。
http.authorizeRequests() .antMatchers("/code/image") .permitAll() //任何路径要求必须认证之后才能访问 .anyRequest().authenticated();
而是在用户名密码验证过滤器前加一个自定义的验证码过滤器,addFilter方法
:
http.addFilterBefore(validateCodeFilter,UsernamePasswordAuthenticationFilter.class);
4、修改登录页
修改login.html:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>用户登陆</title> </head> <body> <h2>登录页面</h2> <!--${param.error}这个如果有值,就显示帐号或密码错误--> <h4 th:if="${param.error}" style="color: #FF0000;">帐号或密码错误,请重新输入</h4> <form action="/login/doLogin" method="post"> <table> <tr> <td>用户名:</td> <td><input type="text" name="uname" value="zhangsan"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="pwd"></td> </tr> <tr> <td>验证码:</td> <td><input type="text" name="code"> <img src="/code/image" style="height:33px;cursor:pointer;" onclick="this.src=this.src"> <span th:text="${session.captchaCodeErrorMsg}" style="color: #FF0000;" >username</span> </td> </tr> <tr> <td colspan="2"> <button type="submit">登录</button> </td> </tr> </table> </form> </body>
效果:
到此这篇关于SpringSecurity集成图片验证码的文章就介绍到这了,更多相关SpringSecurity图片验证码内容请搜索本网站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本网站!
您可能感兴趣的文章:
- Springboot+SpringSecurity实现图片验证码登录的示例
- SpringSecurity添加图形验证码认证实现
- SpringSecurity实现图形验证码功能的实例代码
- SpringBoot + SpringSecurity 短信验证码登录功能实现
- SpringBoot结合SpringSecurity实现图形验证码功能