手机号验证码登陆
概述
spring security默认使用的是用户名密码的登陆,如果想要增加一种登陆方式,
就要仿照源码中用户名密码登陆的方式, 手写一套手机号验证码登陆校验, 话不多说, 开干!
搭建基础架构
搭建项目基础架构工作, 在(一)中已经完成, 这里直接在其基础上继续开发
认证流程
- 1.进入 UsernamePasswordAuthenticationFilter 然后构建一个没有认证的 UsernamePasswordAuthenticationToken
- 2.随后交给 AuthenticationManager 进行验证,
- 3.AuthenticationManager 找到对应的 AuthenticationProvider进行认证
- 4.AuthenticationProvider找到上下文中的UserDetailsService 中寻找用户然后对比
- 5.验证成功返回 Authentication 放入 SecurityContextHolder中
这里需要自定义一个token,filter和provider
定义token
参考UsernamePasswordAuthenticationToken, 其中principal是用户名, credentials是密码,
由于这里只有手机号, 不需要密码, 所以, 将其中有关credentials的去除, 复制下来即可
自定义SmsCodeAuthenticationToken如下
package com.etouch.security.security.smslogin;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import java.util.Collection;
/*
*这一步的作用是为了替换原有系统的 UsernamePasswordAuthenticationToken 用来做验证
*
* 代码都是从UsernamePasswordAuthenticationToken 里粘贴出来的
*
*/
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
/**
* 在 UsernamePasswordAuthenticationToken 中该字段代表登录的用户名,
* 在这里就代表登录的手机号码
*/
private final Object principal;
/**
* 构建一个没有鉴权的 SmsCodeAuthenticationToken
*/
public SmsCodeAuthenticationToken(Object principal) {
super(null);
this.principal = principal;
setAuthenticated(false);
}
/**
* 构建拥有鉴权的 SmsCodeAuthenticationToken
*/
public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
super.setAuthenticated(true); // must use super, as we override
}
// ~ Methods
// 剩下的方法不用动就行了 就是从 UsernamePasswordAuthenticationToken 里粘贴出来的
// ========================================================================================================
public Object getCredentials() {
return null;
}
public Object getPrincipal() {
return this.principal;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
}
定义过滤器filter
同样参照UsernamePasswordAuthenticationFilter,将和password有关的去除, 略微修改以下, 即可完成
自定义的SmsCodeAuthenticationFilter代码如下
package com.etouch.security.security.smslogin;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationServiceException;
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.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 短信登录的鉴权过滤器,模仿 UsernamePasswordAuthenticationFilter 实现
*/
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
/**
* form表单中手机号码的字段name
*/
public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "phone";
private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;
/**
* 是否仅 POST 方式
*/
private boolean postOnly = true;
public SmsCodeAuthenticationFilter() {
// 短信登录的请求 post 方式的 /sms/login
super(new AntPathRequestMatcher("/sys/login/phone", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
// 电话号码
String mobile = obtainUsername(request);
if (StringUtils.isEmpty(mobile)) {
throw new AuthenticationServiceException("电话号码不能为空");
}
return this.getAuthenticationManager().authenticate(new SmsCodeAuthenticationToken(mobile));
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(mobileParameter);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(mobileParameter);
}
}
接下来是自定义手机号验证码登陆鉴证器provider
代码如下
package com.etouch.security.security.smslogin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.