package cn.gistack.auth.config;
|
|
import cn.gistack.auth.utils.TokenUtil;
|
import cn.gistack.common.cache.CacheNames;
|
import com.alibaba.nacos.common.utils.StringUtils;
|
import lombok.SneakyThrows;
|
import org.springblade.core.redis.cache.BladeRedis;
|
import org.springblade.core.tool.utils.DigestUtil;
|
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.WebUtil;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
|
import org.springframework.stereotype.Component;
|
import javax.servlet.http.HttpServletRequest;
|
import java.time.Duration;
|
|
/**
|
* 自定义安全认证
|
* @author zhongrj
|
* @time 2022-9-2
|
*/
|
@Component
|
public class MyAuthenticationProvider extends DaoAuthenticationProvider {
|
|
/**
|
* 免密常量
|
*/
|
public static final String CUSTOM_LOGIN_SMS = "CUSTOM_LOGIN_SMS";
|
|
@Autowired
|
private BladeRedis bladeRedis;
|
|
/**
|
* 设置 userDetailsService
|
* @param userDetailsService
|
*/
|
public MyAuthenticationProvider(UserDetailsService userDetailsService) {
|
super();
|
setUserDetailsService(userDetailsService);
|
}
|
|
/**
|
* 自定义处理密码校验逻辑
|
* @param userDetails 用户信息
|
* @param authentication 认证对象信息
|
* @throws AuthenticationException 认证异常对象信息
|
*/
|
@SneakyThrows
|
@Override
|
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
if (authentication.getCredentials() == null) {
|
this.logger.debug("Authentication failed: no credentials provided");
|
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
} else {
|
String password = authentication.getCredentials().toString();
|
// 手机号登录时不验证密码
|
if(!CUSTOM_LOGIN_SMS.equals(password)){
|
HttpServletRequest request = WebUtil.getRequest();
|
String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
|
// 获取租户ID
|
String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
|
String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
|
// 指定租户ID
|
String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
|
String detailsPassword = userDetails.getPassword().substring(7);
|
if (!detailsPassword.equals(DigestUtil.hex(password))) {
|
int count = getFailCount(tenantId, userDetails.getUsername());
|
// 用户存在但密码错误,超过次数则锁定账号
|
if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY)) {
|
setFailCount(tenantId, userDetails.getUsername(), count);
|
throw new UserDeniedAuthorizationException(TokenUtil.USER_NOT_FOUND);
|
}
|
this.logger.debug("Failed to authenticate since password does not match stored value");
|
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
}
|
// 成功则清除登录错误次数
|
delFailCount(tenantId, userDetails.getUsername());
|
}
|
}
|
}
|
|
/**
|
* 获取账号错误次数
|
*
|
* @param tenantId 租户id
|
* @param username 账号
|
* @return int
|
*/
|
private int getFailCount(String tenantId, String username) {
|
return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
|
}
|
|
/**
|
* 设置账号错误次数
|
*
|
* @param tenantId 租户id
|
* @param username 账号
|
* @param count 次数
|
*/
|
private void setFailCount(String tenantId, String username, int count) {
|
bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
|
}
|
|
/**
|
* 清空账号错误次数
|
*
|
* @param tenantId 租户id
|
* @param username 账号
|
*/
|
private void delFailCount(String tenantId, String username) {
|
bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
|
}
|
}
|