| New file |
| | |
| | | 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)); |
| | | } |
| | | } |