Compare commits

6 Commits

17 changed files with 95 additions and 113 deletions

3
.gitignore vendored
View File

@@ -32,3 +32,6 @@ build/
### VS Code ###
.vscode/
### bak ###
*.bak

View File

@@ -4,6 +4,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
@Configuration
@@ -12,6 +13,6 @@ public class PlusoneExceptionHandlerConfig {
@Bean
@ConditionalOnMissingBean
ExceptionInfoHolder exceptionInfoHolder() {
return new ExceptionInfoHolder();
return new ExceptionInfoHolder(ErrorCodeConsts.DEFAULT_ERROR_CODE);
}
}

View File

@@ -4,7 +4,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler.ExceptionInfoHolder;
/**
@@ -16,6 +15,6 @@ public class AllExceptionHandlerConfig {
@Bean
AllExceptionHandler getAllExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
return new AllExceptionHandler(ErrorCodeConsts.DEFAULT_ERROR_CODE, exceptionInfoHolder);
return new AllExceptionHandler(exceptionInfoHolder);
}
}

View File

@@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.util.RestfulResult;
/**
@@ -43,7 +42,7 @@ import xyz.zhouxy.plusone.util.RestfulResult;
public class DefaultExceptionHandler extends BaseExceptionHandler {
public DefaultExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(ErrorCodeConsts.DEFAULT_ERROR_CODE, exceptionInfoHolder);
super(exceptionInfoHolder);
set(IllegalArgumentException.class, 4010000, "格式错误", HttpStatus.FORBIDDEN);
set(DataAccessException.class, 6030000, "数据库错误", HttpStatus.INTERNAL_SERVER_ERROR, true);
set(MethodArgumentNotValidException.class,

View File

@@ -44,7 +44,7 @@
<dependency>
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-exception-handler</artifactId>
<version>0.0.4-SNAPSHOT</version>
<version>0.0.5-SNAPSHOT</version>
</dependency>
</dependencies>

View File

@@ -0,0 +1,15 @@
package xyz.zhouxy.plusone.validator;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
@RestControllerAdvice
public class InvalidInputExceptionHandler extends BaseExceptionHandler {
protected InvalidInputExceptionHandler() {
super(new ExceptionInfoHolder(ErrorCodeConsts.DEFAULT_ERROR_CODE));
set(InvalidInputException.class, InvalidInputException.ERROR_CODE, "无效的用户输入");
}
}

View File

@@ -1,12 +0,0 @@
package xyz.zhouxy.plusone;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("/test")
public String test() throws Exception {
throw new Exception();
}
}

View File

@@ -34,6 +34,12 @@ public class AccountContextController {
return RestfulResult.success("查询成功", result);
}
@GetMapping("logout")
public RestfulResult logout() {
service.logout();
return RestfulResult.success("注销成功");
}
@GetMapping("menus")
public RestfulResult getMenuTree() {
adminAuthLogic.checkLogin();

View File

@@ -1,30 +0,0 @@
package xyz.zhouxy.plusone.system.application.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.zhouxy.plusone.system.application.service.AdminLogoutService;
import xyz.zhouxy.plusone.util.RestfulResult;
/**
* Admin 账号登出
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@RestController
@RequestMapping("logout")
public class AdminLogoutController {
private final AdminLogoutService service;
public AdminLogoutController(AdminLogoutService service) {
this.service = service;
}
@GetMapping
public RestfulResult execute() {
service.execute();
return RestfulResult.success("注销成功");
}
}

View File

@@ -6,15 +6,14 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.SameTokenInvalidException;
import cn.dev33.satoken.exception.NotBasicAuthException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.NotSafeException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.exception.SameTokenInvalidException;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.constant.ErrorCodeConsts;
import xyz.zhouxy.plusone.exception.handler.BaseExceptionHandler;
import xyz.zhouxy.plusone.util.RestfulResult;
@@ -28,7 +27,7 @@ import xyz.zhouxy.plusone.util.RestfulResult;
public class SaTokenExceptionHandler extends BaseExceptionHandler {
public SaTokenExceptionHandler(ExceptionInfoHolder exceptionInfoHolder) {
super(ErrorCodeConsts.DEFAULT_ERROR_CODE, exceptionInfoHolder);
super(exceptionInfoHolder);
set(NotPermissionException.class, 4030103, "会话未能通过权限认证", HttpStatus.FORBIDDEN);
set(NotRoleException.class, 4030103, "会话未能通过角色认证", HttpStatus.FORBIDDEN);
set(DisableServiceException.class, 4030202, "账号指定服务已被封禁", HttpStatus.FORBIDDEN);

View File

@@ -10,10 +10,13 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.dev33.satoken.stp.StpLogic;
import cn.hutool.core.lang.Assert;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
import xyz.zhouxy.plusone.system.application.query.AccountQueries;
import xyz.zhouxy.plusone.system.application.query.result.AccountDetails;
import xyz.zhouxy.plusone.system.application.query.result.MenuViewObject;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordByOtpCommand;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordCommand;
import xyz.zhouxy.plusone.system.application.service.command.ChangePasswordWithoutLoginCommand;
import xyz.zhouxy.plusone.system.domain.model.account.Account;
@@ -21,9 +24,10 @@ import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
import xyz.zhouxy.plusone.system.domain.model.account.Principal;
import xyz.zhouxy.plusone.validator.InvalidInputException;
/**
* 账号查询本身相关信息
* 账号对当前帐号进行操作
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@@ -47,6 +51,11 @@ public class AccountContextService {
return accountQueries.queryAccountDetails(accountId);
}
public void logout() {
adminAuthLogic.checkLogin();
adminAuthLogic.logout();
}
public List<MenuViewObject> getMenuTree() {
adminAuthLogic.checkLogin();
long accountId = adminAuthLogic.getLoginIdAsLong();
@@ -76,4 +85,18 @@ public class AccountContextService {
accountRepository.save(account);
adminAuthLogic.logout();
}
@Transactional
public void changePasswordByOtp(ChangePasswordByOtpCommand command) {
var principal = command.getAccount();
Account account = switch (command.getPrincipalType()) {
case EMAIL -> accountRepository.findByEmail(Email.of(principal));
case MOBILE_PHONE -> accountRepository.findByMobilePhone(MobilePhone.of(principal));
default -> throw InvalidInputException.unsupportedPrincipalTypeException("输入邮箱地址或手机号");
};
Assert.notNull(account, () -> AccountLoginException.accountNotExistException());
mailAndSmsVerifyService.checkOtp(principal, command.getOtp());
}
}

View File

@@ -5,7 +5,7 @@ import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xyz.zhouxy.plusone.exception.InvalidInputException;
import cn.hutool.core.lang.Assert;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
import xyz.zhouxy.plusone.system.application.exception.AccountLoginException;
@@ -17,8 +17,8 @@ import xyz.zhouxy.plusone.system.domain.model.account.Account;
import xyz.zhouxy.plusone.system.domain.model.account.AccountRepository;
import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
import xyz.zhouxy.plusone.system.domain.model.account.Principal;
import xyz.zhouxy.plusone.system.domain.model.account.Username;
import xyz.zhouxy.plusone.validator.InvalidInputException;
import xyz.zhouxy.plusone.validator.ValidateDto;
/**
@@ -43,62 +43,45 @@ public class AdminLoginService {
@ValidateDto
public LoginInfoViewObject loginByPassword(LoginByPasswordCommand command) {
Principal principal = PrincipalUtil.getPrincipal(command.getPrincipal());
Account account;
if (principal instanceof Email) {
account = accountRepository.findByEmail((Email) principal);
} else if (principal instanceof MobilePhone) {
account = accountRepository.findByMobilePhone((MobilePhone) principal);
} else {
account = accountRepository.findByUsername((Username) principal);
}
var principal = command.getPrincipal();
Account account = switch (command.getPrincipalType()) {
case USERNAME -> accountRepository.findByUsername(Username.of(principal));
case EMAIL -> accountRepository.findByEmail(Email.of(principal));
case MOBILE_PHONE -> accountRepository.findByMobilePhone(MobilePhone.of(principal));
};
Assert.notNull(account, () -> AccountLoginException.accountNotExistException());
var isPasswordCorrect = account.checkPassword(command.getPassword());
Assert.isTrue(isPasswordCorrect, () -> AccountLoginException.passwordErrorException());
if (account == null) {
throw AccountLoginException.accountNotExistException();
}
@SuppressWarnings("null")
boolean isPasswordCorrect = account.checkPassword(command.getPassword());
if (!isPasswordCorrect) {
throw AccountLoginException.passwordErrorException();
}
adminAuthLogic.login(account.getId().orElseThrow(), command.isRememberMe());
var accountDetails = accountQueries.queryAccountDetails(account.getId().orElseThrow());
return LoginInfoViewObject.of(adminAuthLogic.getTokenValue(), accountDetails);
}
@ValidateDto
public LoginInfoViewObject loginByOtp(LoginByOtpCommand command) {
String principal = command.getPrincipal();
PrincipalType principalType = PrincipalUtil.getPrincipalType(principal);
String otp = command.getOtp();
boolean rememberMe = command.isRememberMe();
var principal = command.getPrincipal();
Account account = switch (command.getPrincipalType()) {
case EMAIL -> accountRepository.findByEmail(Email.of(principal));
case MOBILE_PHONE -> accountRepository.findByMobilePhone(MobilePhone.of(principal));
default -> throw InvalidInputException.unsupportedPrincipalTypeException("输入邮箱地址或手机号");
};
Assert.notNull(account, () -> AccountLoginException.accountNotExistException());
Account account;
if (principalType == PrincipalType.EMAIL) {
account = accountRepository.findByEmail(Email.of(principal));
} else if (principalType == PrincipalType.MOBILE_PHONE) {
account = accountRepository.findByMobilePhone(MobilePhone.of(principal));
} else {
throw InvalidInputException.unsupportedPrincipalTypeException("输入邮箱地址或手机号");
}
mailAndSmsVerifyService.checkOtp(principal, command.getOtp());
if (account == null) {
throw AccountLoginException.accountNotExistException();
}
mailAndSmsVerifyService.checkOtp(principal, otp);
adminAuthLogic.login(account.getId().orElseThrow(), rememberMe);
adminAuthLogic.login(account.getId().orElseThrow(), command.isRememberMe());
var accountDetails = accountQueries.queryAccountDetails(account.getId().orElseThrow());
return LoginInfoViewObject.of(adminAuthLogic.getTokenValue(), accountDetails);
}
public void sendOtp(String principal) {
Principal emailOrMobilePhone = PrincipalUtil.getEmailOrMobilePhone(principal);
if (emailOrMobilePhone instanceof Email) {
mailAndSmsVerifyService.sendOtpToEmail((Email) emailOrMobilePhone);
PrincipalType principalType = PrincipalUtil.getPrincipalType(principal);
if (principalType == PrincipalType.EMAIL) {
mailAndSmsVerifyService.sendOtpToEmail(Email.of(principal));
} else {
mailAndSmsVerifyService.sendOtpToMobilePhone((MobilePhone) emailOrMobilePhone);
mailAndSmsVerifyService.sendOtpToMobilePhone(MobilePhone.of(principal));
}
}
}

View File

@@ -1,19 +0,0 @@
package xyz.zhouxy.plusone.system.application.service;
import static xyz.zhouxy.plusone.system.constant.AuthLogic.adminAuthLogic;
import org.springframework.stereotype.Service;
/**
* Admin 账号登出
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
@Service
public class AdminLogoutService {
public void execute() {
adminAuthLogic.checkLogin();
adminAuthLogic.logout();
}
}

View File

@@ -5,7 +5,6 @@ import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xyz.zhouxy.plusone.exception.InvalidInputException;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalUtil;
import xyz.zhouxy.plusone.system.application.exception.AccountRegisterException;
@@ -18,6 +17,7 @@ import xyz.zhouxy.plusone.system.domain.model.account.Email;
import xyz.zhouxy.plusone.system.domain.model.account.MobilePhone;
import xyz.zhouxy.plusone.system.domain.model.account.Password;
import xyz.zhouxy.plusone.system.domain.model.account.Username;
import xyz.zhouxy.plusone.validator.InvalidInputException;
/**
* 注册账号服务

View File

@@ -0,0 +1,11 @@
package xyz.zhouxy.plusone.system.application.service.command;
import lombok.Data;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
@Data
public class ChangePasswordByOtpCommand {
String account;
String otp;
PrincipalType principalType;
}

View File

@@ -2,6 +2,7 @@ package xyz.zhouxy.plusone.system.application.service.command;
import lombok.Data;
import xyz.zhouxy.plusone.domain.ICommand;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
/**
* 登录命令
@@ -14,6 +15,7 @@ public class LoginByOtpCommand implements ICommand {
String principal; // 邮箱地址 / 手机号
String otp; // 密码
boolean rememberMe; // 记住我
PrincipalType principalType;
// 进入登陆界面时或刷新验证码时,前端发送图形验证码的请求,后端生成 captcha 并暂存到 redis 中key 为 UUID将图形和 uuid 响应给前端。
// String uuid; // 校验码的 key

View File

@@ -2,6 +2,7 @@ package xyz.zhouxy.plusone.system.application.service.command;
import lombok.Data;
import xyz.zhouxy.plusone.domain.ICommand;
import xyz.zhouxy.plusone.system.application.common.util.PrincipalType;
/**
* 登录命令
@@ -14,6 +15,7 @@ public class LoginByPasswordCommand implements ICommand {
String principal; // 用户名 / 邮箱地址 / 手机号
String password; // 密码
boolean rememberMe; // 记住我
PrincipalType principalType;
// 进入登陆界面时或刷新验证码时,前端发送图形验证码的请求,后端生成 captcha 并暂存到 redis 中key 为 UUID将图形和 uuid 响应给前端。
// String uuid; // 校验码的 key