Spring Security 验证码

原理、存在问题、解决思路
我们知道Spring Security是通过过滤器链来完成了,所以它的解决方案是创建一个过滤器放到Security的过滤器链中,在自定义的过滤器中比较验证码

1 添加依赖(生成验证码)

<!--引入hutool-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.9</version>
</dependency>

2 生产验证码

CodeController


@Controller
@RequestMapping("/code")
public class CodeController {

    @RequestMapping("/img")
    public void code(HttpServletRequest request, HttpServletResponse response) {

        //创建验证码长,宽,字符数,干扰元素个数
        CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
        // 放在session里面

        System.out.println("生成的验证码" + circleCaptcha.getCode());
        request.getSession().setAttribute("circleCaptcha", circleCaptcha.getCode());
        // 用流写出去
        try {
            ImageIO.write(circleCaptcha.getImage(), "JPEG", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

3 创建验证码过滤器

ValidateCodeFilter

@Component
public class ValidateCodeFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //得到请求地址
        String requestURI = request.getRequestURI();
        System.out.println("requestURL" + requestURI);

        //判断是否是登录请求
        if (requestURI.equals("/login/doLogin")) {
            //说明当前请求为登陆
            //1,得到登陆时用户输入的验证码
            String code1 = request.getSession().getAttribute("circleCaptcha").toString();
            String code = request.getParameter("code");
            System.out.println("用户输入的验证码:" + code);
            if (StringUtils.hasText(code)) {
                if (code.equalsIgnoreCase(code1)) {
                    //说明验证码正确  直接放行
                    request.getSession().removeAttribute("errorMSg");
                    filterChain.doFilter(request, response);
                    return;
                } else {
                    //说明验证码不正确,返回登陆页面
                    request.getSession().setAttribute("errorMsg", "验证码错误");
                    response.sendRedirect("/index/toLogin");
                    return;
                }
            } else {
                //用户没有输出验证码重定向到登陆页面
                request.getSession().setAttribute("errorMsg", "验证码不能为空");
                response.sendRedirect("/index/toLogin");
                return;
            }
        } else {
            //说明不是登陆 直接放行到下一个过滤器
            filterChain.doFilter(request, response);
            return;
        }

    }
}

4 配置类放行

在这里插入图片描述
在这里插入图片描述


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /*注入 登录成功处理器*/
    @Autowired
    private AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;

    /*注入 登录 失败处理器*/
    @Autowired
    private AppAuthenticationFailureHandler appAuthenticationFailureHandler;

    /*注入  没有权限处理器*/
    @Autowired
    private AppAccessDeniedHandler appAccessDeniedHandler;

    /*注入 登出成功处理器*/
    @Autowired
    private AppLogoutSuccessHandler appLogoutSuccessHandler;

    @Autowired
    private AppUserDetailsService appUserDetailsService;

    //验证码拦截器注入
    @Autowired
    private ValidateCodeFilter validateCodeFilter;

    /*配置多用户*/
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(appUserDetailsService);
    }

    /*http请求配置*/

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure(http);// 不使用父类的 方法, 需要 提供登录配置
        /*没权权限的处理*/
//        http.exceptionHandling().accessDeniedHandler(appAccessDeniedHandler);
        /*登录*/
        // 配置登录之前添加一个验证码的过滤器
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.formLogin()
                .usernameParameter("uname")//页面表单账号的参数名  默认 为 username
                .passwordParameter("pwd")//页面表单密码的参数名   默认 为 password
                .loginPage("/index/toLogin")//定义登录页面的 请求 地址(转发到登录页面)
                .loginProcessingUrl("/login/doLogin")// 表单提交的 地址(不需要提供),登录验证.....
                .successForwardUrl("/index/toIndex")//登录成功 跳转的路径
                .failureForwardUrl("/index/toLogin")//登录失败 跳转的路径
//                .successHandler(appAuthenticationSuccessHandler)//登录成功处理器
//                .failureHandler(appAuthenticationFailureHandler)//登录失败处理器
                .permitAll();
        ;

        /*登出*/
        http.logout()
                .logoutUrl("/logout")//登出的 请求地址
                .logoutSuccessUrl("/index/toLogin")//登出成功后 访问的路径
//                .logoutSuccessHandler(appLogoutSuccessHandler)//登出成功处理器
                .permitAll()
        ;
          /*设置 资源所需要的 权限 (好比 门上锁)*/
        http.authorizeRequests()
//                .mvcMatchers("/index/toLogin", "/index.html","/code/img").permitAll()//不需要认证就可以访问
                .antMatchers("/code/img")  // 放行验证码的路径
                .permitAll()
                .anyRequest().authenticated()//所有请求都需要登录认证 才能进行

        ;
        /*禁用csrf跨域请求攻击   如果不禁用  自定义的登录页面无法登录*/
        http.csrf().disable();
    }

    /*资源服务匹配放行:静态资源*/
    @Override
    public void configure(WebSecurity web) throws Exception {
//        super.configure(web);
        web.ignoring().antMatchers("/css/**");
    }

    /*强制要求配置 密码加密器*/
    @Bean// 将对象 交给 spring容器 管理
    public PasswordEncoder passwordEncoder() {
//        return NoOpPasswordEncoder.getInstance();//不加密
        return new BCryptPasswordEncoder();
    }

}

5 前端template

<!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="${session.errorMsgs}" style="color: #c41f1f;">帐号或密码错误,请重新输入</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" value="123456"></td>
        </tr>
        <td>验证码:</td>
        <td><input type="text" name="code"> <img src="/code/img" style="height:33px;cursor:pointer;"
                                                 onclick="this.src=this.src">
            <span th:text="${session.errorMsg}" style="color: #FF0000;"></span>
        </td>
        <tr>
            <td colspan="2">
                <button type="submit">登录</button>
            </td>
        </tr>
    </table>
</form>
</body>

效果图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

huangshaohui00

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值