> 文档中心 > Security+Captcha验证码登录案例-带网页案例(附完整git项目源码)

Security+Captcha验证码登录案例-带网页案例(附完整git项目源码)


Security系列教程

  • ①Security入门体验
  • ②Security自定义账号密码验证+thymeleaf登录案例(附带网页案例
  • ③Security+验证码登录案例(附带网页案例)
  • ④Security+jwt 实现无状态认证,前后端分离(附带网页案例))
  • ⑤Security+jwt 短信认证登录,前后端分离(附带网页案例)
  • ⑥Security+oauth2 授权服务器(附带网页案例)

文章目录

  • Security系列教程
  • 简介
    • 1. 环境
    • 2. 生成验证码
    • 3. 验证码校验
    • 4. 最终效果演示
    • 5. 源码分享

简介

本章主要实现Spring Security自定义用户认证功能,并使用thymeleaf编写一个简单网页供测试使用。
在这里插入图片描述

  • Springboot版本2.5.3
  • 整合网页模板引擎thymeleaf
  • hutool工具包生成验证码

1. 环境

  • 基础环境搭建请参考:②Security自定义账号密码验证+thymeleaf登录案例(附带网页案例
  • pom.xml 中新增hutool工具包,用于生成验证码
  <dependency>     <groupId>cn.hutool</groupId>     <artifactId>hutool-all</artifactId>     <version>5.8.2</version> </dependency>

2. 生成验证码

  • 我们封装一个验证码校验工具类,我们的思路是:把验证码存入session会话中,需要校验时再从session会话中取出进行校验。
  • 首先封装一个RequestUtils工具,用于静态获取sessionrequest
/ * 请求工具类 * * @author ding */public class RequestUtils {    /     * 获取session     */    public static HttpSession getHttpSession() { return getHttpRequest().getSession();    }    /     * 获取request     */    public static HttpServletRequest getHttpRequest() { ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); assert servletRequestAttributes != null; return servletRequestAttributes.getRequest();    }}
  • 验证码生成工具CaptchaUtils
/ * 生成及校验图片验证码 * * @author ding */public class CaptchaUtils {    private static final String CAPTCHA = "captcha";    /     * CircleCaptcha 圆圈干扰验证码     * 定义图形验证码的长、宽、验证码字符数、干扰元素个数     */    public static void getCircleCaptcha(HttpSession session, HttpServletResponse response) throws IOException { LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100, 5, 20); session.setAttribute(CAPTCHA, lineCaptcha.getCode()); writeResp(lineCaptcha, response);    } /     * 验证码校验     *     * @param code 验证码     */    public static boolean verify(String code) { HttpSession session = RequestUtils.getHttpSession(); String captcha = (String) session.getAttribute(CAPTCHA); return code.equals(captcha);    }    /     * http图片响应     */    private static void writeResp(AbstractCaptcha abstractCaptcha, HttpServletResponse response) throws IOException { ServletOutputStream out = null; try {     out = response.getOutputStream();     abstractCaptcha.write(out); } finally {     if (Objects.nonNull(out)) {  out.close();     } }    }}
  • 编写一个验证码获取接口
    @GetMapping("/getCode")    @ResponseBody    public void getCode(HttpSession session, HttpServletResponse response) throws IOException { CaptchaUtils.getCircleCaptcha(session, response);    }
  • 验证码获取并不需要认证,所以我们在WebSecurityConfig中开放该请求。
  • 以下为WebSecurityConfig部分截取
@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)@RequiredArgsConstructorpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {      @Override    protected void configure(HttpSecurity http) throws Exception { http.headers().frameOptions().disable();//开启运行iframe嵌套页面 // 1、配置权限认证 http.authorizeRequests()  // 1. 配置不拦截路由  .antMatchers("/toLogin", "/getCode").permitAll()  // 任何其它请求  .anyRequest()  // 都需要身份认证  .authenticated()...   
  • 改造网页,新增验证码获取
<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head>    <meta charset="UTF-8">    <title>登陆页</title></head><body><div>    <form action="/login" method="post"> <h2>登陆页</h2> <h6>账号:admin,密码:123456</h6>   <img style="width: auto;height: 100px" src="/getCode"/> <br /> <label for="username">账号:</label><input style="margin-left: 17px" type="text" id="username" name="username" placeholder="admin"> <br /> <label for="password">密码:</label><input style="margin-left: 17px" type="password" id="password" name="password" placeholder="123456"> <br /> <label for="code">验证码:</label><input type="text" id="code" name="code" placeholder=""> <br /> <br /> <button type="submit">登陆</button> <div th:if="${param.captchaInvalid}" style="color: red;">     验证码错误. </div> <div th:if="${param.error}" style="color: red;">     账号或密码错误. </div> <div th:if="${param.logout}" style="color: red;">     登录失效. </div>    </form></div></body></html>
  • 启动项目,效果如下
    Security+Captcha验证码登录案例-带网页案例(附完整git项目源码)

3. 验证码校验

  • 我们定义一个异常类,用于处理验证校验错误,重写AuthenticationException,定义我们的验证码类异常。
/ * 验证码错误异常 * * @author ding */public class CaptchaInvalidException extends AuthenticationException {    public CaptchaInvalidException(String msg, Throwable cause) { super(msg, cause);    }    public CaptchaInvalidException(String msg) { super(msg);    }}
  • 接下来我们需要在UserDetailsServiceImpl中添加我们的校验逻辑,我们定义了一个code参数用来接收登录的验证码,由于security的默认登录接口只有一个用户名参数,那么我们如何获取验证码呢?
    Security+Captcha验证码登录案例-带网页案例(附完整git项目源码)
  • 其实很简单,还记得我们开头封装的RequestUtils 工具类吗,没错,我们可以通过静态获取request的方式获取请求参数,如下:
@Component@RequiredArgsConstructor@Slf4jpublic class UserDetailsServiceImpl implements UserDetailsService {    /     * 模拟一个数据库用户     * 账号 admin     * 密码 123456     */    private final static HashMap<String, MyUser> USER_MAP = new LinkedHashMap<>() { {     put("admin", new MyUser()      .setUserId(1L)      .setUsername("admin")      .setPassword(new BCryptPasswordEncoder().encode("123456"))     ); }    };    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { HttpServletRequest request = RequestUtils.getHttpRequest(); String code = request.getParameter("code"); log.info("登录者账号为:{},验证码:code:{}", username, code); // 验证码是否正确 if (!CaptchaUtils.verify(code)) {     throw new CaptchaInvalidException("验证码错误"); } // 通过userName获取用户信息,实战中把USER_MAP换成数据库获取即可 MyUser user = USER_MAP.get(username); if (user == null) {     throw new UsernameNotFoundException("not found"); } // 角色和权限都在这里添加,角色以ROLE_前缀,不是ROLE_前缀的视为权限,这里添加了ROLE_ADMIN角色和read、write权限 List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN,read,write"); AuthUser authUser = new AuthUser(user.getUsername(), user.getPassword(), authorities); // 我们存放我们自定义的信息,如用户id,方便后续获取用户信息 authUser.setUserId(user.getUserId()); return authUser;    }}
  • 可以看见我们在校验完验证码后,抛出了一个CaptchaInvalidException异常,这是我们之前定义的一个专门处理验证码错误的异常,这么做的目的是为了在后续处理中,我们可以根据业务,给web端返回不同的呈现信息。

  • 重新编写登录失败处理器

@Component@Slf4jpublic class LoginFailure extends SimpleUrlAuthenticationFailureHandler {    @Override    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException { log.info("登录失败"); ex.printStackTrace(); this.saveException(request, ex); if ("验证码错误".equals(ex.getMessage())) {     this.getRedirectStrategy().sendRedirect(request, response, "/toLogin?captchaInvalid=true"); } else {     this.getRedirectStrategy().sendRedirect(request, response, "/toLogin?error=true"); }    }}

4. 最终效果演示

在这里插入图片描述

5. 源码分享

本系列项目已收录
Springboot、SpringCloud全家桶教程+源码,各种常用框架使用案例都有哦,具备完善的文档,致力于让开发者快速搭建基础环境并让应用跑起来,并提供丰富的使用示例供使用者参考,快来看看吧。

  • 项目源码github地址
  • 项目源码国内gitee地址

开发者涨薪指南 Security+Captcha验证码登录案例-带网页案例(附完整git项目源码) 48位大咖的思考法则、工作方式、逻辑体系