(五)Spring Security自定義過濾器

在Spring Security中自定義一個的過濾器,將其添加到Spring Security過濾器鏈的合適位置。定義一個本身的過濾器類繼承Filter接口便可。html

可是在 Spring 體系中,推薦使用
OncePerRequestFilter來實現,它能夠確保一次請求只會經過一次該過濾器(Filter實際上並不能保證這
一點)。java

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 過濾器鏈中:git

@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添加策略:github

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頁面,輸入帳號密碼,登陸,整個流程的日誌記錄以下:session

非登陸處理!
username:admin
password:aaaaaa
非登陸處理!

實戰:實現圖片驗證碼

參考:kaptcha谷歌驗證碼工具 https://www.cnblogs.com/zhangyuanbo/p/11214078.htmlapp

maven引入驗證碼相關包

<!-- 圖片驗證碼相關-->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

獲取圖片驗證碼

編寫自定義的圖片驗證碼校驗過濾器:maven

@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.javaide

@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以後,就能夠自定義驗證碼校驗過濾器。post

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);
    }
}

重啓服務,測試執行

在這裏插入圖片描述

相關文章
相關標籤/搜索