1. 程式人生 > >Springboot+SpringSecurity實現圖片驗證碼登入問題

Springboot+SpringSecurity實現圖片驗證碼登入問題

這個問題,網上找了好多,結果程式碼都不全,找了好多,要不是就自動注入的類注入不了,編譯報錯,要不異常捕獲不了浪費好多時間,就覺得,框架不熟就不能隨便用,全是坑,氣死我了,最後改了兩天.終於弄好啦;

問題主要是:

  1. 返回的驗證碼不知道在SpringSecurity的哪裡和存在Session裡的比較.
  2. 比較之後應該怎麼處理,
  3. 其次是捕獲驗證碼錯誤異常的處理,

這個問題比較多,網上大都是直接注入一個AuthenticationFailureHandler,我就不明白這個咋注進去的,我這個一寫就報錯,注入不進去,後來就想自己new一個哇,就OK了

new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        RespBean respBean = RespBean.error("驗證碼錯誤!");
                        out.write(new ObjectMapper().writeValueAsString(respBean));
                        out.flush();
                        out.close();
                    }
                }.onAuthenticationFailure(request, response, e);
                return;

​​​​​​

流程 

  1.  請求登入頁,將驗證碼結果存到基於Servlet的session裡,以JSON格式返回驗證碼,

  2. 之後前端傳送登入請求,SpringSecurity中處理,自定義一個filter讓它繼承自OncePerRequestFilter,然後重寫doFilterInternal方法,在這個方法中實現驗證碼的功能,如果驗證碼錯誤就丟擲一個繼承自AuthenticationException的驗證嗎錯誤的異常訊息寫入到響應訊息中.

  3. 之後返回異常資訊

下面以這個順序書寫程式碼:

依賴大家照著import導一下吧,記得有這兩個,驗證碼需要一個依賴,之後還使用了一個工具包

​

 <!--圖片驗證-->
        <dependency>
            <groupId>com.github.whvcse</groupId>
            <artifactId>easy-captcha</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

前端使用Vue+ElementUI

     
        <div class="login-code">
          <img :src="codeUrl"
               @click="getCode">
        </div>
 
     

後端程式碼:

獲取驗證碼,將結果放到session裡

package com.liruilong.hros.controller;

import com.liruilong.hros.model.RespBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wf.captcha.ArithmeticCaptcha;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description :
 * @Author: Liruilong
 * @Date: 2019/12/19 19:58
 */
@RestController
public class LoginController {
    
    @GetMapping(value = "/auth/code")
    public Map getCode(HttpServletRequest request,HttpServletResponse response){
        // 算術型別 https://gitee.com/whvse/EasyCaptcha
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        // 幾位數運算,預設是兩位
        captcha.setLen(2);
        // 獲取運算的結果
        String result = captcha.text();
        System.err.println("生成的驗證碼:"+result);
        // 儲存
        // 驗證碼資訊
        Map<String,Object> imgResult = new HashMap<String,Object>(2){{
            put("img", captcha.toBase64());
    
        }};
        request.getSession().setAttribute("yanzhengma",result);

        return imgResult;
    }
}

security配置. 

在之前的基礎上加filter的基礎上加了

http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class)

 /**
     * @Author Liruilong
     * @Description  放行的請求路徑
     * @Date 19:25 2020/2/7
     * @Param [web]
     * @return void
     **/
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/login","/css/**","/js/**", "/index.html", "/img/**", "/fonts/**","/favicon.ico","/auth/code");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                //.anyRequest().authenticated()
             ...........

    }

定義一個VerifyCodeFilter 過濾器

package com.liruilong.hros.filter;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.liruilong.hros.Exception.ValidateCodeException;
import com.liruilong.hros.model.RespBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


/**
 * @Description :
 * @Author: Liruilong
 * @Date: 2020/2/7 19:39
 */
@Component
public class VerifyCodeFilter extends OncePerRequestFilter {
    
    @Bean
    public VerifyCodeFilter getVerifyCodeFilter() {
        return new VerifyCodeFilter();
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, ValidateCodeException {

        if (StringUtils.equals("/doLogin", request.getRequestURI())
                && StringUtils.equalsIgnoreCase(request.getMethod(), "post")) {
            // 1. 進行驗證碼的校驗
            String requestCaptcha = request.getParameter("code");
            String code = (String) request.getSession().getAttribute("yanzhengma");
            logger.info("開始校驗驗證碼,生成的驗證碼為:" + code + " ,輸入的驗證碼為:" + requestCaptcha);
            try {
                if (StringUtils.isBlank(requestCaptcha)) {
                    throw new ValidateCodeException("驗證碼不能為空!");
                }
                if (requestCaptcha == null) {
                    throw new ValidateCodeException("驗證碼不存在");
                }
                if (!StringUtils.equals(code, requestCaptcha)) {
                    throw new ValidateCodeException("驗證碼不匹配");
                }
            } catch (AuthenticationException e) {
                // 2. 捕獲步驟1中校驗出現異常,交給失敗處理類進行進行處理
                new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        RespBean respBean = RespBean.error("驗證碼錯誤!");
                        out.write(new ObjectMapper().writeValueAsString(respBean));
                        out.flush();
                        out.close();
                    }
                }.onAuthenticationFailure(request, response, e);
                return;
            }
            filterChain.doFilter(request, response);
        }
        filterChain.doFilter(request, response);
    }


}

之後就可以啦

感覺主要是捕獲驗證碼錯誤異常的處理,

這個問題比較多,網上大都是直接注入一個AuthenticationFailureHandler,我就不明白這個咋注進去的,我這個一寫就報錯,注入不進去,後來就想自己new一個哇,

new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        RespBean respBean = RespBean.error("驗證碼錯誤!");
                        out.write(new ObjectMapper().writeValueAsString(respBean));
                        out.flush();
                        out.close();
                    }
                }.onAuthenticationFailure(request, response, e);
                return;