自定义token,继承 AbstractAuthenticationToken
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* 手机短信token验证
*
* @author ming
* @version 1.0.0
* @date 2021/3/1 15:17
**/
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 530L;
private final Object principal;
private Object credentials;
public SmsAuthenticationToken(Object principal, Object credentials) {
super((Collection) null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public SmsAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
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");
} else {
super.setAuthenticated(false);
}
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
自定义拦截类Filter,继承AbstractAuthenticationProcessingFilter
import com.base.web.config.security.authentication.SmsAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 自定义手机号、短信验证码登录过滤器
*
* @author ming
* @version 1.0.0
* @date 2021/3/1 15:17
**/
public class JwtSmsLoginFilter extends AbstractAuthenticationProcessingFilter {
public JwtSmsLoginFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
super(requiresAuthenticationRequestMatcher);
}
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String phoneNo = request.getParameter("phoneNo");
String code = request.getParameter("code");
if (phoneNo == null) {
phoneNo = "";
}
if (code == null) {
code = "";
}
SmsAuthenticationToken token = new SmsAuthenticationToken(phoneNo, code);
//token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));/
token.setDetails(this.authenticationDetailsSource.buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(token);
return super.getAuthenticationManager().authenticate(token);
}
}
实现登录验证逻辑
import cn.hutool.core.util.ObjectUtil;
import com.base.common.constant.WebConstant;
import com.base.redis.utils.RedisCacheUtil;
import com.base.web.config.security.authentication.SmsAuthenticationToken;
import com.base.web.service.AccountService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collection;
/**
* 用户自定义身份认证, 短信验证码模式
*
* @author ming
* @date : 2019/7/2 17:17
* @since : 1.0
*/
@Component
public class SmsAuthenticationProvider implements AuthenticationProvider {
@Resource
private AccountService accountService;
@Resource
private RedisCacheUtil redisCacheUtil;
/**
* 认证处理,返回一个Authentication的实现类则代表认证成功,返回null则代表认证失败
*
* @date 2019/7/5 15:19
* @since 1.0
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String phoneNo = authentication.getName();
String code = (String) authentication.getCredentials();
if (StringUtils.isBlank(phoneNo)) {
throw new UsernameNotFoundException("手机号不可以为空");
}
if (StringUtils.isBlank(code)) {
throw new BadCredentialsException("验证码不可以为空");
}
//验证短信验证码
String key = phoneNo + WebConstant.REDIS_KEY_SUFFIX_FOR_VERIFICATION_CODE;
boolean hasCode = redisCacheUtil.hasKey(key);
if (!hasCode) {
throw new BadCredentialsException("短信验证码已失效请重新获取");
}
String smsCode = redisCacheUtil.getString(key);
//比较前端传入的明文和数据库中加密的密文是否相等
if (!smsCode.equalsIgnoreCase(code)) {
throw new BadCredentialsException("验证码错误");
}
redisCacheUtil.delete(key);
//获取用户信息
UserDetails user = accountService.loadUserByUsername(phoneNo);
if (ObjectUtil.isEmpty(user)) {
throw new BadCredentialsException("用户信息加载失败");
}
//获取用户权限信息
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, null, authorities);
}
/**
* 如果该AuthenticationProvider支持传入的Authentication对象,则返回true
*
* @date 2019/7/5 15:19
* @since 1.0
*/
@Override
public boolean supports(Class<?> aClass) {
return SmsAuthenticationToken.class.isAssignableFrom(aClass);
}
}
security配置
// 添加过滤器
http.addFilterBefore(smsLoginFilter(), AbstractPreAuthenticatedProcessingFilter.class);
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService)
.passwordEncoder(passwordEncoder())
.and()
.authenticationProvider(smsAuthenticationProvider)
.authenticationProvider(passwordAuthenticationProvider);
}
/**
* 登录认证器 --- 手机号、短信登录
*/
@Bean
public AbstractAuthenticationProcessingFilter smsLoginFilter() throws Exception {
JwtSmsLoginFilter jwtSmsLoginFilter = new JwtSmsLoginFilter(new AntPathRequestMatcher("/account/login", "POST"));
jwtSmsLoginFilter.setAuthenticationManager(this.authenticationManager());
jwtSmsLoginFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
jwtSmsLoginFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
return jwtSmsLoginFilter;
}