自定義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;
}