commit 51bb7a2530f92365562a9ff2c49aab12c5a2633e Author: fst1996 <2411194573@qq.com> Date: Sun Sep 17 16:46:40 2023 +0800 初始化 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05d7c76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ +target/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4316e5b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# 基础镜像 +FROM anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/openjdk:17-8.6 + +#暴露端口位置 +EXPOSE 9200 + +# 挂载目录 +VOLUME /home/logs/bawei-auth + +# 复制jar文件到docker内部 +COPY /target/menghang-auth.jar /home/app.jar + +#工作目录 exec -it 进来默认就是这个目录 +WORKDIR /home + +# 启动java程序 +ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-jar","/home/app.jar"] diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5f8ba9a --- /dev/null +++ b/pom.xml @@ -0,0 +1,92 @@ + + + com.bawei + parent + 3.6.0 + + 4.0.0 + + 3.6.0 + menghang-auth + + + + menghang-public + 梦航-public + http://192.168.111.130:8081/repository/maven-public/ + + + + + + menghang-releases + 梦航-releases + http://192.168.111.130:8081/repository/maven-releases/ + + + + + menghang-auth认证授权中心 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.bawei + menghang-common-security + 3.6.0 + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/src/main/java/com/bawei/auth/BaWeiAuthApplication.java b/src/main/java/com/bawei/auth/BaWeiAuthApplication.java new file mode 100644 index 0000000..aab3620 --- /dev/null +++ b/src/main/java/com/bawei/auth/BaWeiAuthApplication.java @@ -0,0 +1,22 @@ +package com.bawei.auth; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import com.bawei.common.security.annotation.EnableRyFeignClients; + +/** + * 认证授权中心 + * + * @author bawei + */ +@EnableRyFeignClients +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) +public class BaWeiAuthApplication +{ + public static void main(String[] args) + { + SpringApplication.run(BaWeiAuthApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 认证授权中心启动成功 ლ(´ڡ`ლ)゙ "); + } +} diff --git a/src/main/java/com/bawei/auth/controller/TokenController.java b/src/main/java/com/bawei/auth/controller/TokenController.java new file mode 100644 index 0000000..3c7e9e1 --- /dev/null +++ b/src/main/java/com/bawei/auth/controller/TokenController.java @@ -0,0 +1,78 @@ +package com.bawei.auth.controller; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +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 com.bawei.auth.form.LoginBody; +import com.bawei.auth.form.RegisterBody; +import com.bawei.auth.service.SysLoginService; +import com.bawei.common.core.domain.R; +import com.bawei.common.core.utils.JwtUtils; +import com.bawei.common.core.utils.StringUtils; +import com.bawei.common.security.auth.AuthUtil; +import com.bawei.common.security.service.TokenService; +import com.bawei.common.security.utils.SecurityUtils; +import com.bawei.system.domain.model.LoginUser; + +/** + * token 控制 + * + * @author bawei + */ +@RestController +public class TokenController +{ + @Autowired + private TokenService tokenService; + + @Autowired + private SysLoginService sysLoginService; + + @PostMapping("login") + public R login(@RequestBody LoginBody form) + { + // 用户登录 + LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword()); + // 获取登录token + return R.ok(tokenService.createToken(userInfo)); + } + + @DeleteMapping("logout") + public R logout(HttpServletRequest request) + { + String token = SecurityUtils.getToken(request); + if (StringUtils.isNotEmpty(token)) + { + String username = JwtUtils.getUserName(token); + // 删除用户缓存记录 + AuthUtil.logoutByToken(token); + // 记录用户退出日志 + sysLoginService.logout(username); + } + return R.ok(); + } + + @PostMapping("refresh") + public R refresh(HttpServletRequest request) + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) + { + // 刷新令牌有效期 + tokenService.refreshToken(loginUser); + return R.ok(); + } + return R.ok(); + } + + @PostMapping("register") + public R register(@RequestBody RegisterBody registerBody) + { + // 用户注册 + sysLoginService.register(registerBody.getUsername(), registerBody.getPassword()); + return R.ok(); + } +} diff --git a/src/main/java/com/bawei/auth/form/LoginBody.java b/src/main/java/com/bawei/auth/form/LoginBody.java new file mode 100644 index 0000000..c589693 --- /dev/null +++ b/src/main/java/com/bawei/auth/form/LoginBody.java @@ -0,0 +1,39 @@ +package com.bawei.auth.form; + +/** + * 用户登录对象 + * + * @author bawei + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } +} diff --git a/src/main/java/com/bawei/auth/form/RegisterBody.java b/src/main/java/com/bawei/auth/form/RegisterBody.java new file mode 100644 index 0000000..a0f2c10 --- /dev/null +++ b/src/main/java/com/bawei/auth/form/RegisterBody.java @@ -0,0 +1,11 @@ +package com.bawei.auth.form; + +/** + * 用户注册对象 + * + * @author bawei + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/src/main/java/com/bawei/auth/service/SysLoginService.java b/src/main/java/com/bawei/auth/service/SysLoginService.java new file mode 100644 index 0000000..8db1114 --- /dev/null +++ b/src/main/java/com/bawei/auth/service/SysLoginService.java @@ -0,0 +1,129 @@ +package com.bawei.auth.service; + +import com.bawei.system.remote.api.RemoteUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.bawei.common.core.constant.Constants; +import com.bawei.common.core.constant.SecurityConstants; +import com.bawei.common.core.constant.UserConstants; +import com.bawei.common.core.domain.R; +import com.bawei.common.core.enums.UserStatus; +import com.bawei.common.core.exception.ServiceException; +import com.bawei.common.core.utils.StringUtils; +import com.bawei.common.security.utils.SecurityUtils; +import com.bawei.system.domain.SysUser; +import com.bawei.system.domain.model.LoginUser; + +/** + * 登录校验方法 + * + * @author bawei + */ +@Component +public class SysLoginService +{ + @Autowired + private RemoteUserService remoteUserService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysRecordLogService recordLogService; + + /** + * 登录 + */ + public LoginUser login(String username, String password) + { + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username, password)) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); + throw new ServiceException("用户/密码必须填写"); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); + throw new ServiceException("用户密码不在指定范围"); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); + throw new ServiceException("用户名不在指定范围"); + } + // 查询用户信息 + R userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); + + if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); + throw new ServiceException("登录用户:" + username + " 不存在"); + } + + if (R.FAIL == userResult.getCode()) + { + throw new ServiceException(userResult.getMsg()); + } + + LoginUser userInfo = userResult.getData(); + SysUser user = userResult.getData().getSysUser(); + if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); + throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); + } + if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); + throw new ServiceException("对不起,您的账号:" + username + " 已停用"); + } + passwordService.validate(user, password); + recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); + return userInfo; + } + + public void logout(String loginName) + { + recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); + } + + /** + * 注册 + */ + public void register(String username, String password) + { + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username, password)) + { + throw new ServiceException("用户/密码必须填写"); + } + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + throw new ServiceException("账户长度必须在2到20个字符之间"); + } + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + throw new ServiceException("密码长度必须在5到20个字符之间"); + } + + // 注册用户信息 + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + sysUser.setNickName(username); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + R registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER); + + if (R.FAIL == registerResult.getCode()) + { + throw new ServiceException(registerResult.getMsg()); + } + recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功"); + } +} diff --git a/src/main/java/com/bawei/auth/service/SysPasswordService.java b/src/main/java/com/bawei/auth/service/SysPasswordService.java new file mode 100644 index 0000000..becd9af --- /dev/null +++ b/src/main/java/com/bawei/auth/service/SysPasswordService.java @@ -0,0 +1,85 @@ +package com.bawei.auth.service; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.bawei.common.core.constant.CacheConstants; +import com.bawei.common.core.constant.Constants; +import com.bawei.common.core.exception.ServiceException; +import com.bawei.common.redis.service.RedisService; +import com.bawei.common.security.utils.SecurityUtils; +import com.bawei.system.domain.SysUser; + +/** + * 登录密码方法 + * + * @author bawei + */ +@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; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user, String password) + { + String username = user.getUserName(); + + Integer retryCount = redisService.getCacheObject(getCacheKey(username)); + + if (retryCount == null) + { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime); + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL,errMsg); + throw new ServiceException(errMsg); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", retryCount)); + redisService.setCacheObject(getCacheKey(username), retryCount, 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)); + } + } +} diff --git a/src/main/java/com/bawei/auth/service/SysRecordLogService.java b/src/main/java/com/bawei/auth/service/SysRecordLogService.java new file mode 100644 index 0000000..881b38a --- /dev/null +++ b/src/main/java/com/bawei/auth/service/SysRecordLogService.java @@ -0,0 +1,49 @@ +package com.bawei.auth.service; + +import com.bawei.system.remote.api.RemoteLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.bawei.common.core.constant.Constants; +import com.bawei.common.core.constant.SecurityConstants; +import com.bawei.common.core.utils.ServletUtils; +import com.bawei.common.core.utils.StringUtils; +import com.bawei.common.core.utils.ip.IpUtils; +import com.bawei.system.domain.SysLogininfor; + +/** + * 记录日志方法 + * + * @author bawei + */ +@Component +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(ServletUtils.getRequest())); + 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); + } +} diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..97c5c27 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _ _ _ + (_) | | | | + _ __ _ _ ___ _ _ _ ______ __ _ _ _ | |_ | |__ +| '__|| | | | / _ \ | | | || ||______| / _` || | | || __|| '_ \ +| | | |_| || (_) || |_| || | | (_| || |_| || |_ | | | | +|_| \__,_| \___/ \__, ||_| \__,_| \__,_| \__||_| |_| + __/ | + |___/ \ No newline at end of file diff --git a/src/main/resources/bootstrap.yml b/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..3f96560 --- /dev/null +++ b/src/main/resources/bootstrap.yml @@ -0,0 +1,25 @@ +# Tomcat +server: + port: 9200 + +# Spring +spring: + application: + # 应用名称 + name: bawei-auth + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: 192.168.111.130:8848 + config: + # 配置中心地址 + server-addr: 192.168.111.130:8848 + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..fcfa898 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + +