This commit is contained in:
sxu 2025-01-27 14:02:25 +08:00
parent 18b184bdac
commit df46fd869f
30 changed files with 2418 additions and 0 deletions

136
bonus-cust-auth/pom.xml Normal file
View File

@ -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>

View File

@ -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("认证授权中心启动成功");
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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皖送登录方式 0web端登录 1H5登录
*/
private String sysType;
}

View File

@ -0,0 +1,5 @@
package com.bonus.auth.form;
public enum LoginType {
PASSWORD, MOBILE
}

View File

@ -0,0 +1,10 @@
package com.bonus.auth.form;
/**
* 用户注册对象
*
* @author bonus
*/
public class RegisterBody extends LoginBody {
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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("不支持的登录方式");
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
package com.bonus.auth.service;
public interface VerificationCodeStrategy {
/**
* @param contactInfo 可以是邮箱地址或手机号码
*/
void sendVerificationCode(String contactInfo);
}

View File

@ -0,0 +1,2 @@
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}

View File

@ -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

View File

@ -0,0 +1,14 @@
# Spring
spring:
application:
# 应用名称
name: bonus-cust-auth
profiles:
# 环境配置
active: smart_canteen_local
#加密组件
jasypt:
encryptor:
password: Encrypt

View File

@ -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>

View File

@ -246,6 +246,7 @@
<modules>
<module>bonus-modules</module>
<module>bonus-common-biz</module>
<module>bonus-cust-auth</module>
</modules>
<packaging>pom</packaging>