SpringSecurity入門3---過濾器實現圖形驗證碼

代碼地址

思路

後端生成驗證碼保存在Session中(Redis也可以),當前端輸入驗證碼進行登錄時,在校驗用戶名密碼之前校驗驗證碼是否正確,不正確就拋出異常,由失敗處理器進行處理

實現

使用Kaptcha進行驗證碼以及圖片的生成,先引入依賴

		<dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

WebSecurityConfig文件中注入我們的驗證碼生成器

	@Bean
    // 配置驗證碼工具
    public Producer captcha(){
        Properties properties = new Properties();
        properties.setProperty("kaptcha.image.width","150");
        properties.setProperty("kaptcha.image.height","50");
        // 字符集
        properties.setProperty("kaptcha.textproducer.char.string","0123456789");
        // 字符長度
        properties.setProperty("kaptcha.textproducer.char.length","4");
        Config config = new Config(properties);
        // 使用默認圖形驗證碼
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

接下來我們創建一個Controller用於生成驗證碼圖片,生成驗證碼並將其放入Session中

@Controller
public class KaptchaController {

    @Autowired
    private Producer captcha;

    @GetMapping("/captcha.jpg")
    public void getCaptcha(HttpServletRequest request,
                           HttpServletResponse response){
        response.setContentType("image/jpeg");
        String text = captcha.createText();
        // 將生成的驗證碼放入session中
        request.getSession().setAttribute("captcha",text);
        BufferedImage bi = captcha.createImage(text);
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            ImageIO.write(bi,"jpg",outputStream);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

記得在配置文件中對生成驗證碼圖片的路徑放行

httpp.authorizeRequests()
                .antMatchers("/css/**", "/img/**", "/js/**", "/bootstrap/**","/captcha.jpg").permitAll()

修改前端代碼使用img標籤獲取驗證碼圖片,並添加用於輸入驗證碼的輸入框

						<form action="/login" method="POST">
                            <div class="form-group">
                                <label for="username">User-Name</label>

                                <input id="username" class="form-control" name="username" value="" required autofocus>
                            </div>
                            <div class="form-group">
                                <label for="password">Password
                                    <a href="forgot.html" class="float-right">
                                        Forgot Password?
                                    </a>
                                </label>
                                <input id="password" type="password" class="form-control" name="password" required
                                       data-eye>
                            </div>
                            <div class="form-group">
                                <label for="captcha">驗證碼
                                </label>
                                <input id="captcha" type="text" class="form-control" name="captcha" required>
                                <img src="/captcha.jpg" alt="captcha" height="50px" width="150px">
                            </div>
                            <div class="form-group">
                                <label>
                                    <input type="checkbox" name="remember"> Remember Me
                                </label>
                            </div>
                            <div class="form-group no-margin">
                                <button type="submit" class="btn btn-primary btn-block">
                                    Login
                                </button>
                            </div>
                            <div class="margin-top20 text-center">
                                Don't have an account? <a href="register.html">Create One</a>
                            </div>
                        </form>

在我們配置的入參爲HttpSecurity的configure中,查看http的方法可以看到有addFilterAfter,addFilterBefore,addFilter,addFilterAt用於添加過濾器,其實SpringSecurity的原理就是過濾器鏈,之前我們一系列的操作就是基於過濾器鏈完成的,接下來我們創建自己的攔截器用於驗證驗證驗證碼是否正確,事項Filter接口即可,這裏實現的是OncePerRequestFilter,它可以保證一次請求只會經過一次該過濾器

public class VerificationCodeFilter extends OncePerRequestFilter {

    private AuthenticationFailureHandler failureHandler = new MyFailureHandler();

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        if(!"/login".equals(httpServletRequest.getRequestURI())){
            // 非登陸請求就不驗證驗證碼
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }else {
            try{
                verificationCode(httpServletRequest);
                filterChain.doFilter(httpServletRequest, httpServletResponse);
            }catch (VerificationCodeException e){
                failureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
            }
        }
    }

    private void verificationCode(HttpServletRequest httpServletRequest) throws VerificationCodeException {
        String requestCode = httpServletRequest.getParameter("captcha");
        HttpSession session = httpServletRequest.getSession();
        String vertificationCode = (String) session.getAttribute("captcha");
        // 不論校驗成功還是失敗,要保證session的驗證碼被刪除
        session.removeAttribute("captcha");
        if(StringUtils.isEmpty(requestCode) || StringUtils.isEmpty(vertificationCode)
                || !requestCode.equals(vertificationCode)){
            throw new VerificationCodeException();
        }
    }
}

這裏我將之前的失敗處理器單獨的放在了一個類裏面,沒有像之前使用內部類了。還創建了一個自定義的異常,用於驗證碼校驗失敗

public class VerificationCodeException extends AuthenticationException {
    public VerificationCodeException() {
        super("驗證碼校驗失敗");
    }
}

再在配置文件中將我們的攔截器添加進去即可

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 開啓註解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MySuccessHandler successHandler;

    @Autowired
    private MyFailureHandler failureHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/css/**", "/img/**", "/js/**", "/bootstrap/**","/captcha.jpg").permitAll()
                .antMatchers("/app/api/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage("/myLogin.html")
                .loginProcessingUrl("/login")
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .authenticationDetailsSource(myWebAuthenticationDetailsSource)
                .permitAll()
                // 使登錄頁不受限
                .and()
                .csrf().disable()
                // 在驗證用戶名密碼之前驗證驗證碼信息
                .addFilterBefore(new VerificationCodeFilter(),
                UsernamePasswordAuthenticationFilter.class);
    }

    @Autowired
    private DataSource dataSource;

    @Autowired
    private MyUserDetailService userDetailService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder);
    }
	
	@Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    // 配置驗證碼工具
    public Producer captcha(){
        Properties properties = new Properties();
        properties.setProperty("kaptcha.image.width","150");
        properties.setProperty("kaptcha.image.height","50");
        // 字符集
        properties.setProperty("kaptcha.textproducer.char.string","0123456789");
        // 字符長度
        properties.setProperty("kaptcha.textproducer.char.length","4");
        Config config = new Config(properties);
        // 使用默認圖形驗證碼
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

接下來訪問登錄頁測試即可
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章