custauth
This commit is contained in:
parent
18b184bdac
commit
df46fd869f
|
|
@ -0,0 +1,136 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus</artifactId>
|
||||
<version>24.12.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>bonus-cust-auth</artifactId>
|
||||
|
||||
<description>
|
||||
bonus-cust-auth认证授权中心
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos Config -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Sentinel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- bonus Common Security-->
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-security</artifactId>
|
||||
</dependency>
|
||||
<!--加密依赖包-->
|
||||
<dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>${jasypt-spring-boot-starter.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-log</artifactId>
|
||||
</dependency>
|
||||
<!-- Swagger UI -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${swagger.fox.version}</version>
|
||||
</dependency>
|
||||
<!-- bonus Common Swagger -->
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-swagger</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.hankcs</groupId>
|
||||
<artifactId>hanlp</artifactId>
|
||||
<version>portable-1.7.8</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-config</artifactId>
|
||||
<version>24.12.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.2.23</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>bns-releases</id>
|
||||
<url>http://192.168.0.56:8081/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>bns-snapshots</id>
|
||||
<url>http://192.168.0.56:8081/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.bonus.auth;
|
||||
|
||||
import com.bonus.common.swagger.annotation.EnableCustomSwagger2;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import com.bonus.common.security.annotation.EnableRyFeignClients;
|
||||
|
||||
/**
|
||||
* 认证授权中心
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@EnableCustomSwagger2
|
||||
@EnableRyFeignClients
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
|
||||
public class BonusCustAuthApplication
|
||||
{
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SpringApplication.run(BonusCustAuthApplication.class, args);
|
||||
System.out.println("认证授权中心启动成功");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.bonus.auth.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
public enum LoginType {
|
||||
/**
|
||||
* 账号密码
|
||||
*/
|
||||
USERNAME_PASSWORD,
|
||||
/**
|
||||
* 手机号密码
|
||||
*/
|
||||
PHONE_PASSWORD,
|
||||
/**
|
||||
* 邮箱密码
|
||||
*/
|
||||
EMAIL_PASSWORD,
|
||||
/**
|
||||
* 手机号验证码
|
||||
*/
|
||||
PHONE_OTP,
|
||||
/**
|
||||
* 邮箱验证码
|
||||
*/
|
||||
EMAIL_OTP;
|
||||
|
||||
@JsonCreator
|
||||
public static LoginType fromString(String key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 自定义转换逻辑,允许大小写不敏感的匹配
|
||||
for (LoginType type : LoginType.values()) {
|
||||
if (type.name().equalsIgnoreCase(key)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
// throw new ServiceException("不支持的登录方式");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.bonus.auth.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 该类用于从 `application.yml` 中加载密码策略的配置项。
|
||||
* 使用 @ConfigurationProperties 注解,前缀为 password-policy。
|
||||
* @author bonus
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "password-policy")
|
||||
@Data
|
||||
public class PasswordPolicyConfig {
|
||||
|
||||
// 密码的最小长度
|
||||
private int minLength;
|
||||
|
||||
// 密码的最大长度
|
||||
private int maxLength;
|
||||
|
||||
// 是否需要包含大写字母
|
||||
private boolean requireUpperCase;
|
||||
|
||||
// 是否需要包含小写字母
|
||||
private boolean requireLowerCase;
|
||||
|
||||
// 是否需要包含数字
|
||||
private boolean requireDigit;
|
||||
|
||||
// 是否需要包含特殊字符
|
||||
private boolean requireSpecialChar;
|
||||
|
||||
// 常见的弱密码列表,禁止使用这些密码
|
||||
private List<String> weakPasswords;
|
||||
|
||||
// 密码历史记录限制
|
||||
private int passwordHistoryLimit;
|
||||
|
||||
// 是否限制连续相同字符
|
||||
private boolean restrictConsecutiveChars;
|
||||
|
||||
// 最大允许的连续字符数
|
||||
private int maxConsecutiveChars;
|
||||
|
||||
// 密码中是否不能包含用户名
|
||||
private boolean excludeUsernameInPassword;
|
||||
|
||||
// 是否在首次登录时强制修改密码
|
||||
private boolean forcePasswordChangeOnFirstLogin;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.bonus.auth.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
|
||||
public enum VerificationCodeType {
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
LOGIN,
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
REGISTER;
|
||||
|
||||
@JsonCreator
|
||||
public static VerificationCodeType fromString(String key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 自定义转换逻辑,允许大小写不敏感的匹配
|
||||
for (VerificationCodeType type : VerificationCodeType.values()) {
|
||||
if (type.name().equalsIgnoreCase(key)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
// throw new ServiceException("不支持的登录方式");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.bonus.auth.controller;
|
||||
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author bonus 系统配置层
|
||||
*/
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RefreshScope
|
||||
public class ConfigController {
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@GetMapping("getConfig")
|
||||
public R<Object> getConfig() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("loginConfig", systemConfig.getLoginConfig());
|
||||
map.put("registersConfig", systemConfig.getRegistersConfig());
|
||||
map.put("isAdmin", systemConfig.isAdmin());
|
||||
map.put("webSocketurl", systemConfig.getWebsocketurl());
|
||||
map.put("isAddRootCompany", systemConfig.isAddRootCompany());
|
||||
map.put("requestConfig", systemConfig.getRequestConfig());
|
||||
map.put("passwordConfig", systemConfig.getPasswordConfig());
|
||||
map.put("addAddress", systemConfig.isAddAddress());
|
||||
return R.ok(map);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
package com.bonus.auth.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.bonus.auth.config.LoginType;
|
||||
import com.bonus.auth.factory.LoginStrategyFactory;
|
||||
import com.bonus.auth.form.LoginBody;
|
||||
import com.bonus.auth.form.RegisterBody;
|
||||
import com.bonus.auth.service.*;
|
||||
import com.bonus.common.core.constant.CacheConstants;
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.utils.JwtUtils;
|
||||
import com.bonus.common.core.utils.StringUtils;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.redis.service.RedisService;
|
||||
import com.bonus.common.security.auth.AuthUtil;
|
||||
import com.bonus.common.security.service.TokenService;
|
||||
import com.bonus.common.security.utils.SecurityUtils;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.RemoteConfigService;
|
||||
import com.bonus.system.api.RemoteLogService;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Token 控制器
|
||||
* 处理登录、获取验证码、登出、刷新令牌和注册功能
|
||||
*/
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class TokenController {
|
||||
|
||||
@Resource
|
||||
private SystemConfig config;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
@Autowired
|
||||
private SysLoginService sysLoginService;
|
||||
|
||||
@Autowired
|
||||
private LoginStrategyFactory loginStrategyFactory;
|
||||
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Autowired
|
||||
private SysPasswordService passwordService;
|
||||
|
||||
@Autowired
|
||||
private PasswordValidatorService passwordValidatorService;
|
||||
|
||||
@Autowired
|
||||
private SysRecordLogService logService;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
@Resource
|
||||
private RemoteConfigService configService;
|
||||
|
||||
@PostMapping("isAdmin")
|
||||
public R<?> isAdmin(@RequestBody LoginBody form) {
|
||||
if (!config.isAdmin()) {
|
||||
return R.ok(false);
|
||||
}
|
||||
passwordValidatorService.validateLoginParameters(form.getUsername(), form.getPassword());
|
||||
//通过用户名获取人员信息
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfo(form.getUsername(), SecurityConstants.INNER);
|
||||
// 验证用户查询结果
|
||||
passwordValidatorService.validateUserResult(form.getUsername(), userResult);
|
||||
LoginUser userInfo = userResult.getData();
|
||||
SysUser user = userInfo.getSysUser();
|
||||
passwordValidatorService.validateApprovalStatus(form.getUsername(), user);
|
||||
// 验证用户状态
|
||||
passwordValidatorService.validateUserStatus(form.getUsername(), user);
|
||||
// 验证密码
|
||||
passwordService.validate(user, form.getPassword(), System.currentTimeMillis());
|
||||
// 处理IP校验
|
||||
passwordValidatorService.handleIpValidation(form.getUsername(), user);
|
||||
if (userResult.getData() == null || R.FAIL == userResult.getCode()) {
|
||||
return R.fail("登录用户不存在");
|
||||
}
|
||||
Set<String> roles = userResult.getData().getRoles();
|
||||
return R.ok(roles.contains("admin"));
|
||||
}
|
||||
|
||||
@PostMapping("isLogin")
|
||||
public R<?> isLogin(@RequestBody LoginBody form) {
|
||||
LoginStrategy strategy = loginStrategyFactory.getStrategy(form.getLoginType());
|
||||
if (strategy == null) {
|
||||
return R.fail("不支持的登录方式");
|
||||
}
|
||||
if (form.getLoginType()== LoginType.EMAIL_OTP || form.getLoginType()== LoginType.PHONE_OTP ){
|
||||
form.setPassword(form.getVerificationCode());
|
||||
}
|
||||
LoginUser login = strategy.login(form.getUsername(), form.getPassword());
|
||||
return R.ok(tokenService.isLogin(String.valueOf(login.getSysUser().getUserId())));
|
||||
}
|
||||
|
||||
@PostMapping("login")
|
||||
public R<?> login(@RequestBody LoginBody form) {
|
||||
// 获取相应的登录策略
|
||||
LoginStrategy strategy = loginStrategyFactory.getStrategy(form.getLoginType());
|
||||
if (strategy == null) {
|
||||
return R.fail("不支持的登录方式");
|
||||
}
|
||||
if (form.getLoginType()== LoginType.EMAIL_OTP || form.getLoginType()== LoginType.PHONE_OTP ){
|
||||
form.setPassword(form.getVerificationCode());
|
||||
}
|
||||
|
||||
/**对系统并发数进行判断*/
|
||||
long concurrency = 100;
|
||||
AjaxResult result = configService.getConfigKey("sys.backend.concurrency");
|
||||
if (result.isSuccess())
|
||||
{
|
||||
concurrency = Long.parseLong(result.get("msg").toString());
|
||||
}
|
||||
Collection<String> keys = redisService.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
|
||||
if (keys.size() >= concurrency){
|
||||
return R.fail("当前系统用户并发数超过系统配置,请稍后再试");
|
||||
}
|
||||
|
||||
LoginUser login = strategy.login(form.getUsername(), form.getPassword());
|
||||
logService.saveLogin(form.getUsername(), "登录", "登录成功", null, "成功");
|
||||
return R.ok(tokenService.createToken(login));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机验证码
|
||||
*
|
||||
* @param form 登录表单
|
||||
* @return 验证码发送结果
|
||||
*/
|
||||
@PostMapping("getPhoneCode")
|
||||
public R<?> getPhoneCode(@RequestBody LoginBody form) {
|
||||
return sysLoginService.getPhoneCode(form.getUsername(), form.getVerificationCodeType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*
|
||||
* @param request HTTP 请求
|
||||
* @return 登出结果
|
||||
*/
|
||||
@PostMapping("logout")
|
||||
public R<?> logout(HttpServletRequest request) {
|
||||
try {
|
||||
String token = SecurityUtils.getToken(request);
|
||||
if (StringUtils.isNotEmpty(token)) {
|
||||
String key = JwtUtils.getUserKey(token);
|
||||
boolean key1 = tokenService.isKey(key);
|
||||
if (key1) {
|
||||
String username = JwtUtils.getUserName(token);
|
||||
String userId = JwtUtils.getUserId(token);
|
||||
AuthUtil.logoutByToken(token);
|
||||
tokenService.delExistingToken(Long.valueOf(userId));
|
||||
sysLoginService.logout(username, userId);
|
||||
logService.saveLogout(username, "退出登录", "退出成功", userId, "成功");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("登出失败: {}", e.getMessage(), e);
|
||||
}
|
||||
sysLoginService.logout("", "");
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*
|
||||
* @param request HTTP 请求
|
||||
* @return 刷新结果
|
||||
*/
|
||||
@PostMapping("refresh")
|
||||
public R<?> refresh(HttpServletRequest request) {
|
||||
try {
|
||||
LoginUser loginUser = tokenService.getLoginUser(request);
|
||||
if (StringUtils.isNotNull(loginUser)) {
|
||||
tokenService.refreshToken(loginUser);
|
||||
return R.ok();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("刷新令牌失败: {}", e.getMessage(), e);
|
||||
}
|
||||
return R.fail("刷新令牌失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param registerBody 注册表单
|
||||
* @return 注册结果
|
||||
*/
|
||||
@PostMapping("register")
|
||||
public R<?> register(@RequestBody RegisterBody registerBody) {
|
||||
sysLoginService.register(registerBody);
|
||||
logService.saveRegister(registerBody.getUsername(), "注册", "注册成功", null, "成功");
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.bonus.auth.factory;
|
||||
|
||||
import com.bonus.auth.config.LoginType;
|
||||
import com.bonus.auth.service.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Component
|
||||
public class LoginStrategyFactory {
|
||||
private final Map<LoginType, LoginStrategy> strategyMap;
|
||||
|
||||
@Autowired
|
||||
public LoginStrategyFactory(List<LoginStrategy> strategies) {
|
||||
strategyMap = new HashMap<>();
|
||||
// 通过类型查找对应的策略
|
||||
strategies.forEach(strategy -> {
|
||||
if (strategy instanceof UsernamePasswordLoginStrategy) {
|
||||
strategyMap.put(LoginType.USERNAME_PASSWORD, strategy);
|
||||
} else if (strategy instanceof PhonePasswordLoginStrategy) {
|
||||
strategyMap.put(LoginType.PHONE_PASSWORD, strategy);
|
||||
} else if (strategy instanceof PhoneOtpLoginStrategy) {
|
||||
strategyMap.put(LoginType.PHONE_OTP, strategy);
|
||||
} else if (strategy instanceof EmailPasswordLoginStrategy) {
|
||||
strategyMap.put(LoginType.EMAIL_PASSWORD, strategy);
|
||||
} else if (strategy instanceof EmailOtpLoginStrategy) {
|
||||
strategyMap.put(LoginType.EMAIL_OTP, strategy);
|
||||
}
|
||||
// 继续添加其他策略
|
||||
});
|
||||
}
|
||||
|
||||
public LoginStrategy getStrategy(LoginType loginType) {
|
||||
return strategyMap.get(loginType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.bonus.auth.factory;
|
||||
|
||||
import com.bonus.auth.config.VerificationCodeType;
|
||||
import com.bonus.auth.service.LoginVerificationCodeSender;
|
||||
import com.bonus.auth.service.RegisterVerificationCodeSender;
|
||||
import com.bonus.auth.service.VerificationCodeStrategy;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Component
|
||||
public class VerificationCodeStrategyFactory {
|
||||
private final Map<VerificationCodeType, VerificationCodeStrategy> strategyMap;
|
||||
|
||||
@Autowired
|
||||
public VerificationCodeStrategyFactory(List<VerificationCodeStrategy> strategies) {
|
||||
strategyMap = new HashMap<>();
|
||||
// 通过类型查找对应的策略
|
||||
strategies.forEach(strategy -> {
|
||||
if (strategy instanceof LoginVerificationCodeSender) {
|
||||
strategyMap.put(VerificationCodeType.LOGIN, strategy);
|
||||
} else if (strategy instanceof RegisterVerificationCodeSender) {
|
||||
strategyMap.put(VerificationCodeType.REGISTER, strategy);
|
||||
}
|
||||
// 继续添加其他策略
|
||||
});
|
||||
}
|
||||
|
||||
public VerificationCodeStrategy getStrategy(VerificationCodeType verificationCodeType) {
|
||||
return strategyMap.get(verificationCodeType);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.bonus.auth.form;
|
||||
|
||||
import com.bonus.auth.config.LoginType;
|
||||
import com.bonus.auth.config.VerificationCodeType;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户登录对象
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Data
|
||||
public class LoginBody {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
|
||||
private String nickName;
|
||||
|
||||
private String email;
|
||||
|
||||
private String phone;
|
||||
private String mobile;
|
||||
private String verificationCode;
|
||||
|
||||
private String code;
|
||||
|
||||
private LoginType loginType;
|
||||
|
||||
private VerificationCodeType verificationCodeType;
|
||||
|
||||
private String mobileCodeType;
|
||||
|
||||
/**
|
||||
* i皖送使用的ticket
|
||||
*/
|
||||
private String ticket;
|
||||
|
||||
/**
|
||||
* i皖送登录方式 0:web端登录 1:H5登录
|
||||
*/
|
||||
private String sysType;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.bonus.auth.form;
|
||||
|
||||
public enum LoginType {
|
||||
PASSWORD, MOBILE
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.bonus.auth.form;
|
||||
|
||||
/**
|
||||
* 用户注册对象
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
public class RegisterBody extends LoginBody {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.utils.encryption.Sm4Utils;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class EmailOtpLoginStrategy implements LoginStrategy {
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Resource
|
||||
private PasswordValidatorService passwordValidatorService;
|
||||
|
||||
@Override
|
||||
public LoginUser login(String email, String otp) {
|
||||
if (!systemConfig.getLoginConfig().isPhoneCode()) {
|
||||
throw new ServiceException("用户不存在/密码错误");
|
||||
}
|
||||
passwordValidatorService.checkPhoneCaptcha(email, otp);
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfoByEmail(email, SecurityConstants.INNER);
|
||||
//验证用户是否存在
|
||||
passwordValidatorService.validateUserResult(email, userResult);
|
||||
LoginUser userInfo = userResult.getData();
|
||||
SysUser user = userInfo.getSysUser();
|
||||
passwordValidatorService.validateApprovalStatus(email, user);
|
||||
// 验证用户状态
|
||||
passwordValidatorService.validateUserStatus(email, user);
|
||||
passwordValidatorService.processLoginBlackList(user);
|
||||
//返回信息
|
||||
return userInfo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.utils.encryption.Sm4Utils;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class EmailPasswordLoginStrategy implements LoginStrategy {
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Resource
|
||||
private PasswordValidatorService passwordValidatorService;
|
||||
@Resource
|
||||
private SysPasswordService passwordService;
|
||||
|
||||
@Override
|
||||
public LoginUser login(String email, String password) {
|
||||
if (!systemConfig.getLoginConfig().isEmailPassword()) {
|
||||
throw new ServiceException("用户不存在/密码错误");
|
||||
}
|
||||
//通过手机号获取用户信息
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfoByEmail(email, SecurityConstants.INNER);
|
||||
//验证用户是否存在
|
||||
passwordValidatorService.validateUserResult(email, userResult);
|
||||
//获取用户信息
|
||||
LoginUser userInfo = userResult.getData();
|
||||
SysUser user = userInfo.getSysUser();
|
||||
//校验用户审批状态
|
||||
passwordValidatorService.validateApprovalStatus(user.getUserName(), user);
|
||||
// 处理IP校验
|
||||
passwordValidatorService.validateIpBlacklist(user.getUserName());
|
||||
// 验证密码
|
||||
passwordService.validate(user, password, System.currentTimeMillis());
|
||||
//校验用户启用状态
|
||||
passwordValidatorService.validateUserStatus(user.getUserName(), user);
|
||||
passwordValidatorService.processLoginBlackList(user);
|
||||
//返回信息
|
||||
return userInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
public interface LoginStrategy {
|
||||
/**
|
||||
* 登录方法
|
||||
* @param identifier 用户的标识符(用户名、手机号、邮箱)
|
||||
* @param credential 用户凭据(密码或验证码)
|
||||
* @return 登录结果
|
||||
*/
|
||||
LoginUser login(String identifier, String credential);
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.security.service.EmailService;
|
||||
import com.bonus.common.security.service.SmsService;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 验证码发送服务
|
||||
* 可发送到邮箱或手机
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class LoginVerificationCodeSender implements VerificationCodeStrategy {
|
||||
|
||||
private static final String EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
|
||||
private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";
|
||||
|
||||
@Resource
|
||||
private EmailService emailService;
|
||||
|
||||
@Resource
|
||||
private SmsService smsService;
|
||||
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
/**
|
||||
* 发送验证码到邮箱或手机
|
||||
*
|
||||
* @param contactInfo 可以是邮箱地址或手机号码
|
||||
* @return 验证码发送的结果
|
||||
*/
|
||||
@Override
|
||||
public void sendVerificationCode(String contactInfo) {
|
||||
if (isEmail(contactInfo)) {
|
||||
emailService.sendSimpleEmail(contactInfo);
|
||||
} else if (isPhone(contactInfo)) {
|
||||
smsService.sendSimplePhone(contactInfo);
|
||||
} else {
|
||||
handleUsernameLogin(contactInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是邮箱
|
||||
*
|
||||
* @param contactInfo 输入信息
|
||||
* @return 是否为邮箱
|
||||
*/
|
||||
private boolean isEmail(String contactInfo) {
|
||||
return contactInfo.matches(EMAIL_REGEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是手机号
|
||||
*
|
||||
* @param contactInfo 输入信息
|
||||
* @return 是否为手机号
|
||||
*/
|
||||
private boolean isPhone(String contactInfo) {
|
||||
return contactInfo.matches(PHONE_REGEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户名登录逻辑
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 验证码发送的结果
|
||||
*/
|
||||
private void handleUsernameLogin(String username) {
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
|
||||
if (userResult == null || userResult.getData() == null || R.FAIL == userResult.getCode()) {
|
||||
throw new ServiceException("用户名/密码错误");
|
||||
}
|
||||
LoginUser user = userResult.getData();
|
||||
// 如果用户是管理员,则发送手机验证码
|
||||
if (user.getRoles().contains("admin")) {
|
||||
if (StringUtils.isEmpty(user.getSysUser().getPhonenumber())) {
|
||||
throw new ServiceException("此账号未绑定手机号,请先绑定手机号");
|
||||
}
|
||||
smsService.sendSimplePhone(user.getSysUser().getPhonenumber());
|
||||
} else {
|
||||
throw new ServiceException("不支持的登录方式");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,446 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.CacheConstants;
|
||||
import com.bonus.common.core.constant.UserConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.enums.UserStatus;
|
||||
import com.bonus.common.core.exception.CaptchaException;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.text.Convert;
|
||||
import com.bonus.common.core.utils.StringUtils;
|
||||
import com.bonus.common.core.utils.ip.IpUtils;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.redis.service.RedisService;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class PasswordValidatorService {
|
||||
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
|
||||
@Resource
|
||||
private SysRecordLogService recordLogService;
|
||||
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
/**
|
||||
* 对新密码进行校验,返回布尔值表示密码是否符合要求。
|
||||
*
|
||||
* @param username 用户名,不能包含在密码中
|
||||
* @param newPassword 新密码
|
||||
* @return 如果密码符合策略要求,则返回 AjaxResult.success,否则返回错误提示
|
||||
*/
|
||||
public AjaxResult validatePassword(String username, String newPassword) {
|
||||
if (!isPasswordLengthValid(newPassword)) {
|
||||
return AjaxResult.error("密码长度应为" + systemConfig.getPasswordConfig().getMinLength() + "至" + systemConfig.getPasswordConfig().getMaxLength() + "位!");
|
||||
}
|
||||
|
||||
if (!containsRequiredCharacters(newPassword)) {
|
||||
return AjaxResult.error(getCharacterRequirementErrorMessage());
|
||||
}
|
||||
|
||||
if (containsWeakPassword(newPassword)) {
|
||||
return AjaxResult.error("密码包含常见的弱密码片段!");
|
||||
}
|
||||
|
||||
if (containsConsecutiveCharacters(newPassword.toLowerCase(), systemConfig.getPasswordConfig().getMaxConsecutiveChars())) {
|
||||
return AjaxResult.error("密码不能包含超过" + systemConfig.getPasswordConfig().getMaxConsecutiveChars() + "个连续相同字符、连续递增/递减的数字或字母(不区分大小写)");
|
||||
}
|
||||
|
||||
if (newPassword.toLowerCase().contains(username.toLowerCase())) {
|
||||
return AjaxResult.error("密码不能包含账号!");
|
||||
}
|
||||
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密码长度是否符合配置要求
|
||||
*/
|
||||
private boolean isPasswordLengthValid(String password) {
|
||||
return password.length() >= systemConfig.getPasswordConfig().getMinLength() && password.length() <= systemConfig.getPasswordConfig().getMaxLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密码是否包含必需的字符类型
|
||||
*/
|
||||
private boolean containsRequiredCharacters(String password) {
|
||||
boolean hasUpperCase = false, hasLowerCase = false, hasDigit = false, hasSpecialChar = false;
|
||||
|
||||
for (char c : password.toCharArray()) {
|
||||
if (Character.isUpperCase(c)) {
|
||||
hasUpperCase = true;
|
||||
}
|
||||
if (Character.isLowerCase(c)) {
|
||||
hasLowerCase = true;
|
||||
}
|
||||
if (Character.isDigit(c)) {
|
||||
hasDigit = true;
|
||||
}
|
||||
if ("!@#$%^&*()-_=+[{]};:'\",<.>/?".indexOf(c) >= 0) {
|
||||
hasSpecialChar = true;
|
||||
}
|
||||
}
|
||||
|
||||
return (!systemConfig.getPasswordConfig().isRequireUpperCase() || hasUpperCase) &&
|
||||
(!systemConfig.getPasswordConfig().isRequireLowerCase() || hasLowerCase) &&
|
||||
(!systemConfig.getPasswordConfig().isRequireDigit() || hasDigit) &&
|
||||
(!systemConfig.getPasswordConfig().isRequireSpecialChar() || hasSpecialChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置返回密码不符合要求时的错误提示信息
|
||||
*/
|
||||
private String getCharacterRequirementErrorMessage() {
|
||||
if (systemConfig.getPasswordConfig().isRequireUpperCase()) {
|
||||
return "密码必须包含大写字母!";
|
||||
}
|
||||
if (systemConfig.getPasswordConfig().isRequireLowerCase()) {
|
||||
return "密码必须包含小写字母!";
|
||||
}
|
||||
if (systemConfig.getPasswordConfig().isRequireDigit()) {
|
||||
return "密码必须包含数字!";
|
||||
}
|
||||
if (systemConfig.getPasswordConfig().isRequireSpecialChar()) {
|
||||
return "密码必须包含特殊字符!";
|
||||
}
|
||||
return "密码不符合字符要求!";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密码是否包含常见的弱密码
|
||||
*/
|
||||
private boolean containsWeakPassword(String password) {
|
||||
for (String weakPwd : systemConfig.getPasswordConfig().getWeakPasswords()) {
|
||||
if (password.toLowerCase().contains(weakPwd)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密码中是否包含超过 n 个连续相同字符、连续递增/递减的数字或字母(不区分大小写)
|
||||
*/
|
||||
private boolean containsConsecutiveCharacters(String password, int n) {
|
||||
// 检查连续相同字符
|
||||
n = n + 1;
|
||||
for (int i = 0; i <= password.length() - n; i++) {
|
||||
boolean consecutiveSameChar = true;
|
||||
for (int j = 1; j < n; j++) {
|
||||
if (password.charAt(i + j) != password.charAt(i)) {
|
||||
consecutiveSameChar = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consecutiveSameChar) {
|
||||
return true; // 包含超过 n 个连续相同字符
|
||||
}
|
||||
}
|
||||
|
||||
// 检查连续递增或递减的数字
|
||||
for (int i = 0; i <= password.length() - n; i++) {
|
||||
boolean consecutiveIncreasing = true;
|
||||
boolean consecutiveDecreasing = true;
|
||||
for (int j = 1; j < n; j++) {
|
||||
char currentChar = password.charAt(i);
|
||||
char nextChar = password.charAt(i + j);
|
||||
|
||||
// 检查数字递增或递减
|
||||
if (Character.isDigit(currentChar) && Character.isDigit(nextChar)) {
|
||||
if (nextChar != currentChar + j) {
|
||||
consecutiveIncreasing = false;
|
||||
}
|
||||
if (nextChar != currentChar - j) {
|
||||
consecutiveDecreasing = false;
|
||||
}
|
||||
} else {
|
||||
consecutiveIncreasing = false;
|
||||
consecutiveDecreasing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consecutiveIncreasing || consecutiveDecreasing) {
|
||||
return true; // 包含超过 n 个递增或递减的连续数字
|
||||
}
|
||||
}
|
||||
|
||||
// 检查连续递增或递减的字母(不区分大小写)
|
||||
for (int i = 0; i <= password.length() - n; i++) {
|
||||
boolean consecutiveIncreasing = true;
|
||||
boolean consecutiveDecreasing = true;
|
||||
for (int j = 1; j < n; j++) {
|
||||
char currentChar = Character.toLowerCase(password.charAt(i)); // 转为小写
|
||||
char nextChar = Character.toLowerCase(password.charAt(i + j)); // 转为小写
|
||||
|
||||
// 检查字母递增或递减
|
||||
if (Character.isLetter(currentChar) && Character.isLetter(nextChar)) {
|
||||
if (nextChar != currentChar + j) {
|
||||
consecutiveIncreasing = false;
|
||||
}
|
||||
if (nextChar != currentChar - j) {
|
||||
consecutiveDecreasing = false;
|
||||
}
|
||||
} else {
|
||||
consecutiveIncreasing = false;
|
||||
consecutiveDecreasing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consecutiveIncreasing || consecutiveDecreasing) {
|
||||
// 包含超过 n 个递增或递减的连续字母
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 不包含连续相同字符、数字或字母序列
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证登录参数
|
||||
*/
|
||||
public void validateLoginParameters(String username, String password) {
|
||||
if (StringUtils.isAnyBlank(username, password)) {
|
||||
logAndThrowError(username, "用户名/密码必须填写", "用户名/密码为空");
|
||||
}
|
||||
if (!isPasswordLengthValid(password)) {
|
||||
logAndThrowError(username, "密码格式不正确", "密码格式不正确");
|
||||
}
|
||||
if (username.length() < UserConstants.USERNAME_MIN_LENGTH || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
|
||||
logAndThrowError(username, "用户名格式不正确", "用户名格式不正确");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证IP黑名单
|
||||
*/
|
||||
public void validateIpBlacklist(String username) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
|
||||
String ip = IpUtils.getIpAddr();
|
||||
if (IpUtils.isMatchedIp(blackStr,ip )) {
|
||||
logAndThrowError(username, "访问IP已被列入系统黑名单", "访问IP已被列入系统黑名单");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logAndThrowError(username, "IP黑名单校验失败,请稍后重试", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户查询结果
|
||||
*/
|
||||
public void validateUserResult(String username, R<LoginUser> userResult) {
|
||||
if (userResult == null || userResult.getData() == null || R.FAIL == userResult.getCode()) {
|
||||
if (Objects.nonNull(userResult)) {
|
||||
logAndThrowError(username, userResult.getMsg(), userResult.getMsg());
|
||||
}
|
||||
logAndThrowError(username, "用户名/密码错误", "登录用户不存在");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户状态
|
||||
*/
|
||||
public void validateUserStatus(String username, SysUser user) {
|
||||
if (UserStatus.DELETED.getCode().equals(user.getDelFlag()) || UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
||||
logAndThrowError(username, "账号已被删除或停用", "账号已被删除或停用");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理IP校验
|
||||
*/
|
||||
public void handleIpValidation(String username, SysUser user) {
|
||||
try {
|
||||
String nowIp = IpUtils.getIpAddr();
|
||||
String hisIp = redisService.getCacheObject("IP:" + user.getUserId());
|
||||
if (!nowIp.equals(hisIp)) {
|
||||
recordLogService.saveErrorLogs(username, System.currentTimeMillis(), user.getUserId().toString(),"用户连续两次在不同IP登录");
|
||||
}
|
||||
redisService.setCacheObject("IP:" + user.getUserId(), nowIp, 5L, TimeUnit.MINUTES);
|
||||
} catch (Exception e) {
|
||||
logAndThrowError(username, "IP校验失败,请稍后重试", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void processLoginBlackList(SysUser user){
|
||||
// 获取黑名单列表
|
||||
List<Map<String, Object>> cacheList = redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST);
|
||||
|
||||
// 获取客户端的 IP 地址
|
||||
String ip = IpUtils.getIpAddr();
|
||||
|
||||
// 遍历黑名单
|
||||
for (Map<String, Object> map : cacheList) {
|
||||
String ipAddress = (String) map.getOrDefault("ipAddress", null);
|
||||
String ipRangeEnd = (String) map.getOrDefault("ipRangeEnd", null);
|
||||
String ipRangeStart = (String) map.getOrDefault("ipRangeStart", null);
|
||||
String accessStartTime = (String) map.getOrDefault("accessStartTime", null);
|
||||
String accessEndTime = (String) map.getOrDefault("accessEndTime", null);
|
||||
|
||||
// 如果 ipAddress 为空,检查是否在 ip 范围内
|
||||
if (ObjectUtils.isEmpty(ipAddress)) {
|
||||
if (isIpInRange(ip, ipRangeStart, ipRangeEnd)) {
|
||||
boolean result = handleAccessTimeCheck(user, accessStartTime, accessEndTime);
|
||||
if (!result) {
|
||||
throw new ServiceException("ip地址异常");
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (ipAddress.equals(ip)) {
|
||||
boolean result = handleAccessTimeCheck(user, accessStartTime, accessEndTime);
|
||||
if (!result) {
|
||||
throw new ServiceException("ip地址异常");
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查当前时间是否在有效的访问时间范围内
|
||||
* @param accessStartTime 访问开始时间
|
||||
* @param accessEndTime 访问结束时间
|
||||
*/
|
||||
private boolean handleAccessTimeCheck(SysUser user, String accessStartTime, String accessEndTime) {
|
||||
if (ObjectUtils.isNotEmpty(accessStartTime)) {
|
||||
boolean currentTimeInRange = isCurrentTimeInRange(accessStartTime, accessEndTime);
|
||||
if (!currentTimeInRange) {
|
||||
recordLogService.saveErrorLogs(user.getUserName(), System.currentTimeMillis(), user.getUserId().toString(),"IP地址异常");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理正常情况
|
||||
*/
|
||||
private void handleNormalProcess() {
|
||||
// 正常处理的具体逻辑
|
||||
System.out.println("IP access is within the allowed time range.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定的IP地址是否在指定的网段区间内
|
||||
*
|
||||
* @param ip 要检查的IP地址,例如 "192.168.1.10"
|
||||
* @param startIp 区间开始的IP地址,例如 "192.168.1.0"
|
||||
* @param endIp 区间结束的IP地址,例如 "192.168.1.255"
|
||||
* @return true 如果IP在区间内;否则返回 false
|
||||
*/
|
||||
public static boolean isIpInRange(String ip, String startIp, String endIp) {
|
||||
try {
|
||||
// 将 IP 地址、起始 IP 和结束 IP 转换为整数
|
||||
long ipToCheck = ipToLong(InetAddress.getByName(ip));
|
||||
long start = ipToLong(InetAddress.getByName(startIp));
|
||||
long end = ipToLong(InetAddress.getByName(endIp));
|
||||
|
||||
// 检查 IP 是否在区间内
|
||||
return ipToCheck >= start && ipToCheck <= end;
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将IP地址转换为整数
|
||||
*
|
||||
* @param inetAddress IP地址对象
|
||||
* @return 转换后的长整数
|
||||
*/
|
||||
private static long ipToLong(InetAddress inetAddress) {
|
||||
byte[] octets = inetAddress.getAddress();
|
||||
long result = 0;
|
||||
for (byte octet : octets) {
|
||||
result = (result << 8) | (octet & 0xFF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static boolean isCurrentTimeInRange(String startDateTime, String endDateTime) {
|
||||
// 定义日期时间格式
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// 将字符串转换为 LocalDateTime
|
||||
LocalDateTime start = LocalDateTime.parse(startDateTime, formatter);
|
||||
LocalDateTime end = LocalDateTime.parse(endDateTime, formatter);
|
||||
|
||||
// 获取当前日期和时间
|
||||
LocalDateTime currentDateTime = LocalDateTime.now();
|
||||
|
||||
// 检查当前日期和时间是否在指定的范围内
|
||||
return !currentDateTime.isBefore(start) && !currentDateTime.isAfter(end);
|
||||
}
|
||||
|
||||
|
||||
public void validateApprovalStatus(String username, SysUser user) {
|
||||
if ("0".equals(user.getApprovalStatus())) {
|
||||
logAndThrowError(username, "账号未审批", "用户不存在");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录错误日志并抛出异常
|
||||
*/
|
||||
private void logAndThrowError(String username, String message, String logMessage) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
recordLogService.saveLogs(username, startTime, logMessage, message, null, "失败");
|
||||
throw new ServiceException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验手机验证码
|
||||
*/
|
||||
public void checkPhoneCaptcha(String phone, String code) throws CaptchaException {
|
||||
if (StringUtils.isEmpty(code)) {
|
||||
throw new CaptchaException("手机验证码不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(phone)) {
|
||||
throw new CaptchaException("手机号不能为空");
|
||||
}
|
||||
String verifyKey = CacheConstants.VERIFICATION_CODE + StringUtils.nvl(phone, "");
|
||||
String captcha = redisService.getCacheObject(verifyKey);
|
||||
if (captcha == null) {
|
||||
throw new CaptchaException("手机验证码已失效");
|
||||
}
|
||||
|
||||
if (!code.equalsIgnoreCase(captcha)) {
|
||||
throw new CaptchaException("手机验证码错误");
|
||||
}else {
|
||||
redisService.deleteObject(verifyKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.utils.encryption.Sm4Utils;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class PhoneOtpLoginStrategy implements LoginStrategy {
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Resource
|
||||
private PasswordValidatorService passwordValidatorService;
|
||||
|
||||
|
||||
@Override
|
||||
public LoginUser login(String phone, String otp) {
|
||||
if (!systemConfig.getLoginConfig().isPhoneCode()) {
|
||||
throw new ServiceException("用户不存在/验证码错误");
|
||||
}
|
||||
passwordValidatorService.checkPhoneCaptcha(phone, otp);
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfoByPhone(phone, SecurityConstants.INNER);
|
||||
//验证用户是否存在
|
||||
passwordValidatorService.validateUserResult(phone, userResult);
|
||||
LoginUser userInfo = userResult.getData();
|
||||
SysUser user = userInfo.getSysUser();
|
||||
passwordValidatorService.validateApprovalStatus(phone, user);
|
||||
// 验证用户状态
|
||||
passwordValidatorService.validateUserStatus(phone, user);
|
||||
|
||||
passwordValidatorService.processLoginBlackList(user);
|
||||
//返回信息
|
||||
return userInfo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.utils.encryption.Sm4Utils;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class PhonePasswordLoginStrategy implements LoginStrategy {
|
||||
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Resource
|
||||
private PasswordValidatorService passwordValidatorService;
|
||||
|
||||
@Resource
|
||||
private SysPasswordService passwordService;
|
||||
|
||||
@Override
|
||||
public LoginUser login(String phone, String password) {
|
||||
if (!systemConfig.getLoginConfig().isPhonePassword()) {
|
||||
throw new ServiceException("用户不存在/密码错误");
|
||||
}
|
||||
//通过手机号获取用户信息
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfoByPhone(phone, SecurityConstants.INNER);
|
||||
//验证用户是否存在
|
||||
passwordValidatorService.validateUserResult(phone, userResult);
|
||||
//获取用户信息
|
||||
LoginUser userInfo = userResult.getData();
|
||||
SysUser user = userInfo.getSysUser();
|
||||
//校验用户审批状态
|
||||
passwordValidatorService.validateApprovalStatus(user.getUserName(), user);
|
||||
// 处理IP校验
|
||||
passwordValidatorService.validateIpBlacklist(user.getUserName());
|
||||
// 验证密码
|
||||
passwordService.validate(user, password, System.currentTimeMillis());
|
||||
//校验用户启用状态
|
||||
passwordValidatorService.validateUserStatus(user.getUserName(), user);
|
||||
|
||||
passwordValidatorService.processLoginBlackList(user);
|
||||
//返回信息
|
||||
return userInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.utils.encryption.Sm4Utils;
|
||||
import com.bonus.common.security.service.EmailService;
|
||||
import com.bonus.common.security.service.SmsService;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class RegisterVerificationCodeSender implements VerificationCodeStrategy {
|
||||
|
||||
private static final String EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
|
||||
private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";
|
||||
|
||||
@Resource
|
||||
private EmailService emailService;
|
||||
@Resource
|
||||
private SmsService smsService;
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
/**
|
||||
* 发送验证码到邮箱或手机
|
||||
*
|
||||
* @param contactInfo 可以是邮箱地址或手机号码
|
||||
* @return 验证码发送的结果
|
||||
*/
|
||||
@Override
|
||||
public void sendVerificationCode(String contactInfo) {
|
||||
if (isEmail(contactInfo)) {
|
||||
if (!systemConfig.getRegistersConfig().isEmailRegisters()) {
|
||||
throw new ServiceException("请输入正确的联系方式");
|
||||
}
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfoByEmail(contactInfo , SecurityConstants.INNER);
|
||||
if (userResult.getData() != null) {
|
||||
throw new ServiceException("联系方式已经注册账号");
|
||||
}
|
||||
emailService.sendSimpleEmail(contactInfo);
|
||||
} else if (isPhone(contactInfo)) {
|
||||
if (!systemConfig.getRegistersConfig().isPhoneRegisters()) {
|
||||
throw new ServiceException("请输入正确的联系方式");
|
||||
}
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfoByPhone(contactInfo, SecurityConstants.INNER);
|
||||
if (userResult.getData() != null) {
|
||||
throw new ServiceException("联系方式已经注册账号");
|
||||
}
|
||||
smsService.sendSimplePhone(contactInfo);
|
||||
} else {
|
||||
throw new ServiceException("请输入正确的联系方式");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是邮箱
|
||||
*
|
||||
* @param contactInfo 输入信息
|
||||
* @return 是否为邮箱
|
||||
*/
|
||||
private boolean isEmail(String contactInfo) {
|
||||
return contactInfo.matches(EMAIL_REGEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是手机号
|
||||
*
|
||||
* @param contactInfo 输入信息
|
||||
* @return 是否为手机号
|
||||
*/
|
||||
private boolean isPhone(String contactInfo) {
|
||||
return contactInfo.matches(PHONE_REGEX);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.bonus.auth.config.VerificationCodeType;
|
||||
import com.bonus.auth.factory.VerificationCodeStrategyFactory;
|
||||
import com.bonus.auth.form.RegisterBody;
|
||||
import com.bonus.common.core.constant.Constants;
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.constant.UserConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.utils.StringUtils;
|
||||
import com.bonus.common.core.utils.encryption.Sm4Utils;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.security.utils.SecurityUtils;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.RemoteConfigService;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import com.hankcs.hanlp.HanLP;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 登录校验方法
|
||||
* 提供登录、注册、验证码获取等服务
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SysLoginService {
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private SysRecordLogService recordLogService;
|
||||
|
||||
|
||||
@Resource
|
||||
private PasswordValidatorService passwordValidatorService;
|
||||
|
||||
@Resource
|
||||
private VerificationCodeStrategyFactory verificationCodeStrategyFactory;
|
||||
@Autowired
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource
|
||||
private RemoteConfigService configService;
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*
|
||||
* @param username 用户名或手机号
|
||||
* @param verificationCodeType 获取验证码类型(register:注册,其他:登录)
|
||||
* @return 响应结果
|
||||
*/
|
||||
public R<?> getPhoneCode(String username, VerificationCodeType verificationCodeType) {
|
||||
// 获取相应的登录策略
|
||||
VerificationCodeStrategy strategyFactory = verificationCodeStrategyFactory.getStrategy(verificationCodeType);
|
||||
if (strategyFactory == null) {
|
||||
return R.fail("不支持的方式");
|
||||
}
|
||||
strategyFactory.sendVerificationCode(username);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*
|
||||
* @param loginName 登录名
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
public void logout(String loginName, String userId) {
|
||||
try {
|
||||
recordLogService.saveLogout(loginName, "成功", "成功", userId, "成功");
|
||||
} catch (Exception e) {
|
||||
recordLogService.saveLogs(loginName, System.currentTimeMillis(), "登出异常", e.getMessage(), null, "失败");
|
||||
throw new ServiceException("退出失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param registerBody 注册信息
|
||||
*/
|
||||
public void register(RegisterBody registerBody) {
|
||||
long startTime = System.currentTimeMillis(); // 记录开始时间
|
||||
String result = convertAndAppend(registerBody.getNickName(), registerBody.getMobile());
|
||||
int contactType = getContactType(registerBody.getMobile());
|
||||
|
||||
if (contactType == 0) {
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfoByEmail(registerBody.getMobile() , SecurityConstants.INNER);
|
||||
if (userResult.getData() != null) {
|
||||
throw new ServiceException("联系方式已经注册账号");
|
||||
}
|
||||
} else if (contactType == 1) {
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfoByPhone(registerBody.getMobile() , SecurityConstants.INNER);
|
||||
if (userResult.getData() != null) {
|
||||
throw new ServiceException("联系方式已经注册账号");
|
||||
}
|
||||
}else {
|
||||
throw new ServiceException("请输入正确的联系方式");
|
||||
}
|
||||
registerBody.setUsername(result);
|
||||
if (StringUtils.isAnyBlank(registerBody.getUsername(), registerBody.getPassword()) ||
|
||||
registerBody.getUsername().length() < UserConstants.USERNAME_MIN_LENGTH ||
|
||||
registerBody.getUsername().length() > UserConstants.USERNAME_MAX_LENGTH) {
|
||||
recordLogService.saveLogs(registerBody.getUsername(), startTime, "注册参数无效", "账户或密码长度不符合要求", null, "失败");
|
||||
throw new ServiceException("账户或密码长度不符合要求");
|
||||
}
|
||||
if (systemConfig.getRegistersConfig().isVerificationCode()){
|
||||
passwordValidatorService.checkPhoneCaptcha(registerBody.getMobile(),registerBody.getVerificationCode());
|
||||
}
|
||||
AjaxResult ajaxResult = passwordValidatorService.validatePassword(registerBody.getUsername(), registerBody.getPassword());
|
||||
|
||||
if (ajaxResult.isError()) {
|
||||
throw new ServiceException((String) ajaxResult.get("msg"));
|
||||
}
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUserName(registerBody.getUsername());
|
||||
sysUser.setNickName(registerBody.getNickName());
|
||||
|
||||
if (systemConfig.getRegistersConfig().isApprovalStatus()){
|
||||
sysUser.setApprovalStatus("0");
|
||||
sysUser.setStatus("1");
|
||||
}else {
|
||||
sysUser.setApprovalStatus("1");
|
||||
sysUser.setStatus("0");
|
||||
}
|
||||
//有要求另加
|
||||
sysUser.setPassword(SecurityUtils.encryptPassword(registerBody.getPassword()));
|
||||
if (getContactType(registerBody.getMobile()) == 1) {
|
||||
sysUser.setPhonenumber(registerBody.getMobile());
|
||||
} else {
|
||||
sysUser.setEmail(registerBody.getMobile());
|
||||
}
|
||||
|
||||
try {
|
||||
R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER);
|
||||
if (R.FAIL == registerResult.getCode()) {
|
||||
recordLogService.saveLogs(registerBody.getUsername(), startTime, "注册失败", registerResult.getMsg(), null, "失败");
|
||||
throw new ServiceException(registerResult.getMsg());
|
||||
}
|
||||
|
||||
recordLogService.recordLogininfor(registerBody.getUsername(), Constants.REGISTER, "注册成功");
|
||||
} catch (Exception e) {
|
||||
recordLogService.saveLogs(registerBody.getUsername(), startTime, "注册异常", e.getMessage(), null, "失败");
|
||||
throw new ServiceException("注册失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取联系方式类型
|
||||
*
|
||||
* @param contactInfo 联系方式
|
||||
* @return 联系方式类型(0:邮箱,1:手机号,2:无效)
|
||||
*/
|
||||
public static int getContactType(String contactInfo) {
|
||||
String emailRegex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
|
||||
String phoneRegex = "^1[3-9]\\d{9}$";
|
||||
if (contactInfo.matches(emailRegex)) {
|
||||
return 0;
|
||||
} else if (contactInfo.matches(phoneRegex)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将中文转换为拼音并拼接联系方式
|
||||
*
|
||||
* @param chineseText 中文文本
|
||||
* @param contactInfo 联系方式
|
||||
* @return 拼接后的字符串
|
||||
*/
|
||||
public static String convertAndAppend(String chineseText, String contactInfo) {
|
||||
String pinyin = HanLP.convertToPinyinString(chineseText, "", true);
|
||||
int contactType = getContactType(contactInfo);
|
||||
|
||||
if (contactType == 0) {
|
||||
return pinyin + "_" + contactInfo.substring(0, 4);
|
||||
} else if (contactType == 1) {
|
||||
return pinyin + "_" + contactInfo.substring(contactInfo.length() - 4);
|
||||
} else {
|
||||
return pinyin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* i皖送Web端登录
|
||||
* @param ticket
|
||||
* @param iwsWebAppId
|
||||
* @param iwsWebUrl
|
||||
* @return
|
||||
*/
|
||||
public void iwsWebLogin(String ticket, String iwsWebAppId, String iwsWebUrl,LoginUser loginUser,SysUser sysUser) {
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("ticket", ticket);
|
||||
paramMap.put("appId", iwsWebAppId);
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
ResponseEntity<String> authResponse = restTemplate.getForEntity(iwsWebUrl, String.class, paramMap);
|
||||
log.info("authResponse:" + authResponse.toString());
|
||||
if ("200".equals(authResponse.getStatusCode())){
|
||||
// 根据ResponseEntity<String> responseEntity对象,获取body部分,body为json格式字符串
|
||||
String content = authResponse.getBody();
|
||||
// 将json字符串转化为json对象
|
||||
JSONObject json = JSONObject.parseObject(content);
|
||||
// 取出data部分对象
|
||||
JSONObject data = json.getJSONObject("data");
|
||||
sysUser.setUserName(data.get("userName").toString());
|
||||
sysUser.setNickName(data.get("name").toString());
|
||||
sysUser.setPhonenumber(data.get("mobile").toString());
|
||||
loginUser.setSysUser(sysUser);
|
||||
createUser(sysUser,loginUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 先查,如果没有进行创建
|
||||
* @param sysUser
|
||||
* @param loginUser
|
||||
*/
|
||||
private void createUser(SysUser sysUser, LoginUser loginUser) {
|
||||
//通过用户名获取人员信息
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfo(sysUser.getUserName(), SecurityConstants.INNER);
|
||||
if (userResult.getData() == null || R.FAIL == userResult.getCode()) {
|
||||
log.info("登录用户不存在,进行创建----");
|
||||
RegisterBody registerBody = new RegisterBody();
|
||||
registerBody.setUsername(sysUser.getUserName());
|
||||
registerBody.setNickName(sysUser.getNickName());
|
||||
registerBody.setMobile(sysUser.getPhonenumber());
|
||||
//获取配置中的初始密码
|
||||
AjaxResult result = configService.getConfigKey("sys.user.initPassword");
|
||||
if (result.isSuccess())
|
||||
{
|
||||
sysUser.setPassword(result.get("msg").toString());
|
||||
}
|
||||
//新用户注册
|
||||
try {
|
||||
register(registerBody);
|
||||
//查询用户信息
|
||||
userResult = remoteUserService.getUserInfo(sysUser.getUserName(), SecurityConstants.INNER);
|
||||
loginUser = userResult.getData();
|
||||
sysUser = loginUser.getSysUser();
|
||||
//初始化一个角色
|
||||
Long[] roleIds = new Long[5];
|
||||
// 将数组的第一个元素赋值为 2
|
||||
roleIds[0] = 2L;
|
||||
remoteUserService.insertAuthRole(sysUser.getUserId(),roleIds,SecurityConstants.INNER);
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("登录失败,请稍后重试");
|
||||
}
|
||||
}else {
|
||||
loginUser = userResult.getData();
|
||||
sysUser = loginUser.getSysUser();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* i皖送H5端登录
|
||||
* @param ticket
|
||||
* @param iwsH5AppId
|
||||
* @param iwsH5Url
|
||||
* @return
|
||||
*/
|
||||
public void iwsH5Login(String ticket, String iwsH5AppId, String iwsH5Url,LoginUser loginUser,SysUser sysUser) {
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("ticket", ticket);
|
||||
paramMap.put("appId", iwsH5AppId);
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
ResponseEntity<String> authResponse = restTemplate.getForEntity(iwsH5Url, String.class, paramMap);
|
||||
log.info("authResponse:" + authResponse.toString());
|
||||
if ("200".equals(authResponse.getStatusCode())){
|
||||
// 根据ResponseEntity<String> responseEntity对象,获取body部分,body为json格式字符串
|
||||
String content = authResponse.getBody();
|
||||
// 将json字符串转化为json对象
|
||||
JSONObject json = JSONObject.parseObject(content);
|
||||
// 取出data部分对象
|
||||
JSONObject data = json.getJSONObject("data");
|
||||
JSONObject userInfo = data.getJSONObject("userInfo");
|
||||
sysUser.setUserName(userInfo.get("userName").toString());
|
||||
sysUser.setNickName(userInfo.get("name").toString());
|
||||
sysUser.setPhonenumber(userInfo.get("mobile").toString());
|
||||
loginUser.setSysUser(sysUser);
|
||||
createUser(sysUser,loginUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.CacheConstants;
|
||||
import com.bonus.common.core.constant.Constants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.common.core.exception.CaptchaException;
|
||||
import com.bonus.common.core.exception.ServiceException;
|
||||
import com.bonus.common.core.utils.StringUtils;
|
||||
import com.bonus.common.core.utils.VerificationCodeUtils;
|
||||
import com.bonus.common.core.utils.sms.SmsUtils;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.redis.service.RedisService;
|
||||
import com.bonus.common.security.utils.SecurityUtils;
|
||||
import com.bonus.system.api.RemoteConfigService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 登录密码方法
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Component
|
||||
public class SysPasswordService {
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
private int maxRetryCount = CacheConstants.PASSWORD_MAX_RETRY_COUNT;
|
||||
|
||||
private Long lockTime = CacheConstants.PASSWORD_LOCK_TIME;
|
||||
|
||||
@Autowired
|
||||
private SysRecordLogService recordLogService;
|
||||
|
||||
@Resource
|
||||
private RemoteConfigService configService;
|
||||
|
||||
/**
|
||||
* 登录账户密码错误次数缓存键名
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 缓存键key
|
||||
*/
|
||||
private String getCacheKey(String username) {
|
||||
return CacheConstants.REDIS_KEY + username;
|
||||
}
|
||||
|
||||
public void validate(SysUser user, String password, long startTime) {
|
||||
String username = user.getUserName();
|
||||
Integer retryCount = redisService.getCacheObject(getCacheKey(username));
|
||||
Integer times = 5;
|
||||
Integer lockTime = 20;
|
||||
AjaxResult timesAjaxResult = configService.getConfigKey("sys.login.failed.times");
|
||||
AjaxResult lockTimeAjaxResult = configService.getConfigKey("sys.login.failed.locktime");
|
||||
if (timesAjaxResult.isSuccess()){
|
||||
times = Integer.parseInt(timesAjaxResult.get("msg").toString());
|
||||
}
|
||||
if (lockTimeAjaxResult.isSuccess()){
|
||||
lockTime = Integer.parseInt(lockTimeAjaxResult.get("msg").toString());
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(retryCount)){
|
||||
retryCount = 0;
|
||||
}
|
||||
if (retryCount >= times) {
|
||||
long time = redisService.getExpire(getCacheKey(username));
|
||||
String errMsg = String.format("密码输入错误%s次,帐户锁定,请%s分钟后重试", maxRetryCount, time / 60 + 1);
|
||||
recordLogService.saveErrorLogs( user.getUserName(), startTime,"", "连续登录失败,锁定账号" );
|
||||
throw new ServiceException(errMsg);
|
||||
}
|
||||
if (!matches(user, password)) {
|
||||
retryCount = retryCount + 1;
|
||||
recordLogService.saveLogs(username, startTime, "密码输入错误", "用户不存在/密码错误", null, null);
|
||||
redisService.setCacheObject(getCacheKey(username), retryCount, (long)lockTime, TimeUnit.MINUTES);
|
||||
throw new ServiceException("用户不存在/密码错误");
|
||||
} else {
|
||||
clearLoginRecordCache(username);
|
||||
}
|
||||
}
|
||||
public boolean matches(SysUser user, String rawPassword) {
|
||||
return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
|
||||
}
|
||||
|
||||
public void clearLoginRecordCache(String loginName) {
|
||||
if (redisService.hasKey(getCacheKey(loginName))) {
|
||||
redisService.deleteObject(getCacheKey(loginName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成手机验证码
|
||||
*
|
||||
* @return AjaxResult
|
||||
* @throws IOException 输入输出异常
|
||||
* @throws CaptchaException 自定义captcha 异常
|
||||
*/
|
||||
public R createPhoneCaptcha(String phone) {
|
||||
if (StringUtils.isEmpty(phone)) {
|
||||
throw new CaptchaException("手机号不能为空");
|
||||
}
|
||||
String code = VerificationCodeUtils.generateVerificationCode(VerificationCodeUtils.CodeType.NUMERIC);
|
||||
String str = "您的验证码为" + code + ",尊敬的客户,以上验证码3分钟有效,微服务平台提醒您:转发可能导致账号被盗,请勿将验证码泄露于他人";
|
||||
String verifyKey = CacheConstants.VERIFICATION_CODE + phone;
|
||||
String s = SmsUtils.smsToken(phone, str, "");
|
||||
if (StringUtils.isNotEmpty(s)) {
|
||||
if (s.contains("ok")) {
|
||||
redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
|
||||
} else {
|
||||
throw new CaptchaException("获取短信失败");
|
||||
}
|
||||
} else {
|
||||
throw new CaptchaException("获取短信失败");
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.alibaba.nacos.common.utils.UuidUtils;
|
||||
import com.bonus.common.core.utils.DateUtils;
|
||||
import com.bonus.common.core.utils.global.SystemGlobal;
|
||||
import com.bonus.common.log.enums.OperaResult;
|
||||
import com.bonus.common.log.enums.OperaType;
|
||||
import com.bonus.config.SystemConfig;
|
||||
import com.bonus.system.api.domain.SysLogsVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.bonus.common.core.constant.Constants;
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.utils.StringUtils;
|
||||
import com.bonus.common.core.utils.ip.IpUtils;
|
||||
import com.bonus.system.api.RemoteLogService;
|
||||
import com.bonus.system.api.domain.SysLogininfor;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 记录日志方法
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SysRecordLogService
|
||||
{
|
||||
@Autowired
|
||||
private RemoteLogService remoteLogService;
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param status 状态
|
||||
* @param message 消息内容
|
||||
* @return
|
||||
*/
|
||||
public void recordLogininfor(String username, String status, String message)
|
||||
{
|
||||
SysLogininfor logininfor = new SysLogininfor();
|
||||
logininfor.setUserName(username);
|
||||
logininfor.setIpaddr(IpUtils.getIpAddr());
|
||||
logininfor.setMsg(message);
|
||||
// 日志状态
|
||||
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
|
||||
{
|
||||
logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS);
|
||||
}
|
||||
else if (Constants.LOGIN_FAIL.equals(status))
|
||||
{
|
||||
logininfor.setStatus(Constants.LOGIN_FAIL_STATUS);
|
||||
}
|
||||
remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param
|
||||
* @param message 消息内容
|
||||
* @return
|
||||
*/
|
||||
public void saveLogs(String username, long startTime, String message,String resultData,String userId,String result) {
|
||||
long endTime = System.currentTimeMillis();
|
||||
SysLogsVo sysLogsVo = new SysLogsVo();
|
||||
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
|
||||
sysLogsVo.setLogId(uuid);
|
||||
sysLogsVo.setOperaUserName(username);
|
||||
sysLogsVo.setIp(IpUtils.getIpAddr());
|
||||
sysLogsVo.setModel("系统认证模块");
|
||||
sysLogsVo.setOperTime(DateUtils.getTime());
|
||||
sysLogsVo.setMethodType(SystemGlobal.POST);
|
||||
sysLogsVo.setMethod("login()");
|
||||
sysLogsVo.setParams("{\"username\":\""+username+"\"}");
|
||||
sysLogsVo.setOperateDetail("用户登录系统");
|
||||
sysLogsVo.setOperType(OperaType.LOGIN);
|
||||
sysLogsVo.setOperUri("/login");
|
||||
sysLogsVo.setLogType(0);
|
||||
if (StringUtils.isNotEmpty(result)){
|
||||
sysLogsVo.setResult(result);
|
||||
}else{
|
||||
sysLogsVo.setResult(OperaResult.FAIL);
|
||||
}
|
||||
if (StringUtils.isNotEmpty(userId)){
|
||||
sysLogsVo.setUserId(userId);
|
||||
}
|
||||
sysLogsVo.setFailureReason(message);
|
||||
sysLogsVo.setTitle("系统登录");
|
||||
sysLogsVo.setResultData(resultData);
|
||||
try{
|
||||
long times=endTime-startTime;
|
||||
sysLogsVo.setTimes(times+"");
|
||||
remoteLogService.addLogs(sysLogsVo, SecurityConstants.INNER);
|
||||
}catch (Exception e){
|
||||
log.error(e.toString(),e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 记录IP异常信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
public void saveErrorLogs(String username, long startTime,String userId, String errMessage) {
|
||||
long endTime = System.currentTimeMillis();
|
||||
SysLogsVo sysLogsVo = new SysLogsVo();
|
||||
sysLogsVo.setGrade("高");
|
||||
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
|
||||
sysLogsVo.setOperType("登录");
|
||||
sysLogsVo.setOperUri("/login");
|
||||
sysLogsVo.setLogType(2);
|
||||
sysLogsVo.setResult(OperaResult.SUCCESS);
|
||||
if (StringUtils.isNotEmpty(userId)){
|
||||
sysLogsVo.setUserId(userId);
|
||||
}
|
||||
sysLogsVo.setIp(IpUtils.getIpAddr());
|
||||
sysLogsVo.setResultData("用户登录成功");
|
||||
sysLogsVo.setTitle("系统登录");
|
||||
sysLogsVo.setModel("系统认证模块");
|
||||
sysLogsVo.setOperTime(DateUtils.getTime());
|
||||
sysLogsVo.setMethodType(SystemGlobal.POST);
|
||||
sysLogsVo.setMethod("login()");
|
||||
sysLogsVo.setLogId(uuid);
|
||||
sysLogsVo.setOperaUserName(username);
|
||||
sysLogsVo.setIp(IpUtils.getIpAddr());
|
||||
sysLogsVo.setParams("{\"username\":\""+username+"\"}");
|
||||
sysLogsVo.setOperateDetail("用户登录系统");
|
||||
sysLogsVo.setErrType(errMessage);
|
||||
try{
|
||||
if(startTime != 0) {
|
||||
long times = endTime - startTime;
|
||||
sysLogsVo.setTimes(times + "");
|
||||
}
|
||||
remoteLogService.addLogs(sysLogsVo, SecurityConstants.INNER);
|
||||
}catch (Exception e){
|
||||
log.error(e.toString(),e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 记录登出信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param
|
||||
* @param message 消息内容
|
||||
* @return
|
||||
*/
|
||||
public void saveLogout(String username, String message,String resultData,String userId,String result) {
|
||||
SysLogsVo sysLogsVo = new SysLogsVo();
|
||||
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
|
||||
sysLogsVo.setLogId(uuid);
|
||||
sysLogsVo.setOperaUserName(username);
|
||||
sysLogsVo.setIp(IpUtils.getIpAddr());
|
||||
sysLogsVo.setModel("系统认证模块");
|
||||
sysLogsVo.setLogType(0);
|
||||
if (StringUtils.isNotEmpty(userId)){
|
||||
sysLogsVo.setUserId(userId);
|
||||
}
|
||||
sysLogsVo.setOperTime(DateUtils.getTime());
|
||||
sysLogsVo.setMethodType(SystemGlobal.POST);
|
||||
sysLogsVo.setMethod("logout()");
|
||||
sysLogsVo.setParams("{\"username\":\""+username+"\"}");
|
||||
sysLogsVo.setOperateDetail("用户退出登录");
|
||||
sysLogsVo.setOperType("登出");
|
||||
sysLogsVo.setOperUri("/logout");
|
||||
if (StringUtils.isNotEmpty(result)){
|
||||
sysLogsVo.setResult(result);
|
||||
}else{
|
||||
sysLogsVo.setResult(OperaResult.SUCCESS);
|
||||
}
|
||||
sysLogsVo.setFailureReason(message);
|
||||
sysLogsVo.setTitle("退出登录");
|
||||
sysLogsVo.setResultData(resultData);
|
||||
try{
|
||||
remoteLogService.addLogs(sysLogsVo, SecurityConstants.INNER);
|
||||
}catch (Exception e){
|
||||
log.error(e.toString(),e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param
|
||||
* @param message 消息内容
|
||||
* @return
|
||||
*/
|
||||
public void saveLogin(String username, String message,String resultData,String userId,String result) {
|
||||
SysLogsVo sysLogsVo = new SysLogsVo();
|
||||
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
|
||||
sysLogsVo.setLogId(uuid);
|
||||
sysLogsVo.setOperaUserName(username);
|
||||
sysLogsVo.setIp(IpUtils.getIpAddr());
|
||||
sysLogsVo.setModel("系统认证模块");
|
||||
sysLogsVo.setLogType(0);
|
||||
if (StringUtils.isNotEmpty(userId)){
|
||||
sysLogsVo.setUserId(userId);
|
||||
}
|
||||
sysLogsVo.setOperTime(DateUtils.getTime());
|
||||
sysLogsVo.setMethodType(SystemGlobal.POST);
|
||||
sysLogsVo.setMethod("login()");
|
||||
sysLogsVo.setParams("{\"username\":\""+username+"\"}");
|
||||
sysLogsVo.setOperateDetail("用户登录");
|
||||
sysLogsVo.setOperType(OperaType.LOGIN);
|
||||
sysLogsVo.setOperUri("/login");
|
||||
if (StringUtils.isNotEmpty(result)){
|
||||
sysLogsVo.setResult(result);
|
||||
}else{
|
||||
sysLogsVo.setResult(OperaResult.SUCCESS);
|
||||
}
|
||||
sysLogsVo.setFailureReason(message);
|
||||
sysLogsVo.setTitle("登录");
|
||||
sysLogsVo.setResultData(resultData);
|
||||
try{
|
||||
remoteLogService.addLogs(sysLogsVo, SecurityConstants.INNER);
|
||||
}catch (Exception e){
|
||||
log.error(e.toString(),e);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveRegister(String username, String message,String resultData,String userId,String result) {
|
||||
SysLogsVo sysLogsVo = new SysLogsVo();
|
||||
String uuid= UUID.randomUUID().toString().replace("-","").toUpperCase();
|
||||
sysLogsVo.setLogId(uuid);
|
||||
sysLogsVo.setOperaUserName(username);
|
||||
sysLogsVo.setIp(IpUtils.getIpAddr());
|
||||
sysLogsVo.setModel("系统认证模块");
|
||||
sysLogsVo.setLogType(0);
|
||||
if (StringUtils.isNotEmpty(userId)){
|
||||
sysLogsVo.setUserId(userId);
|
||||
}
|
||||
sysLogsVo.setOperTime(DateUtils.getTime());
|
||||
sysLogsVo.setMethodType(SystemGlobal.POST);
|
||||
sysLogsVo.setMethod("register()");
|
||||
sysLogsVo.setParams("{\"username\":\""+username+"\"}");
|
||||
sysLogsVo.setOperateDetail("用户注册");
|
||||
sysLogsVo.setOperType(OperaType.REGISTER);
|
||||
sysLogsVo.setOperUri("/register");
|
||||
if (StringUtils.isNotEmpty(result)){
|
||||
sysLogsVo.setResult(result);
|
||||
}else{
|
||||
sysLogsVo.setResult(OperaResult.SUCCESS);
|
||||
}
|
||||
sysLogsVo.setFailureReason(message);
|
||||
sysLogsVo.setTitle("注册");
|
||||
sysLogsVo.setResultData(resultData);
|
||||
try{
|
||||
remoteLogService.addLogs(sysLogsVo, SecurityConstants.INNER);
|
||||
}catch (Exception e){
|
||||
log.error(e.toString(),e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
import com.bonus.common.core.constant.SecurityConstants;
|
||||
import com.bonus.common.core.domain.R;
|
||||
import com.bonus.system.api.RemoteUserService;
|
||||
import com.bonus.system.api.domain.SysUser;
|
||||
import com.bonus.system.api.model.LoginUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class UsernamePasswordLoginStrategy implements LoginStrategy {
|
||||
@Resource
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Resource
|
||||
private PasswordValidatorService passwordValidatorService;
|
||||
|
||||
@Resource
|
||||
private SysPasswordService passwordService;
|
||||
|
||||
|
||||
/**
|
||||
* 登录方法
|
||||
*
|
||||
* @param username 用户的标识符(用户名、手机号、邮箱)
|
||||
* @param password 用户凭据(密码或验证码)
|
||||
* @return 登录结果
|
||||
*/
|
||||
@Override
|
||||
public LoginUser login(String username, String password) {
|
||||
//参数校验
|
||||
passwordValidatorService.validateLoginParameters(username, password);
|
||||
// IP黑名单校验;
|
||||
passwordValidatorService.validateIpBlacklist(username);
|
||||
//通过用户名获取人员信息
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
|
||||
passwordValidatorService.validateUserResult(username, userResult);
|
||||
//获取用户信息
|
||||
LoginUser userInfo = userResult.getData();
|
||||
SysUser user = userInfo.getSysUser();
|
||||
passwordValidatorService.validateApprovalStatus(username, user);
|
||||
// 验证用户状态
|
||||
passwordValidatorService.validateUserStatus(username, user);
|
||||
// 验证密码
|
||||
passwordService.validate(user, password, System.currentTimeMillis());
|
||||
// 处理IP校验
|
||||
passwordValidatorService.handleIpValidation(username, user);
|
||||
|
||||
passwordValidatorService.processLoginBlackList(user);
|
||||
//返回信息
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.bonus.auth.service;
|
||||
|
||||
public interface VerificationCodeStrategy {
|
||||
/**
|
||||
* @param contactInfo 可以是邮箱地址或手机号码
|
||||
*/
|
||||
void sendVerificationCode(String contactInfo);
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Spring Boot Version: ${spring-boot.version}
|
||||
Spring Application Name: ${spring.application.name}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Tomcat
|
||||
server:
|
||||
port: 58079
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: smart_canteen
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: smart_canteen
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
|
||||
#加密组件
|
||||
jasypt:
|
||||
encryptor:
|
||||
password: Encrypt
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: bonus-cust-auth
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: smart_canteen_local
|
||||
|
||||
#加密组件
|
||||
jasypt:
|
||||
encryptor:
|
||||
password: Encrypt
|
||||
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="logs/bonus-cust-auth" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 系统日志输出 -->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/info.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>INFO</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>ERROR</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 系统模块日志级别控制 -->
|
||||
<logger name="com.bonus" level="info" />
|
||||
<!-- Spring日志级别控制 -->
|
||||
<logger name="org.springframework" level="warn" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info" />
|
||||
<appender-ref ref="file_error" />
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue