Compare commits

...

10 Commits

95 changed files with 2185 additions and 186 deletions

13
.idea/encodings.xml generated
View File

@@ -3,8 +3,16 @@
<component name="Encoding">
<file url="file://$PROJECT_DIR$/aioj-backend-ai-service/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-ai-service/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-auth/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-client/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-client/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/aioj-backend-common-bom/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/aioj-backend-common-bom/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/aioj-backend-common-core/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/aioj-backend-common-log/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/aioj-backend-common-mybatis/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/aioj-backend-common-starter/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/aioj-backend-common-starter/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-gateway/src/main/java" charset="UTF-8" />
@@ -15,6 +23,11 @@
<file url="file://$PROJECT_DIR$/aioj-backend-model/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-question-service/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-question-service/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-upms/aioj-backend-upms-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-upms/aioj-backend-upms-biz/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-upms/aioj-upms-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-upms/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-upms/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-user-service/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/aioj-backend-user-service/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />

8
.idea/misc.xml generated
View File

@@ -1,13 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="KubernetesApiProvider"><![CDATA[{}]]></component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
<option name="ignoredFiles">
<set>
<option value="$PROJECT_DIR$/aioj-backend-client/pom.xml" />
<option value="$PROJECT_DIR$/aioj-backend-model/pom.xml" />
<option value="$PROJECT_DIR$/aioj-backend-upms/aioj-upms-api/pom.xml" />
</set>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="zulu-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />

106
aioj-backend-auth/pom.xml Normal file
View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.meowrain</groupId>
<artifactId>ai-oj</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>aioj-backend-auth</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- spring cloud发现服务-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- OAuth2 Client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.13.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.13.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.13.0</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<!--
引用通用模块
-->
<dependency>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!--引入openfeign-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>4.3.0</version>
</dependency>
<!-- 引入redis存储refreshToken-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,13 @@
package cn.meowrain.aioj.backend.auth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients(basePackages = "cn.meowrain.aioj.backend.auth.clients")
@SpringBootApplication
public class AIOJAuthApplication {
public static void main(String[] args) {
SpringApplication.run(AIOJAuthApplication.class, args);
}
}

View File

@@ -0,0 +1,16 @@
package cn.meowrain.aioj.backend.auth.clients;
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.framework.core.web.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "user-service", path = "/api/v1/user")
public interface UserClient {
@GetMapping("/inner/get-by-username")
Result<UserAuthRespDTO> getUserByUserName(@RequestParam("userAccount") String userAccount);
@GetMapping("/inner/get-by-userid")
public Result<UserAuthRespDTO> getUserById(@RequestParam("userId") String userid);
}

View File

@@ -0,0 +1,5 @@
package cn.meowrain.aioj.backend.auth.common.constants;
public class RedisKeyConstants {
public static String REFRESH_TOKEN_KEY_PREFIX = "refresh_token:%s";
}

View File

@@ -0,0 +1,20 @@
package cn.meowrain.aioj.backend.auth.common.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum ChainMarkEnums {
/**
* 用户登录请求验证
*/
USER_LOGIN_REQ_PARAM_VERIFY("USER_LOGIN_REQ_PARAM_VERIFY");
@Getter
private final String markName;
@Override
public String toString() {
return markName;
}
}

View File

@@ -0,0 +1,47 @@
package cn.meowrain.aioj.backend.auth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/v1/auth/**",
"/doc.html",
"/swagger-ui/**",
"/swagger-resources/**",
"/webjars/**",
"/v3/api-docs/**",
"/favicon.ico"
)
.permitAll()
.anyRequest().authenticated());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
}

View File

@@ -0,0 +1,38 @@
package cn.meowrain.aioj.backend.auth.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
@EnableKnife4j
public class SwaggerConfiguration implements ApplicationRunner {
@Value("${server.port:8080}")
private String serverPort;
@Value("${server.servlet.context-path:}")
private String contextPath;
@Bean
public OpenAPI customerOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("AIOJ-renz微服务✨")
.description("用户认证功能")
.version("v1.0.0")
.contact(new Contact().name("meowrain").email("meowrain@126.com"))
.license(new License().name("MeowRain").url("https://meowrain.cn")));
}
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("✨API Document: http://127.0.0.1:{}{}/doc.html", serverPort, contextPath);
}
}

View File

@@ -0,0 +1,25 @@
package cn.meowrain.aioj.backend.auth.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@Data
@ConfigurationProperties(value = JwtPropertiesConfiguration.PREFIX)
public class JwtPropertiesConfiguration {
public static final String PREFIX = "jwt";
/**
* JWT 密钥(必须 32 字节以上)
*/
private String secret;
/**
* 过期时间(单位:毫秒)
*/
private long accessExpire; // access token TTL
/**
* 刷新令牌时间
*/
private long refreshExpire; // refresh token TTL
}

View File

@@ -0,0 +1,37 @@
package cn.meowrain.aioj.backend.auth.controller;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.auth.service.AuthService;
import cn.meowrain.aioj.backend.framework.web.Results;
import cn.meowrain.aioj.backend.framework.web.Result;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/auth")
public class AuthController {
private final AuthService authService;
@PostMapping("/login")
public Result<UserLoginResponseDTO> login(@RequestBody UserLoginRequestDTO userLoginRequest) {
UserLoginResponseDTO userLoginResponse = authService.userLogin(userLoginRequest);
return Results.success(userLoginResponse);
}
@PostMapping("/refresh")
public Result<UserLoginResponseDTO> refresh(@RequestParam String refreshToken) {
return Results.success(authService.refreshToken(refreshToken));
}
@PostMapping("/auth")
public Result<String> auth(@RequestBody UserLoginRequestDTO userLoginRequest) {
UserLoginResponseDTO userLoginResponseDTO = authService.userLogin(userLoginRequest);
return Results.success(userLoginResponseDTO.getAccessToken());
}
}

View File

@@ -0,0 +1,37 @@
package cn.meowrain.aioj.backend.auth.dto.chains;
import cn.meowrain.aioj.backend.auth.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.framework.designpattern.chains.AbstractChianHandler;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.exception.ClientException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class UserLoginRequestParamVerifyChain implements AbstractChianHandler<UserLoginRequestDTO> {
@Override
public void handle(UserLoginRequestDTO requestParam) {
if (StringUtils.isAnyBlank(requestParam.getUserAccount(), requestParam.getUserPassword())) {
throw new ClientException("参数为空", ErrorCode.PARAMS_ERROR);
}
if (requestParam.getUserAccount().length() < 4) {
throw new ClientException("账号长度不小于4位", ErrorCode.PARAMS_ERROR);
}
if (requestParam.getUserPassword().length() < 8) {
throw new ClientException("密码长度不小于8位", ErrorCode.PARAMS_ERROR);
}
}
@Override
public String mark() {
return ChainMarkEnums.USER_LOGIN_REQ_PARAM_VERIFY.getMarkName();
}
@Override
public int getOrder() {
return 10;
}
}

View File

@@ -0,0 +1,9 @@
package cn.meowrain.aioj.backend.auth.dto.chains.context;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.framework.core.designpattern.chains.CommonChainContext;
import org.springframework.stereotype.Component;
@Component
public class UserLoginRequestParamVerifyContext extends CommonChainContext<UserLoginRequestDTO> {
}

View File

@@ -0,0 +1,9 @@
package cn.meowrain.aioj.backend.auth.dto.req;
import lombok.Data;
@Data
public class UserLoginRequestDTO {
private String userAccount;
private String userPassword;
}

View File

@@ -0,0 +1,68 @@
package cn.meowrain.aioj.backend.auth.dto.resp;
import lombok.Data;
import java.util.Date;
/**
* 用户认证响应体
*/
@Data
public class UserAuthRespDTO {
/**
* id
*/
private Long id;
/**
* 用户账号
*/
private String userAccount;
/**
* 用户密码
*/
private String userPassword;
/**
* 开放平台id
*/
private String unionId;
/**
* 公众号openId
*/
private String mpOpenId;
/**
* 用户昵称
*/
private String userName;
/**
* 用户头像
*/
private String userAvatar;
/**
* 用户简介
*/
private String userProfile;
/**
* 用户角色user/admin/ban
*/
private String userRole;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@@ -0,0 +1,30 @@
package cn.meowrain.aioj.backend.auth.dto.resp;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class UserLoginResponseDTO implements Serializable {
/**
* id
*/
private Long id;
/**
* 用户账号
*/
private String userAccount;
/**
* 开放平台id
*/
private String unionId;
private String accessToken;
private String refreshToken;
private Long expire;
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,10 @@
package cn.meowrain.aioj.backend.auth.filter;
import org.springframework.stereotype.Component;
/**
* JWT拦截器
*/
@Component
public class JwtAuthenticationFilter {
}

View File

@@ -0,0 +1,20 @@
package cn.meowrain.aioj.backend.auth.service;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
public interface AuthService {
/**
* 用户登录
* @param request {@link UserLoginRequestDTO}
* @return {@link UserLoginResponseDTO}
*/
UserLoginResponseDTO userLogin(UserLoginRequestDTO request);
/**
* 刷新token
* @param refreshToken
* @return
*/
UserLoginResponseDTO refreshToken(String refreshToken);
}

View File

@@ -0,0 +1,106 @@
package cn.meowrain.aioj.backend.auth.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.meowrain.aioj.backend.auth.clients.UserClient;
import cn.meowrain.aioj.backend.auth.common.constants.RedisKeyConstants;
import cn.meowrain.aioj.backend.auth.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.auth.config.properties.JwtPropertiesConfiguration;
import cn.meowrain.aioj.backend.auth.dto.chains.context.UserLoginRequestParamVerifyContext;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.auth.service.AuthService;
import cn.meowrain.aioj.backend.auth.utils.JwtUtil;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.exception.ServiceException;
import cn.meowrain.aioj.backend.framework.core.web.Result;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
@RequiredArgsConstructor
@Slf4j
public class AuthServiceImpl implements AuthService {
private final JwtUtil jwtUtil;
private final UserLoginRequestParamVerifyContext userLoginRequestParamVerifyContext;
private final UserClient userClient;
private final StringRedisTemplate stringRedisTemplate;
private final JwtPropertiesConfiguration jwtPropertiesConfiguration;
@Override
public UserLoginResponseDTO userLogin(UserLoginRequestDTO requestParam) {
// 1.校验
userLoginRequestParamVerifyContext.handler(ChainMarkEnums.USER_LOGIN_REQ_PARAM_VERIFY.getMarkName(),
requestParam);
// 如果调用user-service失败那么就说明是系统内部错误
Result<UserAuthRespDTO> userResp = userClient.getUserByUserName(requestParam.getUserAccount());
if (userResp.isFail()) {
log.error("调用user-service返回失败{}", userResp.getMessage());
throw new ServiceException(ErrorCode.SYSTEM_ERROR);
}
UserAuthRespDTO user = userResp.getData();
if (ObjectUtil.isNull(user)
|| !BCrypt.checkpw(requestParam.getUserPassword(), user.getUserPassword())) {
throw new ServiceException("用户不存在或者密码错误", ErrorCode.NOT_LOGIN_ERROR);
}
// 生成 JWT
String accessToken = jwtUtil.generateAccessToken(user);
String refreshToken = jwtUtil.generateRefreshToken(user.getId());
UserLoginResponseDTO resp = new UserLoginResponseDTO();
resp.setId(user.getId());
resp.setUserAccount(user.getUserAccount());
resp.setAccessToken(accessToken);
resp.setRefreshToken(refreshToken);
// refresh token存入到REDIS里面
stringRedisTemplate.opsForValue().set(
String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, user.getId()),
refreshToken,
jwtPropertiesConfiguration.getRefreshExpire(),
TimeUnit.MILLISECONDS);
return resp;
}
/**
* 更新access token使用refresh token
* @param refreshToken
* @return
*/
@Override
public UserLoginResponseDTO refreshToken(String refreshToken) {
UserLoginResponseDTO userLoginResponseDTO = new UserLoginResponseDTO();
if (!jwtUtil.isTokenValid(refreshToken)) {
throw new RuntimeException("Refresh Token 已过期");
}
Long userId = Long.valueOf(jwtUtil.parseClaims(refreshToken).getSubject());
String cacheKey = String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, userId);
String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
if (cacheValue == null || !cacheValue.equals(refreshToken)) {
throw new RuntimeException("Refresh Token 已失效");
}
// 再次签发新的 Access Token
// 此处你需要查用户,拿 userName, role
Result<UserAuthRespDTO> userResult = userClient.getUserById(String.valueOf(userId));
if (userResult.isFail()) {
log.error("通过id查找用户失败:{}", userResult.getMessage());
throw new ServiceException(ErrorCode.SYSTEM_ERROR);
}
UserAuthRespDTO user = userResult.getData();
String newAccessToken = jwtUtil.generateAccessToken(user);
//设置refresh token和access token
userLoginResponseDTO.setRefreshToken(refreshToken);
userLoginResponseDTO.setAccessToken(newAccessToken);
return userLoginResponseDTO;
}
}

View File

@@ -0,0 +1,74 @@
package cn.meowrain.aioj.backend.auth.utils;
import cn.meowrain.aioj.backend.auth.config.properties.JwtPropertiesConfiguration;
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@RequiredArgsConstructor
@Component
public class JwtUtil {
private final JwtPropertiesConfiguration jwtConfig;
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes());
}
/** 生成 Access Token */
public String generateAccessToken(UserAuthRespDTO user) {
long now = System.currentTimeMillis();
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("userName", user.getUserName());
claims.put("role", user.getUserRole());
return Jwts.builder()
.subject(user.getUserAccount())
.issuedAt(new Date(now))
.expiration(new Date(now + jwtConfig.getAccessExpire()))
.claims(claims)
.signWith(getSigningKey(), Jwts.SIG.HS256)
.compact();
}
/** 生成 Refresh Token只含 userId */
public String generateRefreshToken(Long userId) {
long now = System.currentTimeMillis();
return Jwts.builder()
.subject(String.valueOf(userId))
.issuedAt(new Date(now))
.expiration(new Date(now + jwtConfig.getRefreshExpire()))
.signWith(getSigningKey(), Jwts.SIG.HS256)
.compact();
}
/** 解析 Token */
public Claims parseClaims(String token) {
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
/** 校验 Token 是否过期 */
public boolean isTokenValid(String token) {
try {
Claims claims = parseClaims(token);
return claims.getExpiration().after(new Date());
} catch (Exception ignored) {
return false;
}
}
}

View File

@@ -0,0 +1,16 @@
spring:
application:
name: auth-service
data:
redis:
host: 10.0.0.10
port: 6379
password: 123456
cloud:
nacos:
discovery:
enabled: true
register-enabled: true
server-addr: 10.0.0.10:8848
username: nacos
password: nacos

View File

@@ -0,0 +1,38 @@
spring:
application:
name: auth-service
profiles:
active: @env@
devtools:
livereload:
enabled: true
server:
port: 10011
servlet:
context-path: /api
springdoc:
api-docs:
enabled: true
path: /v3/api-docs
default-flat-param-object: true
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
group-configs:
- group: 'default'
paths-to-match: '/**'
packages-to-scan: cn.meowrain.aioj.backend.userservice.controller
knife4j:
basic:
enable: true
setting:
language: zh_cn
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:/mapper/**/*.xml
jwt:
secret: "12345678901234567890123456789012" # 至少32字节
access-expire: 900000 # 24小时
refresh-expire: 604800000 # 7天

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<artifactId>aioj-backend-common-bom</artifactId>
<groupId>cn.meowrain.aioj</groupId>
<packaging>pom</packaging>
<version>${revision}</version>
<name>aioj-common-bom</name>
<description>依赖管理</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<revision>1.0.0</revision>
<mybatis-plus.version>3.5.14</mybatis-plus.version>
<spring-boot.version>3.5.7</spring-boot.version>
<spring-cloud-alibaba.version>2025.0.0.0</spring-cloud-alibaba.version>
<mysql.version>9.5.0</mysql.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-bom -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-bom</artifactId>
<version>5.8.41</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--orm 相关 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-bom</artifactId>
<version>${mybatis-plus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- https://github.com/alibaba/easyexcel -->
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<!-- OAuth2 Client -->
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-oauth2-client -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-oauth2-client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-test -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>6.5.7</version>
<scope>test</scope>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>aioj-backend-common-core</artifactId>
<packaging>jar</packaging>
<description>aioj 公共工具类核心包</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,4 +1,4 @@
package cn.meowrain.aioj.backend.framework.banner;
package cn.meowrain.aioj.backend.framework.core.banner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;

View File

@@ -1,6 +1,7 @@
package cn.meowrain.aioj.backend.framework.config;
package cn.meowrain.aioj.backend.framework.core.config;
import cn.meowrain.aioj.backend.framework.exception.handler.GlobalExceptionHandler;
import cn.meowrain.aioj.backend.framework.core.exception.handler.GlobalExceptionHandler;
import org.springframework.context.annotation.Bean;
/**

View File

@@ -1,4 +1,4 @@
package cn.meowrain.aioj.backend.framework.designpattern.chains;
package cn.meowrain.aioj.backend.framework.core.designpattern.chains;
import org.springframework.core.Ordered;

View File

@@ -1,4 +1,4 @@
package cn.meowrain.aioj.backend.framework.designpattern.chains;
package cn.meowrain.aioj.backend.framework.core.designpattern.chains;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;

View File

@@ -0,0 +1,17 @@
package cn.meowrain.aioj.backend.framework.core.enums;
/**
* 删除枚举
*/
public enum DelStatusEnum {
STATUS_NORMAL("0"),
STATUS_DELETE("1");
private final String code;
DelStatusEnum(String code) {
this.code = code;
}
public String code() {
return this.code;
}
}

View File

@@ -1,4 +1,4 @@
package cn.meowrain.aioj.backend.framework.errorcode;
package cn.meowrain.aioj.backend.framework.core.errorcode;
public enum ErrorCode implements IErrorCode {
SUCCESS("0", "ok"),

View File

@@ -1,4 +1,4 @@
package cn.meowrain.aioj.backend.framework.errorcode;
package cn.meowrain.aioj.backend.framework.core.errorcode;
public interface IErrorCode {
String code();

View File

@@ -1,6 +1,6 @@
package cn.meowrain.aioj.backend.framework.exception;
package cn.meowrain.aioj.backend.framework.core.exception;
import cn.meowrain.aioj.backend.framework.errorcode.IErrorCode;
import cn.meowrain.aioj.backend.framework.core.errorcode.IErrorCode;
import lombok.Getter;
import org.springframework.util.StringUtils;

View File

@@ -1,7 +1,7 @@
package cn.meowrain.aioj.backend.framework.exception;
package cn.meowrain.aioj.backend.framework.core.exception;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.errorcode.IErrorCode;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.errorcode.IErrorCode;
import lombok.ToString;
/**

View File

@@ -1,7 +1,7 @@
package cn.meowrain.aioj.backend.framework.exception;
package cn.meowrain.aioj.backend.framework.core.exception;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.errorcode.IErrorCode;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.errorcode.IErrorCode;
import lombok.ToString;
/**

View File

@@ -1,7 +1,7 @@
package cn.meowrain.aioj.backend.framework.exception;
package cn.meowrain.aioj.backend.framework.core.exception;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.errorcode.IErrorCode;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.errorcode.IErrorCode;
import lombok.ToString;
/**

View File

@@ -1,10 +1,9 @@
package cn.meowrain.aioj.backend.framework.exception.handler;
package cn.meowrain.aioj.backend.framework.core.exception.handler;
import cn.meowrain.aioj.backend.framework.exception.AbstractException;
import cn.meowrain.aioj.backend.framework.web.Result;
import cn.meowrain.aioj.backend.framework.web.Results;
import cn.meowrain.aioj.backend.framework.core.exception.AbstractException;
import cn.meowrain.aioj.backend.framework.core.web.Result;
import cn.meowrain.aioj.backend.framework.core.web.Results;
import jakarta.servlet.http.HttpServletRequest;
import lombok.experimental.StandardException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;

View File

@@ -1,4 +1,4 @@
package cn.meowrain.aioj.backend.framework.web;
package cn.meowrain.aioj.backend.framework.core.web;
import lombok.Data;
import lombok.experimental.Accessors;

View File

@@ -1,7 +1,8 @@
package cn.meowrain.aioj.backend.framework.web;
package cn.meowrain.aioj.backend.framework.core.web;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.exception.AbstractException;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.exception.AbstractException;
import java.util.Optional;

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>aioj-backend-common-log</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,32 @@
package cn.meowrain.aioj.backend.framework.log;
import cn.meowrain.aioj.backend.framework.log.aspect.SysLogAspect;
import cn.meowrain.aioj.backend.framework.log.config.AIOJLogPropertiesConfiguration;
import cn.meowrain.aioj.backend.framework.log.event.SysLogListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AIOJLogPropertiesConfiguration.class)
@ConditionalOnProperty(value = "aioj.log.enabled", matchIfMissing = true)
public class LogAutoConfiguration {
/**
* 创建并返回SysLogListener的Bean实例
*/
@Bean
public SysLogListener sysLogListener(AIOJLogPropertiesConfiguration logProperties, RemoteLogService remoteLogService) {
return new SysLogListener(remoteLogService, logProperties);
}
/**
* 返回切面类实例
* @return {@link SysLogAspect}
*/
@Bean
public SysLogAspect sysLogAspect() {
return new SysLogAspect();
}
}

View File

@@ -0,0 +1,23 @@
package cn.meowrain.aioj.backend.framework.log.annotation;
import java.lang.annotation.*;
/**
* 系统日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
/**
* 描述
* @return {@link String}
*/
String value() default "";
/**
* Spel表达式
* @return 日志描述
*/
String expression() default "";
}

View File

@@ -0,0 +1,31 @@
package cn.meowrain.aioj.backend.framework.log.aspect;
import cn.hutool.core.util.StrUtil;
import cn.meowrain.aioj.backend.framework.log.annotation.SysLog;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;
@Aspect
@Slf4j
@RequiredArgsConstructor
public class SysLogAspect {
@Around("@annotation(sysLog)")
public Object around(ProceedingJoinPoint joinPoint,SysLog sysLog) throws Throwable {
String strClassName = joinPoint.getTarget().getClass().getName();
String strMethodName = joinPoint.getSignature().getName();
log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);
String value = sysLog.value();
String expression = sysLog.expression();
if(StrUtil.isNotBlank(expression)) {
// 解析SPEL
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
EvaluationContext context = SysLogUtils.getContext(point.getArgs(), signature.getMethod());
}
}
}

View File

@@ -0,0 +1,21 @@
package cn.meowrain.aioj.backend.framework.log.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@Setter
@ConfigurationProperties(AIOJLogPropertiesConfiguration.PREFIX)
public class AIOJLogPropertiesConfiguration {
public static final String PREFIX = "aioj.log";
/**
* 开启日志记录
*/
private boolean enabled = true;
/**
* 请求报文最大存储长度
*/
private Integer maxLength = 20000;
}

View File

@@ -0,0 +1,92 @@
package cn.meowrain.aioj.backend.framework.log.entity;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 日志对象
*/
@Data
public class SysLog implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 编号
*/
private Long id;
/**
* 日志类型
*/
private String logType;
/**
* 日志标题
*/
private String title;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 操作IP地址
*/
private String remoteAddr;
/**
* 用户代理
*/
private String userAgent;
/**
* 请求URI
*/
private String requestUri;
/**
* 操作方式
*/
private String method;
/**
* 操作提交的数据
*/
private String params;
/**
* 执行时间
*/
private Long time;
/**
* 异常信息
*/
private String exception;
/**
* 服务ID
*/
private String serviceId;
/**
* 删除标记
*/
private String delFlag;
}

View File

@@ -0,0 +1,33 @@
package cn.meowrain.aioj.backend.framework.log.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 日志类型枚举
*/
@Getter
@RequiredArgsConstructor
public enum LogTypeEnum {
/**
* 正常日志类型
*/
NORMAL("0", "正常日志"),
/**
* 错误日志类型
*/
ERROR("9", "错误日志");
/**
* 类型
*/
private final String type;
/**
* 描述
*/
private final String description;
}

View File

@@ -0,0 +1,20 @@
package cn.meowrain.aioj.backend.framework.log.event;
import cn.meowrain.aioj.backend.framework.log.annotation.SysLog;
import org.springframework.context.ApplicationEvent;
import java.io.Serial;
/**
* 系统日志事件类
*/
public class SysLogEvent extends ApplicationEvent {
@Serial
private static final long serialVersionUID = 1L;
/**
* 构造方法根据源SysLog对象创建SysLogEvent
*/
public SysLogEvent(SysLog source) {
super(source);
}
}

View File

@@ -0,0 +1,24 @@
package cn.meowrain.aioj.backend.framework.log.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
/**
* 系统
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class SysLogEventSource extends SysLogEvent implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 参数重写成object
*/
private Object body;
}

View File

@@ -0,0 +1,36 @@
package cn.meowrain.aioj.backend.framework.log.event;
import cn.meowrain.aioj.backend.framework.log.config.AIOJLogPropertiesConfiguration;
import cn.meowrain.aioj.backend.framework.log.entity.SysLog;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
@RequiredArgsConstructor
public class SysLogListener implements InitializingBean {
private final static ObjectMapper objectMapper = new ObjectMapper();
private final RemoteLogService remoteLogService;
private final AIOJLogPropertiesConfiguration logProperties;
@SneakyThrows
@Async
@Order
@EventListener(SysLogEvent.class)
public void saveLog(SysLogEvent sysLogEvent){
SysLogEventSource source = (SysLogEventSource) sysLogEvent.getSource();
SysLog sysLog = new SysLog();
BeanUtils.copyProperties(source, sysLog);
}
@Override
public void afterPropertiesSet() throws Exception {
}
}

View File

@@ -0,0 +1,18 @@
package cn.meowrain.aioj.backend.framework.log.init;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
public class ApplicationLoggerInitializer implements EnvironmentPostProcessor, Ordered {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}

View File

@@ -0,0 +1,9 @@
package cn.meowrain.aioj.backend.framework.log.utils;
import cn.meowrain.aioj.backend.framework.log.event.SysLogEventSource;
public final class SysLogUtils {
public static SysLogEventSource getSysLog() {
}
}

View File

@@ -0,0 +1 @@
cn.meowrain.aioj.backend.framework.log.LogAutoConfiguration

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>aioj-backend-common-mybatis</artifactId>
<packaging>jar</packaging>
<description>aioj mybatis 封装</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,27 @@
package cn.meowrain.backend.common.mybaits;
import cn.meowrain.backend.common.mybaits.config.MybatisPlusMetaObjectHandler;
import cn.meowrain.backend.common.mybaits.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MybatisPlusAutoConfiguration {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInterceptor());
return interceptor;
}
/**
* 创建并返回MybatisPlusMetaObjectHandler实例用于审计字段自动填充
*/
@Bean
public MybatisPlusMetaObjectHandler mybatisPlusMetaObjectHandler() {
return new MybatisPlusMetaObjectHandler();
}
}

View File

@@ -0,0 +1,48 @@
package cn.meowrain.backend.common.mybaits.base;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 基础实体抽象类,包含通用实体字段
*/
@Getter
@Setter
public class BaseEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 创建者
*/
@Schema(description = "创建人")
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新者
*/
@Schema(description = "更新人")
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 更新时间
*/
@Schema(description = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,70 @@
package cn.meowrain.backend.common.mybaits.config;
import cn.meowrain.aioj.backend.framework.core.enums.DelStatusEnum;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.ClassUtils;
import java.time.LocalDateTime;
import java.util.Optional;
/**
* MybatisPlus 自动填充处理器,用于实体类字段的自动填充
*/
@Slf4j
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.debug("mybatis plus start insert fill ....");
LocalDateTime now = LocalDateTime.now();
fillValIfNullByName("createTime", now, metaObject, true);
fillValIfNullByName("updateTime", now, metaObject, true);
fillValIfNullByName("createBy", getUserName(), metaObject, true);
fillValIfNullByName("updateBy", getUserName(), metaObject, true);
// 删除标记自动填充
fillValIfNullByName("delFlag", DelStatusEnum.STATUS_NORMAL.code(), metaObject, true);
}
@Override
public void updateFill(MetaObject metaObject) {
log.debug("mybatis plus start update fill ....");
fillValIfNullByName("updateTime", LocalDateTime.now(), metaObject, true);
fillValIfNullByName("updateBy", getUserName(), metaObject, true);
}
private void fillValIfNullByName(String fieldName, Object fieldVal, MetaObject metaObject, boolean isCover) {
//如果填充值为空
if (fieldVal == null) {
return;
}
// 没有 set 方法
if (!metaObject.hasSetter(fieldName)) {
return;
}
// field 类型相同时设置
Class<?> getterType = metaObject.getGetterType(fieldName);
if (ClassUtils.isAssignableValue(getterType, fieldVal)) {
metaObject.setValue(fieldName, fieldVal);
}
}
private Object getUserName() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 匿名接口直接返回
if (authentication instanceof AnonymousAuthenticationToken) {
return null;
}
if (Optional.ofNullable(authentication).isPresent()) {
return authentication.getName();
}
return null;
}
}

View File

@@ -0,0 +1,62 @@
package cn.meowrain.backend.common.mybaits.plugins;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ParameterUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
/**
* * 分页拦截器实现类,用于处理分页查询逻辑
* * <p>
* * 当分页大小小于0时自动设置为0防止全表查询
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class PaginationInterceptor extends PaginationInnerInterceptor {
/**
* 数据库类型
* <p>
* 查看 {@link #findIDialect(Executor)} 逻辑
*/
private DbType dbType;
/**
* 方言实现类
* <p>
* 查看 {@link #findIDialect(Executor)} 逻辑
*/
private IDialect dialect;
public PaginationInterceptor(DbType dbType) {
this.dbType = dbType;
}
public PaginationInterceptor(IDialect dialect) {
this.dialect = dialect;
}
/**
* 在执行查询前处理分页参数
*/
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
// size 小于 0 直接设置为 0 , 即不查询任何数据
if (null != page && page.getSize() < 0) {
page.setSize(0);
}
super.beforeQuery(executor, ms, page, rowBounds, resultHandler, boundSql);
}
}

View File

@@ -0,0 +1 @@
cn.meowrain.backend.common.mybaits.MybatisPlusAutoConfiguration

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<packaging>pom</packaging>
<artifactId>aioj-backend-common-starter</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common-log</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@@ -10,10 +10,30 @@
</parent>
<artifactId>aioj-backend-common</artifactId>
<packaging>pom</packaging>
<modules>
<module>aioj-backend-common-log</module>
<module>aioj-backend-common-core</module>
<module>aioj-backend-common-starter</module>
<module>aioj-backend-common-mybatis</module>
<module>aioj-backend-common-bom</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.meowrain.aioj</groupId>
<artifactId>aioj-backend-common-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -1,12 +0,0 @@
package cn.meowrain.aioj.backend.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
String mustRole() default "";
}

View File

@@ -1 +0,0 @@
cn.meowrain.aioj.backend.framework.config.WebAutoConfiguration

View File

@@ -1,19 +0,0 @@
█████████ █████ ███████ █████
███░░░░░███ ░░███ ███░░░░░███ ░░███
░███ ░███ ░███ ███ ░░███ ░███
░███████████ ░███ ░███ ░███ ░███
░███░░░░░███ ░███ ░███ ░███ ░███
░███ ░███ ░███ ░░███ ███ ███ ░███
█████ █████ █████ ░░░███████░ ░░████████
░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░
🚀 AIOJ - ${spring.application.name}
🌍 Environment : ${spring.profiles.active}
☕ Java Version : ${java.version}
🔥 Started At : ${application.startup.time}
❤️ Service Running: http://${server.address:127.0.0.1}:${server.port}${server.servlet.context-path}
✨API Document: http://127.0.0.1:${server.port}/doc.html

View File

@@ -15,11 +15,56 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud-gateway.version>4.3.2</spring-cloud-gateway.version>
</properties>
<dependencies>
<dependency>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Spring Cloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
<version>4.3.2</version>
</dependency>
<!-- Nacos Discovery (parent 应已引入 alibaba 版本管理) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,14 @@
package cn.meowrain.aioj.backend.gateway;
import cn.meowrain.aioj.backend.gateway.config.GatewayPropertiesConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@EnableConfigurationProperties(value = {GatewayPropertiesConfiguration.class})
@SpringBootApplication
public class AIOJGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(AIOJGatewayApplication.class, args);
}
}

View File

@@ -0,0 +1,14 @@
package cn.meowrain.aioj.backend.gateway.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = GatewayPropertiesConfiguration.PREFIX)
@Data
public class GatewayPropertiesConfiguration {
public static final String PREFIX = "aioj-backend-gateway";
/*
* 白名单放行
* */
private String[] whiteList;
}

View File

@@ -0,0 +1,27 @@
package cn.meowrain.aioj.backend.gateway.filter;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private final WebClient.Builder webClientBuilder;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return null;
}
@Override
public int getOrder() {
return 0;
}
}

View File

@@ -0,0 +1,18 @@
spring:
data:
redis:
host: 10.0.0.10
port: 6379
password: 123456
cloud:
nacos:
discovery:
enabled: true
register-enabled: true
server-addr: 10.0.0.10:8848
username: nacos
password: nacos
logging:
level:
root: INFO

View File

@@ -0,0 +1,18 @@
spring:
data:
redis:
host: 10.0.0.10
port: 6379
password: 123456
cloud:
nacos:
discovery:
enabled: true
register-enabled: true
server-addr: 10.0.0.10:8848
username: nacos
password: nacos
logging:
level:
root: INFO

View File

@@ -0,0 +1,18 @@
spring:
data:
redis:
host: 10.0.0.10
port: 6379
password: 123456
cloud:
nacos:
discovery:
enabled: true
register-enabled: true
server-addr: 10.0.0.10:8848
username: nacos
password: nacos
logging:
level:
root: INFO

View File

@@ -0,0 +1,20 @@
server:
port: 8085
spring:
cloud:
gateway:
server:
webflux:
routes:
- id: auth-service
uri: lb://auth-service
predicates:
- Path=/api/v1/auth/**
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/v1/user/**
aioj:
log:
enabled: true
max-length: 20000

View File

@@ -0,0 +1,15 @@
spring:
application:
name: aioj-gateway
cloud:
nacos:
discovery:
server-addr: 10.0.0.10:8848
username: nacos
password: nacos
config:
server-addr: 10.0.0.10:8848
username: nacos
password: nacos
file-extension: yaml
group: DEFAULT_GROUP

View File

@@ -5,11 +5,11 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.meowrain</groupId>
<artifactId>ai-oj</artifactId>
<artifactId>aioj-backend-upms</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>aioj-backend-client</artifactId>
<artifactId>aioj-backend-upms-api</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>

View File

@@ -0,0 +1,83 @@
package cn.meowrain.aioj.backend.upms.api.dto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Schema(description = "日志查询对象")
public class SysLogDTO {
/**
* 编号
*/
private Long id;
/**
* 日志类型
*/
@NotBlank(message = "日志类型不能为空")
private String logType;
/**
* 日志标题
*/
@NotBlank(message = "日志标题不能为空")
private String title;
/**
* 创建者
*/
private String createBy;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 操作IP地址
*/
private String remoteAddr;
/**
* 用户代理
*/
private String userAgent;
/**
* 请求URI
*/
private String requestUri;
/**
* 操作方式
*/
private String method;
/**
* 操作提交的数据
*/
private String params;
/**
* 执行时间
*/
private Long time;
/**
* 异常信息
*/
private String exception;
/**
* 服务ID
*/
private String serviceId;
/**
* 创建时间区间 [开始时间,结束时间]
*/
private LocalDateTime[] createTime;
}

View File

@@ -5,11 +5,11 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.meowrain</groupId>
<artifactId>ai-oj</artifactId>
<artifactId>aioj-backend-upms</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>aioj-backend-model</artifactId>
<artifactId>aioj-backend-upms-biz</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>

25
aioj-backend-upms/pom.xml Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.meowrain</groupId>
<artifactId>ai-oj</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>aioj-backend-upms</artifactId>
<packaging>pom</packaging>
<modules>
<module>aioj-backend-upms-api</module>
<module>aioj-backend-upms-biz</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -18,15 +18,52 @@
</properties>
<dependencies>
<dependency>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.meowrain</groupId>
<artifactId>aioj-backend-common</artifactId>
<version>1.0-SNAPSHOT</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- 引入cache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,5 +0,0 @@
package cn.meowrain.aioj.backend.userservice.common.constants;
public class UserServiceChainConstants {
protected static final String register_req_verify = "register_req_verify";
}

View File

@@ -4,11 +4,15 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum ChianMarkEnums {
public enum ChainMarkEnums {
/**
* 用户注册请求验证
*/
USER_REGISTER_REQ_PARAM_VERIFY("USER_REGISTER_REQ_PARAM_VERIFY");
USER_REGISTER_REQ_PARAM_VERIFY("USER_REGISTER_REQ_PARAM_VERIFY"),
/**
* 用户登录请求验证
*/
USER_LOGIN_REQ_PARAM_VERIFY("USER_LOGIN_REQ_PARAM_VERIFY");
@Getter
private final String markName;

View File

@@ -6,7 +6,9 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScans({
@ComponentScan("cn.meowrain.aioj.backend.framework.banner")
@ComponentScan("cn.meowrain.aioj.backend.framework.core.banner")
})
public class FrameworkConfiguration {
}

View File

@@ -1,7 +1,37 @@
package cn.meowrain.aioj.backend.userservice.controller;
import org.springframework.web.bind.annotation.RestController;
import cn.meowrain.aioj.backend.framework.core.web.Result;
import cn.meowrain.aioj.backend.framework.core.web.Results;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
import cn.meowrain.aioj.backend.userservice.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.userservice.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController("/user")
@RequiredArgsConstructor
@RestController()
@RequestMapping("/v1/user")
public class UserController {
private final UserService userService;
@PostMapping("/register")
public Result<Long> register(@RequestBody UserRegisterRequestDTO userRegisterRequest) {
Long l = userService.userRegister(userRegisterRequest);
return Results.success(l);
}
@GetMapping("/inner/get-by-username")
public Result<UserAuthRespDTO> getUserByUserName(@RequestParam("userAccount") String userAccount) {
UserAuthRespDTO userAuthDTO = userService.findAuthInfoByUserAccount(userAccount);
return Results.success(userAuthDTO);
}
@GetMapping("/inner/get-by-userid")
public Result<UserAuthRespDTO> getUserById(@RequestParam("userId") String userid) {
UserAuthRespDTO userAuthRespDTO = userService.findAuthInfoByUserId(userid);
return Results.success(userAuthRespDTO);
}
}

View File

@@ -3,17 +3,17 @@ package cn.meowrain.aioj.backend.userservice.dto.chains;
import cn.meowrain.aioj.backend.framework.designpattern.chains.AbstractChianHandler;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.exception.ClientException;
import cn.meowrain.aioj.backend.userservice.common.enums.ChianMarkEnums;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import com.alibaba.nacos.common.utils.StringUtils;
import cn.meowrain.aioj.backend.userservice.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class UserRegisterRequestParamVerifyChain implements AbstractChianHandler<UserRegisterRequest> {
public class UserRegisterRequestParamVerifyChain implements AbstractChianHandler<UserRegisterRequestDTO> {
@Override
public void handle(UserRegisterRequest requestParam) {
public void handle(UserRegisterRequestDTO requestParam) {
// 校验参数里面用户名和密码是不是空的
if (StringUtils.isAnyBlank(requestParam.getUserAccount(), requestParam.getUserPassword())) {
throw new ClientException("参数为空", ErrorCode.PARAMS_ERROR);
@@ -33,7 +33,7 @@ public class UserRegisterRequestParamVerifyChain implements AbstractChianHandler
@Override
public String mark() {
return ChianMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName();
return ChainMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName();
}
@Override

View File

@@ -1,9 +1,9 @@
package cn.meowrain.aioj.backend.userservice.dto.chains.context;
import cn.meowrain.aioj.backend.framework.designpattern.chains.CommonChainContext;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import cn.meowrain.aioj.backend.framework.core.designpattern.chains.CommonChainContext;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
import org.springframework.stereotype.Component;
@Component
public class UserRegisterRequestParamVerifyContext extends CommonChainContext<UserRegisterRequest> {
public class UserRegisterRequestParamVerifyContext extends CommonChainContext<UserRegisterRequestDTO> {
}

View File

@@ -1,13 +1,10 @@
package cn.meowrain.aioj.backend.userservice.dto.req;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class UserLoginRequest {
public class UserLoginRequestDTO {
private String userAccount;
private String userPassword;
HttpServletRequest request;
}

View File

@@ -1,11 +1,10 @@
package cn.meowrain.aioj.backend.userservice.dto.req;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
public class UserRegisterRequest {
public class UserRegisterRequestDTO {
private String userAccount;
private String userPassword;
private String checkPassword;

View File

@@ -2,17 +2,38 @@ package cn.meowrain.aioj.backend.userservice.dto.resp;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 用户认证响应体
*/
@Data
public class UserLoginResponse implements Serializable {
public class UserAuthRespDTO {
/**
* 用户 id
* id
*/
private Long id;
/**
* 用户账号
*/
private String userAccount;
/**
* 用户密码
*/
private String userPassword;
/**
* 开放平台id
*/
private String unionId;
/**
* 公众号openId
*/
private String mpOpenId;
/**
* 用户昵称
*/
@@ -43,5 +64,6 @@ public class UserLoginResponse implements Serializable {
*/
private Date updateTime;
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,75 @@
package cn.meowrain.aioj.backend.userservice.dto.resp;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class UserLoginResponseDTO implements Serializable {
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 用户账号
*/
private String userAccount;
/**
* 开放平台id
*/
private String unionId;
/**
* 公众号openId
*/
private String mpOpenId;
/**
* 用户昵称
*/
private String userName;
/**
* 用户头像
*/
private String userAvatar;
/**
* 用户简介
*/
private String userProfile;
/**
* 用户角色user/admin/ban
*/
private String userRole;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 是否删除
*/
private Integer isDelete;
/**
* JWT令牌登录成功返回
*/
private String token;
private static final long serialVersionUID = 1L;
}

View File

@@ -8,6 +8,6 @@ import java.io.Serializable;
* 用户注册成功响应对象
*/
@Data
public class UserRegisterResponse implements Serializable {
public class UserRegisterResponseDTO implements Serializable {
private static final long serialVersionUID = 1L;
}

View File

@@ -1,16 +1,30 @@
package cn.meowrain.aioj.backend.userservice.service;
import cn.meowrain.aioj.backend.userservice.dao.entity.User;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
import cn.meowrain.aioj.backend.userservice.dto.resp.UserAuthRespDTO;
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> {
/**
* 用户注册
* @param request {@link UserRegisterRequest}
* @param request {@link cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO}
* @return {@link Long}
*/
Long userRegister(UserRegisterRequestDTO request);
/**
* 根据用户账号查找用户认证信息
* @param userAccount
* @return
*/
Long userRegister(UserRegisterRequest request);
UserAuthRespDTO findAuthInfoByUserAccount(String userAccount);
/**
* 根据用户id查找用户认证信息
*/
UserAuthRespDTO findAuthInfoByUserId(String userId);
}

View File

@@ -1,33 +1,50 @@
package cn.meowrain.aioj.backend.userservice.service.impl;
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.exception.ServiceException;
import cn.meowrain.aioj.backend.userservice.common.enums.ChianMarkEnums;
import cn.hutool.crypto.digest.BCrypt;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.exception.ClientException;
import cn.meowrain.aioj.backend.framework.core.exception.ServiceException;
import cn.meowrain.aioj.backend.userservice.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.userservice.dao.entity.User;
import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper;
import cn.meowrain.aioj.backend.userservice.dto.chains.context.UserRegisterRequestParamVerifyContext;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
import cn.meowrain.aioj.backend.userservice.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.userservice.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.Date;
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
private final UserRegisterRequestParamVerifyContext userRegisterRequestParamVerifyContext;
private final String SALT = "meowrain";
@Override
public Long userRegister(UserRegisterRequest request) {
userRegisterRequestParamVerifyContext.handler(ChianMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(), request);
// 加密
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + request.getUserPassword()).getBytes());
User user = new User().setUserAccount(request.getUserAccount()).setUserPassword(encryptPassword);
public Long userRegister(UserRegisterRequestDTO request) {
UserAuthRespDTO authInfoByUserAccount = findAuthInfoByUserAccount(request.getUserAccount());
if (authInfoByUserAccount != null) {
throw new ClientException("重复创建用户");
}
log.info("进行用户注册");
userRegisterRequestParamVerifyContext.handler(ChainMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(),
request);
// 使用 BCrypt 加密密码
Date now = new Date();
String salt = BCrypt.gensalt();
String encryptPassword = BCrypt.hashpw(request.getUserPassword(), salt);
User user = new User().setUserAccount(request.getUserAccount()).setUserPassword(encryptPassword)
.setUserRole("user").setCreateTime(now).setUpdateTime(now);
try {
// 需要修改表,使得用户名是唯一的
this.save(user);
} catch (DuplicateKeyException e) {
log.error("重复创建用户");
@@ -36,4 +53,28 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return user.getId();
}
@Override
public UserAuthRespDTO findAuthInfoByUserAccount(String userAccount) {
User one = this.lambdaQuery().eq(User::getUserAccount, userAccount).one();
UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
if (one != null) {
BeanUtils.copyProperties(one, userAuthDTO);
return userAuthDTO;
}
return null;
}
@Override
public UserAuthRespDTO findAuthInfoByUserId(String userId) {
User one = this.lambdaQuery().eq(User::getId, userId).one();
UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
if (one != null) {
BeanUtils.copyProperties(one, userAuthDTO);
return userAuthDTO;
}
return null;
}
}

View File

@@ -8,9 +8,12 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.0.0.10/aioj_dev
username: root
password: 123456
password: root
cloud:
nacos:
discovery:
enabled: false # 🚫 禁用 Nacos 服务发现
register-enabled: false # 🚫 禁用向 Nacos 注册
enabled: true
register-enabled: true
server-addr: 10.0.0.10:8848
username: nacos
password: nacos

View File

@@ -8,4 +8,4 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.0.0.10/aioj_prod
username: root
password: 123456
password: root

View File

@@ -5,6 +5,8 @@ spring:
active: @env@
server:
port: 10010
servlet:
context-path: /api
springdoc:
api-docs:
enabled: true
@@ -17,7 +19,7 @@ springdoc:
group-configs:
- group: 'default'
paths-to-match: '/**'
packages-to-scan: cn.meowrain.aioj
packages-to-scan: cn.meowrain.aioj.backend.userservice.controller
knife4j:
basic:
enable: true
@@ -27,3 +29,7 @@ mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:/mapper/**/*.xml
aioj:
log:
enabled: true
max-length: 20000

86
pom.xml
View File

@@ -3,7 +3,12 @@
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.7</version>
<relativePath/>
</parent>
<groupId>cn.meowrain</groupId>
<artifactId>ai-oj</artifactId>
<version>1.0-SNAPSHOT</version>
@@ -15,10 +20,10 @@
<module>aioj-backend-gateway</module>
<module>aioj-backend-judge-service</module>
<module>aioj-backend-user-service</module>
<module>aioj-backend-model</module>
<module>aioj-backend-question-service</module>
<module>aioj-backend-client</module>
<module>aioj-backend-ai-service</module>
<module>aioj-backend-auth</module>
<module>aioj-backend-upms</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
@@ -30,36 +35,7 @@
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
@@ -71,43 +47,22 @@
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<!-- https://github.com/alibaba/easyexcel -->
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.41</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.meowrain.aioj</groupId>
<artifactId>aioj-backend-common-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Boot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
@@ -115,6 +70,7 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
@@ -122,12 +78,6 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>