前言
现在各大网站登录的方式是越来越多。比如:传统的用户名密码登录、快捷的邮箱、手机验证码登录,还有流行的第三方登录。那本篇呢,就给大家带来如何在 Spring Security 中定义使用邮箱验证码登录方式。看完本篇,让你学会自定义认证方式,如果公司还需要使用手机验证码登录,简简单单就能集成,毕竟流程是一致的。
编码
自定义验证方式需要使用到 Spring Security
内置的几个对象,如果各位还不了解,可以先看看这篇文章:Spring Security 中重要对象汇总
用户名密码表单登录会进入到 UsernamePasswordAuthenticationFilter
。在这整个类中,还会用到一个对象 UsernamePasswordAuthenticationToken
。
AbstractAuthenticationProcessingFilter
AbstractAuthenticationToken
EmailVerificationCodeAuthenticationFilter
参考 UsernamePasswordAuthenticationFilter
(copy)类并进行删减, 定义 EmailVerificationCodeAuthenticationFilter
类,内容如下:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author cxyxj
*/
public class EmailVerificationCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
/**
* 默认的请求参数名称
*/
public static final String EMAIL_KEY = "email";
public static final String EMAIL_CODE_KEY = "emailCode";
private String emailParameter = EMAIL_KEY;
private String emailCodeParameter = EMAIL_CODE_KEY;
/**
* 是否仅支持post方式
*/
private boolean postOnly = true;
/**
* 对请求进行过滤,只有接口为 /emil-login,请求方式为 POST,才会进入逻辑
*/
public EmailVerificationCodeAuthenticationFilter() {
super(new AntPathRequestMatcher("/email-login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
// 需要是 POST 请求
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
// 判断请求格式是否 JSON
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
Map<String, String> loginData = new HashMap<>(2);
try {
loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
} catch (IOException e) {
throw new InternalAuthenticationServiceException("请求参数异常");
}
// 获得请求参数
String email = loginData.get(emailParameter);
String emailCode = loginData.get(emailCodeParameter);
// 检查验证码
checkEmailCode(emailCode);
if(StringUtils.isEmpty(email)){
throw new AuthenticationServiceException("邮箱不能为空");
}
/**
* 使用请求参数传递的邮箱和验证码,封装为一个未认证 EmailVerificationCodeAuthenticationToken 身份认证对象,
* 然后将该对象交给 AuthenticationManager 进行认证
*/
EmailVerificationCodeAuthenticationToken authRequest = new EmailVerificationCodeAuthenticationToken(email);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
return null;
}
public void setDetails(HttpServletRequest request , EmailVerification