• (五)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);
        }
    }
    

    重启服务,测试执行

    在这里插入图片描述

  • 相关阅读:
    Attributes in C#
    asp.net C# 时间格式大全
    UVA 10518 How Many Calls?
    UVA 10303 How Many Trees?
    UVA 991 Safe Salutations
    UVA 10862 Connect the Cable Wires
    UVA 10417 Gift Exchanging
    UVA 10229 Modular Fibonacci
    UVA 10079 Pizza Cutting
    UVA 10334 Ray Through Glasses
  • 原文地址:https://www.cnblogs.com/hanliukui/p/16842683.html
Copyright © 2020-2023  润新知