diff --git a/.idea/misc.xml b/.idea/misc.xml
index e2516d5..15a25de 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,6 @@
-
\ No newline at end of file
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/constants/UserServiceChainConstants.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/constants/UserServiceChainConstants.java
deleted file mode 100644
index 12ac2c9..0000000
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/constants/UserServiceChainConstants.java
+++ /dev/null
@@ -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";
-}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/enums/ChianMarkEnums.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/enums/ChainMarkEnums.java
similarity index 72%
rename from aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/enums/ChianMarkEnums.java
rename to aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/enums/ChainMarkEnums.java
index de3f64c..d62fe62 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/enums/ChianMarkEnums.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/common/enums/ChainMarkEnums.java
@@ -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;
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/FrameworkConfiguration.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/FrameworkConfiguration.java
index 6af99f6..0cb15b2 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/FrameworkConfiguration.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/FrameworkConfiguration.java
@@ -1,5 +1,6 @@
package cn.meowrain.aioj.backend.userservice.config;
+import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
@@ -9,4 +10,6 @@ import org.springframework.context.annotation.Configuration;
@ComponentScan("cn.meowrain.aioj.backend.framework.banner")
})
public class FrameworkConfiguration {
+
+
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/JwtPropertiesConfiguration.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/JwtPropertiesConfiguration.java
new file mode 100644
index 0000000..e744365
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/JwtPropertiesConfiguration.java
@@ -0,0 +1,21 @@
+package cn.meowrain.aioj.backend.userservice.config;
+
+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 expire;
+}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/SecurityConfiguration.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/SecurityConfiguration.java
new file mode 100644
index 0000000..e51c723
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/SecurityConfiguration.java
@@ -0,0 +1,56 @@
+package cn.meowrain.aioj.backend.userservice.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;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import cn.meowrain.aioj.backend.userservice.security.JwtAuthenticationFilter;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfiguration {
+ private final JwtAuthenticationFilter jwtAuthenticationFilter;
+
+ public SecurityConfiguration(JwtAuthenticationFilter jwtAuthenticationFilter) {
+ this.jwtAuthenticationFilter = jwtAuthenticationFilter;
+ }
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http
+ .csrf(csrf -> csrf.disable())
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers(
+ "/auth/**",
+ "/doc.html",
+ "/swagger-ui/**",
+ "/swagger-resources/**",
+ "/webjars/**",
+ "/v3/api-docs/**",
+ "/favicon.ico"
+ )
+ .permitAll()
+ .anyRequest().authenticated())
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+ return http.build();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
+ return configuration.getAuthenticationManager();
+ }
+}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/controller/UserController.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/controller/UserController.java
index c51f2fe..7c8c633 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/controller/UserController.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/controller/UserController.java
@@ -1,7 +1,36 @@
package cn.meowrain.aioj.backend.userservice.controller;
+import cn.meowrain.aioj.backend.framework.web.Result;
+import cn.meowrain.aioj.backend.framework.web.Results;
+import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
+import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
+import cn.meowrain.aioj.backend.userservice.dto.resp.UserLoginResponse;
+import cn.meowrain.aioj.backend.userservice.service.UserService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-@RestController("/user")
+@RequiredArgsConstructor
+@RestController()
+@RequestMapping("/auth")
public class UserController {
+
+ private final UserService userService;
+
+
+ @PostMapping("/register")
+ public Result register(@RequestBody UserRegisterRequest userRegisterRequest) {
+ Long l = userService.userRegister(userRegisterRequest);
+ return Results.success(l);
+ }
+
+ @PostMapping("/login")
+ public Result login(@RequestBody UserLoginRequest userLoginRequest) {
+ UserLoginResponse userLoginResponse = userService.userLogin(userLoginRequest);
+ return Results.success(userLoginResponse);
+
+ }
+
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/UserLoginRequestParamVerifyChain.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/UserLoginRequestParamVerifyChain.java
new file mode 100644
index 0000000..d8b7343
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/UserLoginRequestParamVerifyChain.java
@@ -0,0 +1,37 @@
+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.ChainMarkEnums;
+import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class UserLoginRequestParamVerifyChain implements AbstractChianHandler {
+ @Override
+ public void handle(UserLoginRequest 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;
+ }
+}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/UserRegisterRequestParamVerifyChain.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/UserRegisterRequestParamVerifyChain.java
index 192baae..f8f16b4 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/UserRegisterRequestParamVerifyChain.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/UserRegisterRequestParamVerifyChain.java
@@ -3,7 +3,7 @@ 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.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
import com.alibaba.nacos.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
@@ -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
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/context/UserLoginRequestParamVerifyContext.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/context/UserLoginRequestParamVerifyContext.java
new file mode 100644
index 0000000..d4ea053
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/chains/context/UserLoginRequestParamVerifyContext.java
@@ -0,0 +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.UserLoginRequest;
+import org.springframework.stereotype.Component;
+
+@Component
+public class UserLoginRequestParamVerifyContext extends CommonChainContext {
+}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserLoginRequest.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserLoginRequest.java
index a92d012..372f5a5 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserLoginRequest.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserLoginRequest.java
@@ -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 {
private String userAccount;
private String userPassword;
- HttpServletRequest request;
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserRegisterRequest.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserRegisterRequest.java
index c419f7a..32239d0 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserRegisterRequest.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/req/UserRegisterRequest.java
@@ -1,10 +1,9 @@
package cn.meowrain.aioj.backend.userservice.dto.req;
-import lombok.Builder;
import lombok.Data;
+import lombok.NoArgsConstructor;
@Data
-@Builder
public class UserRegisterRequest {
private String userAccount;
private String userPassword;
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/resp/UserLoginResponse.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/resp/UserLoginResponse.java
index da58620..b8ca516 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/resp/UserLoginResponse.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/dto/resp/UserLoginResponse.java
@@ -1,5 +1,6 @@
package cn.meowrain.aioj.backend.userservice.dto.resp;
+import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
@@ -7,11 +8,26 @@ import java.util.Date;
@Data
public class UserLoginResponse implements Serializable {
+ /**
+ * id
+ */
+ @TableId(type = IdType.ASSIGN_ID)
+ private Long id;
/**
- * 用户 id
+ * 用户账号
*/
- private Long id;
+ private String userAccount;
+
+ /**
+ * 开放平台id
+ */
+ private String unionId;
+
+ /**
+ * 公众号openId
+ */
+ private String mpOpenId;
/**
* 用户昵称
@@ -43,5 +59,17 @@ public class UserLoginResponse implements Serializable {
*/
private Date updateTime;
+ /**
+ * 是否删除
+ */
+
+ private Integer isDelete;
+
+
+ /**
+ * JWT令牌(登录成功返回)
+ */
+ private String token;
+
private static final long serialVersionUID = 1L;
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/security/CustomUserDetailsService.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/security/CustomUserDetailsService.java
new file mode 100644
index 0000000..d3e8827
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/security/CustomUserDetailsService.java
@@ -0,0 +1,35 @@
+package cn.meowrain.aioj.backend.userservice.security;
+
+import cn.meowrain.aioj.backend.userservice.dao.entity.User;
+import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.Collections;
+
+@Service
+public class CustomUserDetailsService implements UserDetailsService {
+ private final UserMapper userMapper;
+
+ public CustomUserDetailsService(UserMapper userMapper) {
+ this.userMapper = userMapper;
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUserAccount, username));
+ if (user == null) {
+ throw new UsernameNotFoundException("用户不存在: " + username);
+ }
+ Collection extends GrantedAuthority> authorities = Collections
+ .singletonList(new SimpleGrantedAuthority(user.getUserRole()));
+ return new org.springframework.security.core.userdetails.User(user.getUserAccount(), user.getUserPassword(),
+ authorities);
+ }
+}
\ No newline at end of file
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/security/JwtAuthenticationFilter.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/security/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..7f676cd
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/security/JwtAuthenticationFilter.java
@@ -0,0 +1,76 @@
+package cn.meowrain.aioj.backend.userservice.security;
+
+import cn.meowrain.aioj.backend.userservice.dao.entity.User;
+import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper;
+import cn.meowrain.aioj.backend.userservice.utils.JwtUtil;
+import io.jsonwebtoken.Claims;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * JWT请求过滤器:解析 Authorization 头中的 Bearer token 并设置认证上下文
+ */
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+ private final JwtUtil jwtUtil;
+ private final UserMapper userMapper;
+
+ public JwtAuthenticationFilter(JwtUtil jwtUtil, UserMapper userMapper) {
+ this.jwtUtil = jwtUtil;
+ this.userMapper = userMapper;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request,
+ HttpServletResponse response,
+ FilterChain filterChain) throws ServletException, IOException {
+ String header = request.getHeader("Authorization");
+ if (!StringUtils.hasText(header) || !header.startsWith("Bearer ")) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+ String token = header.substring(7);
+ if (!jwtUtil.isTokenValid(token)) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+ try {
+ Claims claims = jwtUtil.parseClaims(token);
+ String account = claims.getSubject();
+ Long userId = claims.get("userId", Long.class);
+
+ // 避免重复设置
+ if (StringUtils.hasText(account) && SecurityContextHolder.getContext().getAuthentication() == null) {
+ // 可选:从数据库再查一次,保证用户未被删除/封禁
+ User user = userMapper.selectById(userId);
+ if (user == null || !StringUtils.hasText(user.getUserRole())) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+ List authorities = Collections
+ .singletonList(new SimpleGrantedAuthority(user.getUserRole()));
+ UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(account,
+ null, authorities);
+ authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ } catch (Exception ignored) {
+ // 解析失败直接继续,不抛出,保持无状态
+ }
+ filterChain.doFilter(request, response);
+ }
+}
\ No newline at end of file
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/UserService.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/UserService.java
index 07b50a1..1e29a9e 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/UserService.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/UserService.java
@@ -1,16 +1,23 @@
package cn.meowrain.aioj.backend.userservice.service;
import cn.meowrain.aioj.backend.userservice.dao.entity.User;
+import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
+import cn.meowrain.aioj.backend.userservice.dto.resp.UserLoginResponse;
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService {
/**
* 用户注册
* @param request {@link UserRegisterRequest}
- * @return
+ * @return {@link Long}
*/
Long userRegister(UserRegisterRequest request);
-
+ /**
+ * 用户登录
+ * @param request {@link UserLoginRequest}
+ * @return {@link UserLoginResponse}
+ */
+ UserLoginResponse userLogin(UserLoginRequest request);
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/UserServiceImpl.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/UserServiceImpl.java
index abca8d9..a401139 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/UserServiceImpl.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/service/impl/UserServiceImpl.java
@@ -1,33 +1,48 @@
package cn.meowrain.aioj.backend.userservice.service.impl;
+import cn.hutool.core.util.ObjectUtil;
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.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.UserLoginRequestParamVerifyContext;
import cn.meowrain.aioj.backend.userservice.dto.chains.context.UserRegisterRequestParamVerifyContext;
+import cn.meowrain.aioj.backend.userservice.dto.req.UserLoginRequest;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequest;
+import cn.meowrain.aioj.backend.userservice.dto.resp.UserLoginResponse;
import cn.meowrain.aioj.backend.userservice.service.UserService;
+import cn.meowrain.aioj.backend.userservice.utils.JwtUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
-import org.springframework.util.DigestUtils;
+
+import java.util.Date;
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl implements UserService {
private final UserRegisterRequestParamVerifyContext userRegisterRequestParamVerifyContext;
- private final String SALT = "meowrain";
+ private final UserLoginRequestParamVerifyContext userLoginRequestParamVerifyContext;
+ private final PasswordEncoder passwordEncoder;
+ private final JwtUtil jwtUtil;
@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);
+ userRegisterRequestParamVerifyContext.handler(ChainMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(),
+ request);
+ // 使用 BCrypt 加密密码
+ Date now = new Date();
+ String encryptPassword = passwordEncoder.encode(request.getUserPassword());
+ 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 +51,29 @@ public class UserServiceImpl extends ServiceImpl implements Us
return user.getId();
}
+
+ @Override
+ public UserLoginResponse userLogin(UserLoginRequest requestParam) {
+ // 1.校验
+ userLoginRequestParamVerifyContext.handler(ChainMarkEnums.USER_LOGIN_REQ_PARAM_VERIFY.getMarkName(),
+ requestParam);
+ User user = this.baseMapper.selectOne(Wrappers.lambdaQuery(User.class)
+ .eq(User::getUserAccount, requestParam.getUserAccount()));
+ if (ObjectUtil.isNull(user)
+ || !passwordEncoder.matches(requestParam.getUserPassword(), user.getUserPassword())) {
+ throw new ServiceException("用户不存在或者密码错误", ErrorCode.NOT_LOGIN_ERROR);
+ }
+ // 生成 JWT
+ String token = jwtUtil.generateToken(user);
+ UserLoginResponse resp = new UserLoginResponse();
+ resp.setId(user.getId());
+ resp.setUserAccount(user.getUserAccount());
+ resp.setUserAvatar(user.getUserAvatar());
+ resp.setUserProfile(user.getUserProfile());
+ resp.setUserRole(user.getUserRole());
+ resp.setCreateTime(user.getCreateTime());
+ resp.setUpdateTime(user.getUpdateTime());
+ resp.setToken(token);
+ return resp;
+ }
}
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/utils/JwtUtil.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/utils/JwtUtil.java
new file mode 100644
index 0000000..e4702c8
--- /dev/null
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/utils/JwtUtil.java
@@ -0,0 +1,59 @@
+package cn.meowrain.aioj.backend.userservice.utils;
+
+import cn.meowrain.aioj.backend.userservice.config.JwtPropertiesConfiguration;
+import cn.meowrain.aioj.backend.userservice.dao.entity.User;
+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;
+
+/**
+ * JWT工具类
+ */
+@RequiredArgsConstructor
+@Component
+public class JwtUtil {
+ private final JwtPropertiesConfiguration jwtConfig;
+
+ private SecretKey getSigningKey() {
+ return Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes());
+ }
+
+ public String generateToken(User user) {
+ long now = System.currentTimeMillis();
+ Map claims = new HashMap<>();
+ claims.put("userId", user.getId());
+ claims.put("role", user.getUserRole());
+ return Jwts.builder()
+ .subject(user.getUserAccount())
+ .issuedAt(new Date(now))
+ .expiration(new Date(now + jwtConfig.getExpire()))
+ .claims(claims)
+ .signWith(getSigningKey(), Jwts.SIG.HS256)
+ .compact();
+ }
+
+ public Claims parseClaims(String token) {
+ return Jwts.parser()
+ .verifyWith(getSigningKey())
+ .build()
+ .parseSignedClaims(token)
+ .getPayload();
+ }
+
+ public boolean isTokenValid(String token) {
+ try {
+ Claims claims = parseClaims(token);
+ Date expiration = claims.getExpiration();
+ return expiration != null && expiration.after(new Date());
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/aioj-backend-user-service/src/main/resources/application-dev.yml b/aioj-backend-user-service/src/main/resources/application-dev.yml
index 3c2f2a4..4769411 100644
--- a/aioj-backend-user-service/src/main/resources/application-dev.yml
+++ b/aioj-backend-user-service/src/main/resources/application-dev.yml
@@ -8,7 +8,7 @@ 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:
diff --git a/aioj-backend-user-service/src/main/resources/application-prod.yml b/aioj-backend-user-service/src/main/resources/application-prod.yml
index 07caeb5..ed56224 100644
--- a/aioj-backend-user-service/src/main/resources/application-prod.yml
+++ b/aioj-backend-user-service/src/main/resources/application-prod.yml
@@ -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
\ No newline at end of file
+ password: root
\ No newline at end of file
diff --git a/aioj-backend-user-service/src/main/resources/application.yml b/aioj-backend-user-service/src/main/resources/application.yml
index f6ac7e7..d8873e5 100644
--- a/aioj-backend-user-service/src/main/resources/application.yml
+++ b/aioj-backend-user-service/src/main/resources/application.yml
@@ -17,7 +17,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 +27,6 @@ mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:/mapper/**/*.xml
+jwt:
+ secret: "12345678901234567890123456789012" # 至少32字节!!
+ expire: 86400000 # 24小时(单位:毫秒)
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 3d2b2ae..cdbceba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,10 +44,17 @@
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
-
-
-
-
+
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
+
+
+
+
+
com.baomidou
mybatis-plus-spring-boot3-starter
@@ -128,6 +135,34 @@
knife4j-openapi3-jakarta-spring-boot-starter
4.5.0
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+ 3.5.7
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+ 3.5.7
+
+
+
+ org.springframework.security
+ spring-security-test
+ 6.5.7
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+ 3.5.7
+