Spring Security (6): Realize graphical verification code login

The first five articles have learned some of the simplest login authentication processes and the source code of the entire authentication process. This article will take a look at how to implement the login of the graphic verification code.

Develop and generate verification code interface

Get captcha image interface

  • Generate pictures based on random numbers (there are many codes for generating verification code pictures on the Internet, so I won’t post them here)
  • Store the random number in the Session
  • Write the generated picture into the response of the interface
	@GetMapping("/code/image")
	public void createCode(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
		// 生成图片
		ImageCode imageCode = createCodeImageCode(request);
		// 保存到session
		sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
		// 显示给前端
		ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
	}
  • The interface needs to be configured not to be intercepted
	@Override
	protected void configure(HttpSecurity http) throws Exception {
    
    
		http.formLogin()
				.loginPage("/authentication/require")
				.loginProcessingUrl("/authentication/form")
				.successHandler(meicloudAuthenticationSuccessHandler)
				.failureHandler(meicloudAuthenticationFailureHandler)
				.and()
				.authorizeRequests()
				.antMatchers("/authentication/require",
						securityProperties.getBrowser().getSignInPage(),
						// 配置 “/code/image” 不被拦截
						"/code/image").permitAll()
				.anyRequest()
				.authenticated()
				.and()
				.csrf().disable();
	}

Front page

  • Distal need to pass back imageCode, after obtaining the background and Sessionstored in verifying a verification code.
  • Page code:
	<form action="/authentication/form" method="post">
		<table>
			<tr>
				<td>用户名:</td> 
				<td><input type="text" name="username"></td>
			</tr>
			<tr>
				<td>密码:</td>
				<td><input type="password" name="password"></td>
			</tr>
			<tr>
				<td>图形验证码:</td>
				<td>
					<input type="text" name="imageCode">
					<img src="/code/image?width=200">
				</td>
			</tr>
			<tr>
				<td colspan="2"><button type="submit">登录</button></td>
			</tr>
		</table>
	</form>

Add graphic verification code verification in the authentication process

Check logic

  • First of all, you need to define a filter in the background to process the request for verifying the verification code. After the verification is passed, it will continue to call the subsequent filters UserNamePasswordAuthenticationFilter. If the verification fails, an exception will be thrown and an error message will be returned.
public class ValidateCodeFilter extends OncePerRequestFilter {
    
    
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
    
    

		ValidateCodeType type = getValidateCodeType(request);
		if (type != null) {
    
    
			logger.info("校验请求(" + request.getRequestURI() + ")中的验证码,验证码类型" + type);
			try {
    
    
				// 进行验证码的校验
				validateCodeProcessorHolder.findValidateCodeProcessor(type)
						.validate(new ServletWebRequest(request, response));
				logger.info("验证码校验通过");
			} catch (ValidateCodeException exception) {
    
    
				// 如果校验抛出异常,则交给我们之前文章定义的异常处理器进行处理
				authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
				return;
			}
		}
		// 继续调用后边的过滤器
		chain.doFilter(request, response);
	}
}
  • Method of verification
	@SuppressWarnings("unchecked")
	@Override
	public void validate(ServletWebRequest request) {
    
    

		// 获取session中的imageCode
		ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY);

		String codeInRequest;
		try {
    
    
			// 获取前端传过来的imageCode
			codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
					"imageCode");
		} catch (ServletRequestBindingException e) {
    
    
			throw new ValidateCodeException("获取验证码的值失败");
		}
		if (StringUtils.isBlank(codeInRequest)) {
    
    
			throw new ValidateCodeException(codeType + "验证码的值不能为空");
		}
		if (codeInSession == null) {
    
    
			throw new ValidateCodeException(codeType + "验证码不存在");
		}
		if (codeInSession.isExpried()) {
    
    
			validateCodeRepository.remove(request, codeType);
			throw new ValidateCodeException(codeType + "验证码已过期");
		}
		if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
    
    
			throw new ValidateCodeException(codeType + "验证码不匹配");
		}
		// 校验完之后记得将其移除
		sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
	}
  • Add the filter of the verification code defined by yourself to the UserNamePasswordAuthenticationFilterprevious
	@Override
	protected void configure(HttpSecurity http) throws Exception {
    
    
		// 验证码校验过滤器
		ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
		// 将验证码校验过滤器加到 UsernamePasswordAuthenticationFilter 过滤器之前
		http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
				.formLogin()
				.loginPage("/authentication/require")
				.loginProcessingUrl("/authentication/form")
				.successHandler(meicloudAuthenticationSuccessHandler)
				.failureHandler(meicloudAuthenticationFailureHandler)
				.and()
				.authorizeRequests()
				.antMatchers("/authentication/require",
						securityProperties.getBrowser().getSignInPage(),
						"/code/image").permitAll()
				.anyRequest()
				.authenticated()
				.and()
				.csrf().disable();
	}

Guess you like

Origin blog.csdn.net/qq_36221788/article/details/106066109