Commit f8249b11 authored by 向怀芳's avatar 向怀芳 🎱

1. 新增账号锁定功能

parent 9dde27a6
......@@ -2,8 +2,8 @@
package cn.sh.stc.sict.cloud.auth.handler;
import cn.sh.stc.sict.cloud.common.core.constant.RedisCacheConstant;
import cn.sh.stc.sict.cloud.common.security.config.UserAccountProperties;
import cn.sh.stc.sict.cloud.common.security.handler.AuthenticationFailureHandler;
import cn.sh.stc.sict.cloud.upms.feign.RemoteUserService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
......@@ -28,8 +28,7 @@ import java.util.concurrent.TimeUnit;
public class SictAuthenticationFailureLockEventHandler implements AuthenticationFailureHandler {
private final RedisTemplate<String, String> redisTemplate;
private final RemoteUserService remoteUserService;
private final UserAccountProperties userAccountProperties;
/**
* 密码错误超过 5 此自动锁定
......@@ -45,16 +44,14 @@ public class SictAuthenticationFailureLockEventHandler implements Authentication
public void handle(AuthenticationException authenticationException, Authentication authentication,
HttpServletRequest request, HttpServletResponse response) {
// 只处理账号密码错误异常
if (!(authenticationException instanceof BadCredentialsException)) {
if (!(authenticationException instanceof BadCredentialsException) && userAccountProperties.getLimitFlag()) {
return;
}
String username = authentication.getName();
String key = String.format("%s:%s", RedisCacheConstant.LOGIN_ERROR_TIMES, username);
Long deltaTimes = 5L;
Long times = redisTemplate.opsForValue().increment(key);
// 自动过期时间
Long deltaTime = 30L;
redisTemplate.expire(key, deltaTime, TimeUnit.MINUTES);
redisTemplate.expire(key, userAccountProperties.getLimitTime(), TimeUnit.MINUTES);
}
}
package cn.sh.stc.sict.cloud.common.core.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import java.math.BigDecimal;
import java.math.BigInteger;
......@@ -118,4 +119,7 @@ public class NumberUtil {
}
public static Long getLongValue(String value) {
return StrUtil.isBlank(value) ? 0L : Long.parseLong(value);
}
}
package cn.sh.stc.sict.cloud.common.security.config;
import cn.hutool.core.util.ObjectUtil;
import cn.sh.stc.sict.cloud.common.core.util.NumberUtil;
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
/**
* 用户账号 配置属性
*/
@Data
@Component
@RefreshScope
@ConditionalOnExpression("!'${user}'.isEmpty()")
@ConfigurationProperties(prefix = "user.account")
public class UserAccountProperties {
private Boolean limitFlag;
private Long limitCount;
private Long limitTime;
public Boolean getLimitFlag() {
return !ObjectUtil.isNull(this.limitFlag) && this.limitFlag;
}
public Long getLimitCount(){
return NumberUtil.isNullOrZero(this.limitCount) ? 5L : this.limitCount;
}
public Long getLimitTime() {
return NumberUtil.isNullOrZero(this.limitTime) ? 30L : this.limitTime;
}
}
......@@ -58,7 +58,17 @@ public class SictUser extends User {
* @throws IllegalArgumentException if a <code>null</code> value was passed either as
* a parameter or as an element in the <code>GrantedAuthority</code> collection
*/
public SictUser(Long id, String phone, String openId, String appId, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
public SictUser(Long id,
String phone,
String openId,
String appId,
String username,
String password,
boolean enabled,
boolean accountNonExpired,
boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.id = id;
this.phone = phone;
......
......@@ -5,7 +5,9 @@ import cn.hutool.core.util.StrUtil;
import cn.sh.stc.sict.cloud.common.core.constant.Constant;
import cn.sh.stc.sict.cloud.common.core.constant.RedisCacheConstant;
import cn.sh.stc.sict.cloud.common.core.constant.SecurityConstants;
import cn.sh.stc.sict.cloud.common.core.util.NumberUtil;
import cn.sh.stc.sict.cloud.common.core.util.R;
import cn.sh.stc.sict.cloud.common.security.config.UserAccountProperties;
import cn.sh.stc.sict.cloud.common.security.util.AuthUtils;
import cn.sh.stc.sict.cloud.upms.dto.UserInfo;
import cn.sh.stc.sict.cloud.upms.feign.RemoteUserService;
......@@ -16,6 +18,7 @@ import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
......@@ -26,6 +29,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.security.auth.login.AccountLockedException;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
......@@ -42,9 +46,12 @@ public class SictUserDetailsServiceImpl implements SictUserDetailsService {
private static final String BASIC_ = "Basic ";
private final RemoteUserService remoteUserService;
private final CacheManager cacheManager;
private final RedisTemplate<String, String> redisTemplate;
private final UserAccountProperties userAccountProperties;
/**
* 用户密码登录
* .
*
* @param username 用户名
* @return
......@@ -54,6 +61,11 @@ public class SictUserDetailsServiceImpl implements SictUserDetailsService {
@SneakyThrows
public UserDetails loadUserByUsername(String username) {
String key = username;
String lockedKey = String.format("%s:%s", RedisCacheConstant.LOGIN_ERROR_TIMES, username);
Long times = NumberUtil.getLongValue(redisTemplate.opsForValue().get(lockedKey));
if (userAccountProperties.getLimitFlag() && times >= userAccountProperties.getLimitCount()) {
throw new AccountLockedException("账号被锁定");
}
Cache cache = cacheManager.getCache(RedisCacheConstant.USER_DETAILS);
if (!StrUtil.containsAny(key, StringPool.COLON)) {
key = key + StringPool.COLON + getAppId();
......@@ -119,8 +131,11 @@ public class SictUserDetailsServiceImpl implements SictUserDetailsService {
}
String userName = user.getUserName() + StringPool.COLON + info.getAppId();
return new SictUser(user.getId(), user.getPhone(), info.getOpenId(), info.getAppId(), userName, SecurityConstants.BCRYPT + user.getPasswd(), enabled,
true, true, !Constant.BYTE_YES.equals(user.getStatus()), authorities);
String password = SecurityConstants.BCRYPT + user.getPasswd();
// 登录失败5次锁定半小时
boolean nonLockd = !Constant.BYTE_YES.equals(user.getStatus());
return new SictUser(user.getId(), user.getPhone(), info.getOpenId(), info.getAppId(), userName, password, enabled,
true, true, nonLockd, authorities);
}
private static String getAppId() {
......
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.SictHystrixFeignTargeterConfiguration,\
cn.sh.stc.sict.cloud.common.security.feign.SictFeignConfiguration,\
cn.sh.stc.sict.cloud.common.security.config.UserAccountProperties,\
cn.sh.stc.sict.cloud.common.security.service.SictUserDetailsServiceImpl,\
cn.sh.stc.sict.cloud.common.security.component.SictWebResponseExceptionTranslator,\
cn.sh.stc.sict.cloud.common.security.component.ResourceAuthExceptionEntryPoint,\
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment