Springboot+SpringSecurity實現圖片驗證碼登入問題
阿新 • • 發佈:2020-02-08
這個問題,網上找了好多,結果程式碼都不全,找了好多,要不是就自動注入的類注入不了,編譯報錯,要不異常捕獲不了浪費好多時間,就覺得,框架不熟就不能隨便用,全是坑,氣死我了,最後改了兩天.終於弄好啦;
問題主要是:
- 返回的驗證碼不知道在SpringSecurity的哪裡和存在Session裡的比較.
- 比較之後應該怎麼處理,
- 其次是捕獲驗證碼錯誤異常的處理,
這個問題比較多,網上大都是直接注入一個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;
流程
-
請求登入頁,將驗證碼結果存到基於Servlet的session裡,以JSON格式返回驗證碼,
-
之後前端傳送登入請求,SpringSecurity中處理,自定義一個filter讓它繼承自OncePerRequestFilter,然後重寫doFilterInternal方法,在這個方法中實現驗證碼的功能,如果驗證碼錯誤就丟擲一個繼承自AuthenticationException的驗證嗎錯誤的異常訊息寫入到響應訊息中.
-
之後返回異常資訊
下面以這個順序書寫程式碼:
依賴大家照著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;