• (五)Spring Security自定义过滤器


    在Spring Security中自定义一个的过滤器,将其添加到Spring Security过滤器链的合适位置。定义一个自己的过滤器类继承Filter接口即可。

    但是在 Spring 体系中,推荐使用
    OncePerRequestFilter来实现,它可以确保一次请求只会通过一次该过滤器(Filter实际上并不能保证这
    一点)。

    public class MySecurityFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            // 非登录请求,不处理
            if("/login".equals(httpServletRequest.getRequestURI())&&httpServletRequest.getMethod().equals(HttpMethod.POST.name())) {
                String username = httpServletRequest.getParameter("username");
                String password = httpServletRequest.getParameter("password");
                System.out.println("username:" + username);
                System.out.println("password:" + password);
            }else {
                System.out.println("非登录处理!");
            }
            filterChain.doFilter(httpServletRequest,httpServletResponse);
        }
    }
    

    创建Spring Security 配置类,继承WebSecurityConfigurerAdapter,重写方法void configure(HttpSecurity http),将自定义的过滤器添加到Spring Security 过滤器链中:

    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
            // 将自定义的过滤器添加到Spring Security 过滤器链中
            http.addFilterBefore(new MySecurityFilter(),UsernamePasswordAuthenticationFilter.class);
        }
    
    }
    

    将该过滤器添加到Spring Security的过滤器链中即可生效,Spring Security支持三种filter添加策略:

    public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity> implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {
     ......
      
         // 将自定义的过滤器添加在指定过滤器之后
        public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
            this.comparator.registerAfter(filter.getClass(), afterFilter);
            return this.addFilter(filter);
        }
        // 将自定义的过滤器添加在指定过滤器之前
        public HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) {
            this.comparator.registerBefore(filter.getClass(), beforeFilter);
            return this.addFilter(filter);
        }
    
        // 添加一个过滤器,但必须是Spring Security自身提供的过滤器实例或其子过滤器
        public HttpSecurity addFilter(Filter filter) {
            Class<? extends Filter> filterClass = filter.getClass();
            if (!this.comparator.isRegistered(filterClass)) {
                throw new IllegalArgumentException("The Filter class " + filterClass.getName() + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
            } else {
                this.filters.add(filter);
                return this;
            }
        }
    
        // 添加一个过滤器在指定过滤器位置
        public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
            this.comparator.registerAt(filter.getClass(), atFilter);
            return this.addFilter(filter);
        }
        ......    
    }
    

    重启服务测试:
    访问localhost:8080/login,会跳转到localhost:8080/login.html页面,输入账号密码,登录,整个流程的日志记录如下:

    非登录处理!
    username:admin
    password:aaaaaa
    非登录处理!
    

    实战:实现图片验证码

    参考:kaptcha谷歌验证码工具 https://www.cnblogs.com/zhangyuanbo/p/11214078.html

    maven引入验证码相关包

            <!--    图片验证码相关-->
            <dependency>
                <groupId>com.github.penggle</groupId>
                <artifactId>kaptcha</artifactId>
                <version>2.3.2</version>
            </dependency>
    

    获取图片验证码

    编写自定义的图片验证码校验过滤器:

      @Bean
        public DefaultKaptcha getDDefaultKaptcha() {
            DefaultKaptcha dk = new DefaultKaptcha();
            Properties properties = new Properties();
            // 图片边框
            properties.setProperty("kaptcha.border", "yes");
            // 边框颜色
            properties.setProperty("kaptcha.border.color", "105,179,90");
            // 字体颜色
            properties.setProperty("kaptcha.textproducer.font.color", "red");
            // 图片宽
            properties.setProperty("kaptcha.image.width", "110");
            // 图片高
            properties.setProperty("kaptcha.image.height", "40");
            // 字体大小
            properties.setProperty("kaptcha.textproducer.font.size", "30");
            // session key
            properties.setProperty("kaptcha.session.key", "code");
            // 验证码长度
            properties.setProperty("kaptcha.textproducer.char.length", "4");
            // 字体
            properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
            Config config = new Config(properties);
            dk.setConfig(config);
    
            return dk;
        }
    

    KaptchaController.java

    @Controller
    public class KaptchaController {
    
        /**
         * 验证码工具
         */
        @Autowired
        DefaultKaptcha defaultKaptcha;
    
        @GetMapping("/kaptcha.jpg")
        public void defaultKaptcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
                // 设置内容类型
                response.setContentType("image/jpeg");
                // 创建验证码文本
                String createText = defaultKaptcha.createText();
                // 将生成的验证码保存在session中
                request.getSession().setAttribute("kaptcha", createText);
                // 创建验证码图片
                BufferedImage bi = defaultKaptcha.createImage(createText);
    
                // 获取响应输出流
                ServletOutputStream out = response.getOutputStream();
                // 将图片验证码数据写入到图片输出流
                ImageIO.write(bi, "jpg", out);
    
                // 推送并关闭输出流
                out.flush();
                out.close();
            }
    
    }
    

    当用户访问/captcha.jpg时,即可得到一张携带验证码的图片,验证码文本则被存放到session中,用于后续的校验。

    图片验证码校验过滤器

    有了图形验证码的API之后,就可以自定义验证码校验过滤器。

    public class MyVerificationCodeFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            // 只处理登录请求
            if("/login".equals(request.getRequestURI())&&request.getMethod().equals(HttpMethod.POST.name())) {
                if(this.verificationCode(request, response)){
                    filterChain.doFilter(request, response);
                }else {
                    response.getWriter().write("verification code check failure!");
                }
            }else {
                filterChain.doFilter(request, response);
            }
        }
    
    
        private Boolean verificationCode(HttpServletRequest request,HttpServletResponse response){
            // 从session中获取正确的验证码
            HttpSession session = request.getSession();
            String kaptcha = (String) session.getAttribute("kaptcha");
    
            // 从参数中获取用户输入的验证码
            String code = request.getParameter("code");
            if (StringUtils.isEmpty(code)){
                // 清空session中的验证码,让用户重新获取
                session.removeAttribute("kaptcha");
                return false;
            }
            // 验证码校验
            if (!code.equals(kaptcha)){
                return false;
            }
            return true;
        }
    }
    
    

    MyVerificationCodeFilter添加在UsernamePasswordAuthenticationFilter之前,即在密码认证之前:

    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
            // 将自定义的过滤器添加到Spring Security 过滤器链中
            http
                    .addFilterBefore(new MyVerificationCodeFilter(),UsernamePasswordAuthenticationFilter.class);
        }
    

    自定义带验证码的登陆页

    在static文件夹下新建login.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form method="post" action="/login">
        <input type="text" name="username"/><br/>
        <input type="password" name="password"/><br/>
        <div style="display: inline-block">
            <input type="text" name="code"  required placeholder="验证码" />
            <img alt="验证码" onclick="this.src='/kaptcha.jpg'" src="/kaptcha.jpg" />
            <a>看不清?点击图片刷新一下</a>
        </div>
        </br>
        <input type="submit" value="登录">
    </form>
    </body>
    </html>
    
    

    修改WebSecurityConfig,设置自定义登录页URL:

    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    //        super.configure(http);
            http
                    .authorizeRequests()
                    .antMatchers("/kaptcha.jpg").permitAll()    // 放开验证码获取的访问地址
                    .anyRequest()
                    .authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/login.html")           // 自定义登录页URL
                    .loginProcessingUrl("/login")       // 自定义登陆处理请求地址
                    .permitAll();
            // 将自定义的过滤器添加到Spring Security 过滤器链中
            http
                    .addFilterBefore(new MyVerificationCodeFilter(),UsernamePasswordAuthenticationFilter.class);
        }
    }
    

    重启服务,测试执行

    在这里插入图片描述

  • 相关阅读:
    Pytorch环境搭建(Anaconda+Pycharm,清华镜像源)
    Leetcode 220. 存在重复元素 III (Contains Duplicate III)
    Leetcode 217. 存在重复元素 (Contains Duplicate)
    Leetcode 219. 存在重复元素 II (Contains Duplicate II)
    Leetcode 1073. 负二进制数相加(Adding Two Negabinary Numbers)
    极客战记(codecombat)攻略-地牢-Kithgard 地牢
    python elasticsearch 深度分页——scroll的使用与清除(clear_scroll)
    sqlmap参数
    文件包含
    web安全
  • 原文地址:https://www.cnblogs.com/hanliukui/p/16842683.html
Copyright © 2020-2023  润新知