diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/pom.xml b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/pom.xml index b673beeab279b6a48e1682bc66bcab53fa002d75..97b05bc22f5d86136fecfcc703284266ac93aa8a 100644 --- a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/pom.xml +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/pom.xml @@ -15,6 +15,12 @@ + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot spring-boot-starter-web diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/config/BasicSecurityConfig.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/config/BasicSecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..711340c7d5aa30fcc44bafbea3b1ffb844876725 --- /dev/null +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/config/BasicSecurityConfig.java @@ -0,0 +1,87 @@ +package com.example.user.adapter.in.web.config; + +import com.example.user.adapter.in.web.filter.JwtAuthenticationFilter; +import lombok.RequiredArgsConstructor; +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.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; +import java.util.List; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity(prePostEnabled = true) +@RequiredArgsConstructor +public class BasicSecurityConfig { + + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + return config.getAuthenticationManager(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOriginPatterns(List.of("*")); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")); + configuration.setAllowedHeaders(List.of("*")); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + configuration.setExposedHeaders(Arrays.asList("Authorization", "Content-Type")); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .sessionManagement(session -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .authorizeHttpRequests(authz -> authz + .requestMatchers("/users/login", "/users/register").permitAll() + .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**").permitAll() + .requestMatchers("/doc.html", "/webjars/**", "/favicon.ico").permitAll() + .requestMatchers("/actuator/**").permitAll() + .requestMatchers("/error").permitAll() + .anyRequest().authenticated() + ) + + .formLogin(AbstractHttpConfigurer::disable) + .logout(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .exceptionHandling(exceptions -> exceptions + .authenticationEntryPoint((request, response, authException) -> { + response.setStatus(401); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"code\":401,\"message\":\"未授权访问,请先登录\",\"data\":null}"); + }) + // 访问拒绝处理器,当已认证用户访问没有权限的资源时调用 + .accessDeniedHandler((request, response, accessDeniedException) -> { + response.setStatus(403); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"code\":403,\"message\":\"访问被拒绝,权限不足\",\"data\":null}"); + }) + ); + + return http.build(); + } +} diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/config/SecurityConfig.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/config/SecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..5a0bfa786885fa48f81324fc5d2eef3cfb7b7031 --- /dev/null +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/config/SecurityConfig.java @@ -0,0 +1,44 @@ +package com.example.user.adapter.in.web.config; + +import com.example.user.service.domain.User; +import com.example.user.service.domain.port.GetUserByNamePort; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +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 java.util.Collections; + +@Configuration +@EnableWebSecurity +@Slf4j +public class SecurityConfig { + @Resource + private GetUserByNamePort getUserByNamePort; + @Bean + public UserDetailsService userDetailsService() { + return new UserDetailsService() { + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + log.debug("Loading user by username: {}", username); + User user = User.getUserByName(username,getUserByNamePort); + if (user == null) { + log.warn("User not found: {}", username); + throw new UsernameNotFoundException("用户不存在: " + username); + } + return org.springframework.security.core.userdetails.User.builder() + .username(user.getName().username()) + .password(user.getPassword().encryptedValue()) + .accountExpired(false) + .accountLocked(false) + .credentialsExpired(false) + .build(); + } + }; + } +} diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/controller/UserController.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/controller/UserController.java index 96820c00d7cf18e1f698ddc65b3e279601b5d576..bbce4a7c6732e76e29774704c32204a610f90b89 100644 --- a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/controller/UserController.java +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/controller/UserController.java @@ -1,16 +1,19 @@ package com.example.user.adapter.in.web.controller; -import com.example.user.adapter.in.web.dto.CreateUserRequestDTO; -import com.example.user.adapter.in.web.dto.UpdateUserRequestDTO; -import com.example.user.adapter.in.web.dto.UserLoginRequestDTO; -import com.example.user.adapter.in.web.dto.UserResponseDTO; +import com.example.user.adapter.in.web.dto.*; +import com.example.user.adapter.in.web.service.TokenBlacklistService; import com.example.user.service.application.command.CreateUserCommand; import com.example.user.service.application.command.UpdateUserCommand; import com.example.user.service.application.command.UserLoginCommand; import com.example.user.service.application.port.in.*; import com.example.user.service.domain.User; +import com.example.user.service.domain.port.GetUserByNamePort; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -27,6 +30,8 @@ public class UserController { private final UpdateUserUseCase updateUserUseCase; private final GetUserByIdUseCase getUserByIdUseCase; private final UserLoginUseCase userLoginUseCase; + private final GetUserByNamePort getUserByNamePort; + private final TokenBlacklistService tokenBlacklistService; @PostMapping("login") @@ -36,8 +41,7 @@ public class UserController { .name(userLoginRequestDTO.getName()) .password(userLoginRequestDTO.getPassword()) .build(); - String token = userLoginUseCase.login(command); - return token; + return userLoginUseCase.login(command); } @@ -67,9 +71,25 @@ public class UserController { .age(createUserRequestDTO.age()) .email(createUserRequestDTO.email()) .password(createUserRequestDTO.password()) + .isSuper(createUserRequestDTO.isSuper()) .build(); - - return createUserUseCase.createUser(command); + User existingUser = User.getUserByName(createUserRequestDTO.name(),getUserByNamePort); + try{ + if (existingUser != null) { + log.warn("注册失败: 用户名已存在 - {}", createUserRequestDTO.name()); + return Result.error("用户名已存在,请选择其他用户名"); + } + User user = createUserUseCase.createUser(command); + if(user != null){ + return Result.success("注册成功",user); + }else { + return Result.error("注册失败,请稍后重试"); + } + }catch (Exception e){ + log.error("用户注册失败: 数据库插入失败 - {}", createUserRequestDTO.name()); + log.info(e.getMessage()); + return Result.error("注册失败,请稍后重试"); + } } @@ -82,11 +102,13 @@ public class UserController { @PutMapping("") public User updateUser(@RequestBody UpdateUserRequestDTO updateUserRequestDTO){ + UpdateUserCommand command=UpdateUserCommand.builder() .id(updateUserRequestDTO.id()) .name(updateUserRequestDTO.name()) .age(updateUserRequestDTO.age()) .email(updateUserRequestDTO.email()) + .is_super(updateUserRequestDTO.is_super()) .build(); User user = updateUserUseCase.updateUser(command); return user; diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/CreateUserRequestDTO.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/CreateUserRequestDTO.java index 574d0e0d349d44bf914066a763d8f59390c32203..3b26d0617563481f3e62a9ef348b3962b944ac36 100644 --- a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/CreateUserRequestDTO.java +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/CreateUserRequestDTO.java @@ -5,7 +5,8 @@ public record CreateUserRequestDTO( Integer age, String email, String password, - String rePassword) { + String rePassword, + boolean isSuper) { // TODO: 密码校验 /** diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/Result.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/Result.java new file mode 100644 index 0000000000000000000000000000000000000000..302c0851835435cbf8a7b1645a6f158b11ed15a2 --- /dev/null +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/Result.java @@ -0,0 +1,52 @@ +package com.example.user.adapter.in.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Result { + + private Integer code; + + + private String message; + + + private T data; + + public static Result success(T data) { + return new Result<>(200, "操作成功", data); + } + + public static Result success() { + return new Result<>(200, "操作成功", null); + } + + + public static Result success(String message, T data) { + return new Result<>(200, message, data); + } + + + public static Result error(String message) { + return new Result<>(500, message, null); + } + + + public static Result error(Integer code, String message) { + return new Result<>(code, message, null); + } + + + public static Result unauthorized(String message) { + return new Result<>(401, message, null); + } + + + public static Result forbidden(String message) { + return new Result<>(403, message, null); + } +} \ No newline at end of file diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/UpdateUserRequestDTO.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/UpdateUserRequestDTO.java index d42ae558a29f0f8fd392aaeae649437aebd8e65e..c8b60f931d578cb56505197ae815c73023932022 100644 --- a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/UpdateUserRequestDTO.java +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/dto/UpdateUserRequestDTO.java @@ -3,5 +3,6 @@ package com.example.user.adapter.in.web.dto; public record UpdateUserRequestDTO(Long id, String name, Integer age, - String email) { + String email, + boolean is_super) { } diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/filter/JwtAuthenticationFilter.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/filter/JwtAuthenticationFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..8bc670826b7fe6ecbe06a0c6ff7b9c2aa33db9e2 --- /dev/null +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/filter/JwtAuthenticationFilter.java @@ -0,0 +1,99 @@ +package com.example.user.adapter.in.web.filter; + +import com.example.user.adapter.in.web.service.TokenBlacklistService; +import com.example.user.service.common.JwtUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +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; + +@Slf4j +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + @Autowired + private JwtUtils jwtUtil; + + @Autowired + private TokenBlacklistService tokenBlacklistService; + + @Autowired + private ApplicationContext applicationContext; + + private UserDetailsService userDetailsService; + + private UserDetailsService getUserDetailsService() { + if (userDetailsService == null) { + userDetailsService = applicationContext.getBean(UserDetailsService.class); + } + return userDetailsService; + } + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + try { + String jwt = getJwtFromRequest(request); + if (StringUtils.hasText(jwt)) { + if (tokenBlacklistService.isBlacklisted(jwt)) { + log.warn("Token is blacklisted (user has logged out): {}", + jwt.substring(0, Math.min(20, jwt.length())) + "..."); + SecurityContextHolder.clearContext(); + filterChain.doFilter(request, response); + return; + } + String username = jwtUtil.getUsernameFromToken(jwt); + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = getUserDetailsService().loadUserByUsername(username); + if (jwtUtil.validateToken(jwt, userDetails.getUsername())) { + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + log.debug("JWT authentication successful for user: {}", username); + } else { + log.warn("JWT token validation failed for user: {}", username); + } + } + } + } catch (Exception e) { + log.error("Cannot set user authentication: {}", e.getMessage()); + SecurityContextHolder.clearContext(); + } + filterChain.doFilter(request, response); + } + + private String getJwtFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + return jwtUtil.extractTokenFromHeader(bearerToken); + } + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + String path = request.getRequestURI(); + boolean isBusinessWhitelist = path.startsWith("/users/login") || + path.startsWith("/users/register"); + boolean isDocWhitelist = path.startsWith("/doc.html") || + path.startsWith("/swagger-ui") || + path.startsWith("/v3/api-docs") || + path.startsWith("/webjars"); + return isBusinessWhitelist || isDocWhitelist; + } +} \ No newline at end of file diff --git a/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/service/TokenBlacklistService.java b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/service/TokenBlacklistService.java new file mode 100644 index 0000000000000000000000000000000000000000..d33b8c68179e0517d5f76245aa2a415410e6fc2b --- /dev/null +++ b/user-service/user-service-adapter/user-adapter-in/user-adapter-in-web/src/main/java/com/example/user/adapter/in/web/service/TokenBlacklistService.java @@ -0,0 +1,76 @@ +package com.example.user.adapter.in.web.service; + +import com.example.user.service.common.JwtUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TokenBlacklistService { + private final JwtUtils jwtUtil; + + private final ConcurrentHashMap blacklistedTokens = new ConcurrentHashMap<>(); + + public void addToBlacklist(String token) { + try { + Date expirationTime = jwtUtil.getExpirationDateFromToken(token); + blacklistedTokens.put(token, expirationTime); + log.info("令牌已添加到黑名单,过期时间: {}, 令牌前缀: {}...", + expirationTime, + token.substring(0, Math.min(token.length(), 10))); + + } catch (Exception e) { + log.warn("添加令牌到黑名单时发生错误: {}, 令牌前缀: {}...", + e.getMessage(), + token.substring(0, Math.min(token.length(), 10))); + } + } + + + public boolean isBlacklisted(String token) { + boolean isBlacklisted = blacklistedTokens.containsKey(token); + + if (isBlacklisted) { + log.debug("检测到黑名单令牌访问尝试,令牌前缀: {}...", + token.substring(0, Math.min(token.length(), 10))); + } + + return isBlacklisted; + } + + @Scheduled(fixedRate = 3600000) // 每小时执行一次 + public void cleanupExpiredTokens() { + Date now = new Date(); + int initialSize = blacklistedTokens.size(); + + blacklistedTokens.entrySet().removeIf(entry -> { + Date expirationTime = entry.getValue(); + return expirationTime != null && now.after(expirationTime); + }); + + int finalSize = blacklistedTokens.size(); + int cleanedCount = initialSize - finalSize; + + if (cleanedCount > 0) { + log.info("黑名单清理完成,清理了 {} 个过期令牌,当前黑名单大小: {}", cleanedCount, finalSize); + } else { + log.debug("黑名单清理完成,无过期令牌需要清理,当前黑名单大小: {}", finalSize); + } + } + + public int getBlacklistSize() { + return blacklistedTokens.size(); + } + + public void clearBlacklist() { + int size = blacklistedTokens.size(); + blacklistedTokens.clear(); + log.warn("黑名单已被清空,共清理了 {} 个令牌", size); + } +} \ No newline at end of file diff --git a/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/bridge/GetUserByNameBridge.java b/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/bridge/GetUserByNameBridge.java index 38dc3ea5a32a0f658d80085de97fb59d19db3827..9666d82d64b3a29e3c69a21c01fc68b25e20d5ec 100644 --- a/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/bridge/GetUserByNameBridge.java +++ b/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/bridge/GetUserByNameBridge.java @@ -22,8 +22,10 @@ public class GetUserByNameBridge implements GetUserByNamePort { UserEntity userEntity = userMapper.selectOne(wrapper); //password不空 log.info("userEntity: {}", userEntity); - - User user = UserConvertor.toDomain(userEntity); + if(userEntity == null){ + return null; + } + User user = UserConvertor.userToLogin(userEntity); log.info("user: {}", user); return user; } diff --git a/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/bridge/UpdateUserBridge.java b/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/bridge/UpdateUserBridge.java index 231cd5a542ecba8ced5defc94faa43ab52a4af00..890306f3befe8cbcf4f43fd57ac7b5f9b3f8474b 100644 --- a/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/bridge/UpdateUserBridge.java +++ b/user-service/user-service-adapter/user-adapter-out/user-adapter-out-persistence/src/main/java/com/example/user/adapter/out/persistence/bridge/UpdateUserBridge.java @@ -16,7 +16,7 @@ public class UpdateUserBridge implements UpdateUserPort { @Override public User updateUser(User user) { - int result = userMapper.updateById(UserConvertor.toEntity(user)); + int result = userMapper.updateById(UserConvertor.toUpdateEntity(user)); log.info("result:{}",result); return user; } diff --git a/user-service/user-service-application/src/main/java/com/example/user/service/application/command/CreateUserCommand.java b/user-service/user-service-application/src/main/java/com/example/user/service/application/command/CreateUserCommand.java index d7f351917f3750105968088e03b460e2b9ba8bcb..34344105117808bab47cc432adb5872494bd02fd 100644 --- a/user-service/user-service-application/src/main/java/com/example/user/service/application/command/CreateUserCommand.java +++ b/user-service/user-service-application/src/main/java/com/example/user/service/application/command/CreateUserCommand.java @@ -8,6 +8,7 @@ public record CreateUserCommand( String name, Integer age, String email, - String password + String password, + boolean isSuper ) { } diff --git a/user-service/user-service-application/src/main/java/com/example/user/service/application/command/UpdateUserCommand.java b/user-service/user-service-application/src/main/java/com/example/user/service/application/command/UpdateUserCommand.java index 0ef0ed9076efe403013ff1e6c169c353387b6b30..b137582a40744d462673176b72544c1956de2878 100644 --- a/user-service/user-service-application/src/main/java/com/example/user/service/application/command/UpdateUserCommand.java +++ b/user-service/user-service-application/src/main/java/com/example/user/service/application/command/UpdateUserCommand.java @@ -6,5 +6,6 @@ import lombok.Builder; public record UpdateUserCommand(Long id, String name, Integer age, - String email) { + String email, + boolean is_super) { } diff --git a/user-service/user-service-application/src/main/java/com/example/user/service/application/service/CreateUserService.java b/user-service/user-service-application/src/main/java/com/example/user/service/application/service/CreateUserService.java index 931828a4d32e7cae7a78cc0e55f92ade3a7ff2e5..64a87dd43ca11194225642c78805222c6026c162 100644 --- a/user-service/user-service-application/src/main/java/com/example/user/service/application/service/CreateUserService.java +++ b/user-service/user-service-application/src/main/java/com/example/user/service/application/service/CreateUserService.java @@ -4,10 +4,7 @@ import com.example.user.service.application.command.CreateUserCommand; import com.example.user.service.application.port.in.CreateUserUseCase; import com.example.user.service.domain.User; import com.example.user.service.domain.port.CreateUserPort; -import com.example.user.service.domain.valueobject.Email; -import com.example.user.service.domain.valueobject.Password; -import com.example.user.service.domain.valueobject.UserAge; -import com.example.user.service.domain.valueobject.UserName; +import com.example.user.service.domain.valueobject.*; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -25,7 +22,8 @@ public class CreateUserService implements CreateUserUseCase { new UserAge(createUserCommand.age()), new Email(createUserCommand.email()), // new Password( createUserCommand.password()) - Password.fromRaw(createUserCommand.password()) + Password.fromRaw(createUserCommand.password()), + new IsSuper(createUserCommand.isSuper()) ); log.info("user:{}",user); return createUserPort.createUser(user); diff --git a/user-service/user-service-application/src/main/java/com/example/user/service/application/service/UpdateUserService.java b/user-service/user-service-application/src/main/java/com/example/user/service/application/service/UpdateUserService.java index 97da325352c39b77ddfdc0f9982a3f654a201c91..583c4dfd43f862788cb23fceb12b3f353aec396d 100644 --- a/user-service/user-service-application/src/main/java/com/example/user/service/application/service/UpdateUserService.java +++ b/user-service/user-service-application/src/main/java/com/example/user/service/application/service/UpdateUserService.java @@ -8,6 +8,7 @@ import com.example.user.service.domain.valueobject.Email; import com.example.user.service.domain.valueobject.UserAge; import com.example.user.service.domain.valueobject.UserId; import com.example.user.service.domain.valueobject.UserName; +import com.example.user.service.domain.valueobject.IsSuper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -22,7 +23,8 @@ public class UpdateUserService implements UpdateUserUseCase { new UserId(command.id()), new UserName(command.name()), new UserAge(command.age()), - new Email(command.email())); + new Email(command.email()), + new IsSuper(command.is_super())); return updateUserPort.updateUser(user); } } diff --git a/user-service/user-service-common/src/main/java/com/example/user/service/common/JwtUtils.java b/user-service/user-service-common/src/main/java/com/example/user/service/common/JwtUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..832c18c6076dffd9f42d1a70a7f66525d3288633 --- /dev/null +++ b/user-service/user-service-common/src/main/java/com/example/user/service/common/JwtUtils.java @@ -0,0 +1,84 @@ +package com.example.user.service.common; + + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.util.Calendar; +import java.util.Date; + +@Slf4j +@Component +public class JwtUtils { + private static final String key = "${jwt.secret:mySecretKeyForJwtTokenGenerationAndValidation123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789}"; + /** + * @author suzhilin + * 创建JWT令牌 + * @param id 用户ID + * @param name 用户名称 + * @param is_super 是否为超级用户 + * @return JWT令牌 + */ + public static String createToken(long id,String name,boolean is_super){ + Calendar instance = Calendar.getInstance(); + instance.add(Calendar.MINUTE,5); + return JWT.create().withClaim("userId",id) + .withClaim("name",name) + .withClaim("is_super",is_super) + .withExpiresAt(instance.getTime()) + .sign(Algorithm.HMAC512(key)); + } + + public String getUsernameFromToken(String token) { + Claims claims = getClaimsFromToken(token); + return claims.get("name",String.class); + } + + private Claims getClaimsFromToken(String token) { + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + + public boolean validateToken(String token, String username) { + try { + String tokenUsername = getUsernameFromToken(token); + return (username.equals(tokenUsername) && !isTokenExpired(token)); + } catch (Exception e) { + log.error("Token validation failed: {}", e.getMessage()); + return false; + } + } + + public Date getExpirationDateFromToken(String token) { + Claims claims = getClaimsFromToken(token); + return claims.getExpiration(); + } + + private boolean isTokenExpired(String token) { + Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + private SecretKey getSigningKey() { + byte[] keyBytes = key.getBytes(); + return Keys.hmacShaKeyFor(keyBytes); + } + + public String extractTokenFromHeader(String authHeader) { + if (authHeader != null && authHeader.startsWith("Bearer ")) { + return authHeader.substring(7); + } + return null; + } +} diff --git a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java index 8d9521b5c44f57f74deb13c1872241318475abe4..665418b57bb4a7bd39dbd0b502a1a5da8f640d64 100644 --- a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java +++ b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/User.java @@ -34,7 +34,7 @@ public class User { this.isSuper = isSuper; } - public User( UserName name, UserAge age, Email email, Password password) { + public User( UserName name, UserAge age, Email email, Password password, IsSuper is_super) { this.id= genId() ; this.name = name; this.age = age; @@ -44,11 +44,26 @@ public class User { } public User(UserId userId, UserName userName, UserAge userAge, Email email) { + this.id = userId; + this.name = userName; + this.age = userAge; + this.email = email; + this.isSuper = new IsSuper(false); + } + + public User(UserId id, UserName name, IsSuper is_super, Password password) { this.id = id; + this.is_super = is_super; this.name = name; - this.age = age; + this.password = password; + } + + public User(UserId userId, UserName userName, UserAge userAge, Email email, IsSuper isSuper) { + this.id = userId; + this.name = userName; + this.age = userAge; this.email = email; - this.isSuper = new IsSuper(false); + this.is_super = isSuper; } @@ -82,4 +97,5 @@ public class User { public boolean validatePassword(String password){ return this.password.verify( password); } + } diff --git a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/valueobject/IsSuper.java b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/valueobject/IsSuper.java index f05ce455c4d7b8f6c90f818413590ec88c4c37f8..42fe0e3d371f453c981221d5e8e55b7b3bcae55b 100644 --- a/user-service/user-service-domain/src/main/java/com/example/user/service/domain/valueobject/IsSuper.java +++ b/user-service/user-service-domain/src/main/java/com/example/user/service/domain/valueobject/IsSuper.java @@ -17,4 +17,4 @@ public record IsSuper(boolean value) { return new IsSuper(value); } -} \ No newline at end of file +}