diff --git a/batch-quartz/src/main/java/com/spring/domain/user/api/SignApi.java b/batch-quartz/src/main/java/com/spring/domain/user/api/SignApi.java index dc0cfaf..5890ef0 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/api/SignApi.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/api/SignApi.java @@ -9,7 +9,9 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.spring.domain.user.dto.ChangePasswordRequest; import com.spring.domain.user.dto.SignUpRequest; +import com.spring.domain.user.service.ChangePasswordService; import com.spring.domain.user.service.SignUpService; import lombok.RequiredArgsConstructor; @@ -20,6 +22,7 @@ import lombok.RequiredArgsConstructor; public class SignApi { private final SignUpService signUpService; + private final ChangePasswordService changePasswordService; @GetMapping("/conflict/{userId}") public boolean isConflictUserId(@PathVariable String userId) { @@ -31,4 +34,9 @@ public class SignApi { signUpService.signUp(request); } + @PostMapping("/change-password") + public void changePassword(@RequestBody @Valid ChangePasswordRequest request) { + changePasswordService.changePassword(request); + } + } diff --git a/batch-quartz/src/main/java/com/spring/domain/user/api/PasswordChangeApi.java b/batch-quartz/src/main/java/com/spring/domain/user/api/UserManagementApi.java similarity index 50% rename from batch-quartz/src/main/java/com/spring/domain/user/api/PasswordChangeApi.java rename to batch-quartz/src/main/java/com/spring/domain/user/api/UserManagementApi.java index 1b24197..05af61c 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/api/PasswordChangeApi.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/api/UserManagementApi.java @@ -1,5 +1,7 @@ package com.spring.domain.user.api; +import java.util.List; + import javax.validation.Valid; import org.springframework.web.bind.annotation.PostMapping; @@ -7,21 +9,21 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.spring.domain.user.dto.PasswordChangeRequest; -import com.spring.domain.user.service.PasswordChangeService; +import com.spring.domain.user.dto.ChangeUserRoleApproveRequest; +import com.spring.domain.user.service.UserManagementService; import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor @RequestMapping("/api/user") -public class PasswordChangeApi { +public class UserManagementApi { - private final PasswordChangeService passwordChangeService; + private final UserManagementService userManagementService; - @PostMapping("/password-change") - public void passwordChange(@RequestBody @Valid PasswordChangeRequest request) { - passwordChangeService.changePassword(request); + @PostMapping("/change-role-approve") + public void signUp(@RequestBody @Valid List requests) { + userManagementService.changeRoleApprove(requests); } } diff --git a/batch-quartz/src/main/java/com/spring/domain/user/dto/PasswordChangeRequest.java b/batch-quartz/src/main/java/com/spring/domain/user/dto/ChangePasswordRequest.java similarity index 91% rename from batch-quartz/src/main/java/com/spring/domain/user/dto/PasswordChangeRequest.java rename to batch-quartz/src/main/java/com/spring/domain/user/dto/ChangePasswordRequest.java index 566cbac..f3f804f 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/dto/PasswordChangeRequest.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/dto/ChangePasswordRequest.java @@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor -public class PasswordChangeRequest { +public class ChangePasswordRequest { @NotBlank(message = "사용자ID는 필수값 입니다.") private final String userId; diff --git a/batch-quartz/src/main/java/com/spring/domain/user/dto/ChangeUserRoleApproveRequest.java b/batch-quartz/src/main/java/com/spring/domain/user/dto/ChangeUserRoleApproveRequest.java new file mode 100644 index 0000000..343fde3 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/domain/user/dto/ChangeUserRoleApproveRequest.java @@ -0,0 +1,25 @@ +package com.spring.domain.user.dto; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +import com.spring.common.validation.EnumValid; +import com.spring.domain.user.entity.AgentUserRole; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ChangeUserRoleApproveRequest { + + @NotBlank(message = "사용자ID는 필수값 입니다.") + private final String userId; + + @EnumValid(target = AgentUserRole.class, message = "권한은 필수값 입니다.") + private final AgentUserRole userRole; + + @NotNull(message = "승인 여부는 필수값 입니다.") + private final boolean isApproved; + +} diff --git a/batch-quartz/src/main/java/com/spring/domain/user/dto/SignUpRequest.java b/batch-quartz/src/main/java/com/spring/domain/user/dto/SignUpRequest.java index 4293d5c..ea0b04d 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/dto/SignUpRequest.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/dto/SignUpRequest.java @@ -1,5 +1,6 @@ package com.spring.domain.user.dto; +import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import com.spring.common.validation.EnumValid; @@ -23,6 +24,10 @@ public class SignUpRequest { @NotBlank(message = "사용자명은 필수값 입니다.") private String userName; + @Email(message = "EMAIL형식이 잘못 되었습니다.") + @NotBlank(message = "EMAIL은 필수값 입니다.") + private String email; + @EnumValid(target = AgentUserRole.class, message = "올바른 값을 입력해주세요.") private AgentUserRole userRole; @@ -35,6 +40,8 @@ public class SignUpRequest { .userId(userId) .userPassword(userPassword) .userName(userName) + .email(email) + .isApproved(false) .userRole(userRole) .build(); } diff --git a/batch-quartz/src/main/java/com/spring/domain/user/dto/UserManagementResponse.java b/batch-quartz/src/main/java/com/spring/domain/user/dto/UserManagementResponse.java new file mode 100644 index 0000000..fb84b8d --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/domain/user/dto/UserManagementResponse.java @@ -0,0 +1,17 @@ +package com.spring.domain.user.dto; + +import com.spring.domain.user.entity.AgentUserRole; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class UserManagementResponse { + + private final String userId; + private final String userName; + private final String email; + private final AgentUserRole userRole; + +} diff --git a/batch-quartz/src/main/java/com/spring/domain/user/entity/AgentUser.java b/batch-quartz/src/main/java/com/spring/domain/user/entity/AgentUser.java index 1dd68df..c5958eb 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/entity/AgentUser.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/entity/AgentUser.java @@ -45,22 +45,38 @@ public class AgentUser implements UserPrincipal { @Column(name = "USER_NAME", nullable = false, length = 50) private String userName; + @Column(name = "EMAIL", nullable = false, length = 100) + private String email; + + @Column(name = "IS_APPROVED", nullable = false) + private boolean isApproved; + @Enumerated(EnumType.STRING) @Column(name = "USER_ROLE", nullable = false, length = 50) private AgentUserRole userRole; @Builder - public AgentUser(String userId, String userPassword, String userName, AgentUserRole userRole) { + public AgentUser(String userId, String userPassword, String userName, AgentUserRole userRole, String email, boolean isApproved) { this.userId = userId; this.userPassword = userPassword; this.userName = userName; this.userRole = userRole; + this.email = email; + this.isApproved = isApproved; } public void changePassword(String newPassword) { this.userPassword = newPassword; } + public void changeUserRole(AgentUserRole userRole) { + this.userRole = userRole; + } + + public void changeApproved(boolean isApproved) { + this.isApproved = isApproved; + } + @Override public Collection getAuthorities() { return Arrays.stream(AgentUserRole.values()) diff --git a/batch-quartz/src/main/java/com/spring/domain/user/entity/AgentUserRole.java b/batch-quartz/src/main/java/com/spring/domain/user/entity/AgentUserRole.java index cc628b8..2426d48 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/entity/AgentUserRole.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/entity/AgentUserRole.java @@ -12,9 +12,9 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public enum AgentUserRole { - ROLE_USER("ROLE_USER", "사용자"), + ROLE_SUPER("ROLE_SUPER", "슈퍼관리자"), ROLE_ADMIN("ROLE_ADMIN", "관리자"), - ROLE_ANONYMOUS("ROLE_ANONYMOUS", "익명사용자"); + ROLE_USER("ROLE_USER", "사용자"); @JsonValue private final String role; diff --git a/batch-quartz/src/main/java/com/spring/domain/user/service/PasswordChangeService.java b/batch-quartz/src/main/java/com/spring/domain/user/service/ChangePasswordService.java similarity index 86% rename from batch-quartz/src/main/java/com/spring/domain/user/service/PasswordChangeService.java rename to batch-quartz/src/main/java/com/spring/domain/user/service/ChangePasswordService.java index 2be635e..0c2ed16 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/service/PasswordChangeService.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/service/ChangePasswordService.java @@ -4,7 +4,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.spring.domain.user.dto.PasswordChangeRequest; +import com.spring.domain.user.dto.ChangePasswordRequest; import com.spring.domain.user.entity.AgentUser; import com.spring.domain.user.error.PasswordSameException; import com.spring.domain.user.error.UserNotFoundException; @@ -14,13 +14,13 @@ import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor -public class PasswordChangeService { +public class ChangePasswordService { private final AgentUserRepository agentUserRepository; private final PasswordEncoder passwordEncoder; @Transactional - public void changePassword(PasswordChangeRequest request) { + public void changePassword(ChangePasswordRequest request) { AgentUser user = agentUserRepository.findByUserId(request.getUserId()).orElseThrow(UserNotFoundException::new); if (passwordEncoder.matches(request.getNewPassword(), user.getPassword())) { throw new PasswordSameException(); diff --git a/batch-quartz/src/main/java/com/spring/domain/user/service/UserManagementService.java b/batch-quartz/src/main/java/com/spring/domain/user/service/UserManagementService.java new file mode 100644 index 0000000..a7a913c --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/domain/user/service/UserManagementService.java @@ -0,0 +1,30 @@ +package com.spring.domain.user.service; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.spring.domain.user.dto.ChangeUserRoleApproveRequest; +import com.spring.domain.user.entity.AgentUser; +import com.spring.domain.user.error.UserNotFoundException; +import com.spring.domain.user.repository.AgentUserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class UserManagementService { + + private final AgentUserRepository agentUserRepository; + + @Transactional + public void changeRoleApprove(List requests) { + for (ChangeUserRoleApproveRequest request : requests) { + AgentUser user = agentUserRepository.findByUserId(request.getUserId()).orElseThrow(UserNotFoundException::new); + user.changeUserRole(request.getUserRole()); + user.changeApproved(request.isApproved()); + } + } + +} diff --git a/batch-quartz/src/main/java/com/spring/domain/user/service/UserPrincipalService.java b/batch-quartz/src/main/java/com/spring/domain/user/service/UserPrincipalService.java index cf9f725..9852802 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/service/UserPrincipalService.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/service/UserPrincipalService.java @@ -12,12 +12,13 @@ import com.spring.domain.user.repository.AgentUserRepository; import com.spring.infra.security.domain.UserPrincipal; import com.spring.infra.security.error.SecurityAuthException; import com.spring.infra.security.error.SecurityExceptionRule; +import com.spring.infra.security.service.UserAuthenticationService; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor -public class UserPrincipalService implements UserDetailsService { +public class UserPrincipalService implements UserDetailsService, UserAuthenticationService { private final AgentUserRepository agentUserRepository; @@ -29,7 +30,8 @@ public class UserPrincipalService implements UserDetailsService { } @Transactional(readOnly = true) - public UserPrincipal getUser(String key) { + @Override + public UserPrincipal getUserDetails(String key) { return agentUserRepository.findById(UUID.fromString(key)) .orElseThrow(UserNotFoundException::new); } diff --git a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzConfig.java b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzConfig.java index 74a7fef..fd7549c 100644 --- a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzConfig.java @@ -77,7 +77,7 @@ public class QuartzConfig { factory.setDataSource(dataSource); factory.setTransactionManager(transactionManager); factory.setJobFactory(jobFactory); - factory.setAutoStartup(true); + factory.setAutoStartup(false); factory.setWaitForJobsToCompleteOnShutdown(true); return factory; } diff --git a/batch-quartz/src/main/java/com/spring/infra/security/config/PermittedURI.java b/batch-quartz/src/main/java/com/spring/infra/security/config/PermittedURI.java index 7223d07..42b65cb 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/config/PermittedURI.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/config/PermittedURI.java @@ -12,7 +12,7 @@ public enum PermittedURI { FAVICON_URI("/favicon.ico"), USER_CONFLICT_URI("/api/user/conflict/{userId}"), USER_SIGN_UP("/api/user/sign-up"), - PASSWOD_CHANGE("/api/user/password-change"), + PASSWOD_CHANGE("/api/user/change-password"), USER_SIGN_IN("/sign-in"), USER_SIGN_OUT("/sign-out"); diff --git a/batch-quartz/src/main/java/com/spring/infra/security/error/SecurityExceptionRule.java b/batch-quartz/src/main/java/com/spring/infra/security/error/SecurityExceptionRule.java index 2206c9a..40f4ea7 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/error/SecurityExceptionRule.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/error/SecurityExceptionRule.java @@ -17,6 +17,7 @@ public enum SecurityExceptionRule implements ErrorRule { USER_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "사용자 인증에 실패 하였습니다."), USER_NOT_PASSWORD(HttpStatus.UNAUTHORIZED, "비밀번호가 틀립니다."), USER_FORBIDDEN(HttpStatus.FORBIDDEN, "사용자 권한이 없습니다."), + USER_NOT_APPROVED(HttpStatus.FORBIDDEN, "사용자가 승인되지 않았습니다."), JWT_TOKEN_ERROR(HttpStatus.UNAUTHORIZED, "토큰이 잘못되었습니다."), JWT_TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "토큰 정보가 없습니다."), SIGNATURE_ERROR(HttpStatus.UNAUTHORIZED, "토큰이 유효하지 않습니다."), diff --git a/batch-quartz/src/main/java/com/spring/infra/security/filter/AuthenticationProcessingFilter.java b/batch-quartz/src/main/java/com/spring/infra/security/filter/AuthenticationProcessingFilter.java index 5f69a65..5cba89a 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/filter/AuthenticationProcessingFilter.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/filter/AuthenticationProcessingFilter.java @@ -17,6 +17,7 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.util.StringUtils; import com.fasterxml.jackson.databind.ObjectMapper; +import com.spring.domain.user.entity.AgentUser; import com.spring.infra.security.config.PermittedURI; import com.spring.infra.security.dto.SignInRequest; import com.spring.infra.security.error.SecurityAuthException; @@ -47,7 +48,11 @@ public class AuthenticationProcessingFilter extends AbstractAuthenticationProces throw new SecurityAuthException(SecurityExceptionRule.USER_BAD_REQUEST); } var token = new UsernamePasswordAuthenticationToken(signInRequest.getUsername(), signInRequest.getPassword()); - return this.getAuthenticationManager().authenticate(token); + var authentication = this.getAuthenticationManager().authenticate(token); + if (!((AgentUser) authentication.getPrincipal()).isApproved()) { + throw new SecurityAuthException(SecurityExceptionRule.USER_NOT_APPROVED); + } + return authentication; } private boolean isValidRequestType(HttpServletRequest request) { diff --git a/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java b/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java index fd4f6e5..689524d 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java @@ -98,7 +98,7 @@ public final class JwtAuthenticationFilter extends OncePerRequestFilter { return Optional.ofNullable(request.getHeader(headerName)) .filter(token -> token.substring(0, 7).equalsIgnoreCase(JwtTokenRule.BEARER_PREFIX.getValue())) .map(token -> token.substring(7)) - .orElse(null); + .orElse(jwtTokenService.resolveTokenFromCookie(request, JwtTokenRule.ACCESS_PREFIX)); } } diff --git a/batch-quartz/src/main/java/com/spring/infra/security/filter/RedirectIfAuthenticatedFilter.java b/batch-quartz/src/main/java/com/spring/infra/security/filter/RedirectIfAuthenticatedFilter.java index 8842285..24a1ab3 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/filter/RedirectIfAuthenticatedFilter.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/filter/RedirectIfAuthenticatedFilter.java @@ -12,6 +12,9 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; +import com.spring.infra.security.config.PermittedURI; +import com.spring.infra.security.config.SecurityURI; + public class RedirectIfAuthenticatedFilter extends OncePerRequestFilter { @Override @@ -22,8 +25,8 @@ public class RedirectIfAuthenticatedFilter extends OncePerRequestFilter { ) throws ServletException, IOException { String requestURI = request.getRequestURI(); Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth != null && auth.isAuthenticated() && "/".equals(requestURI)) { - response.sendRedirect("/dashboard"); + if (auth != null && auth.isAuthenticated() && PermittedURI.ROOT_URI.getUri().equals(requestURI)) { + response.sendRedirect(SecurityURI.REDIRECT_URI.getUri()); return; } filterChain.doFilter(request, response); diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java index 02bd5d1..82724f0 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java @@ -1,6 +1,7 @@ package com.spring.infra.security.jwt; import java.security.Key; +import java.time.Duration; import java.util.List; import java.util.stream.Collectors; @@ -17,11 +18,11 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.spring.domain.user.service.UserPrincipalService; import com.spring.infra.security.domain.JwtUserPrincipal; import com.spring.infra.security.error.SecurityAuthException; import com.spring.infra.security.error.SecurityExceptionRule; import com.spring.infra.security.service.RefreshTokenService; +import com.spring.infra.security.service.UserAuthenticationService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; @@ -39,25 +40,27 @@ public class JwtTokenService { private final JwtTokenUtil jwtTokenUtil; private final JwtTokenGenerator jwtTokenGenerator; - private final UserPrincipalService userPrincipalService; + private final UserAuthenticationService userAuthenticationService; private final RefreshTokenService refreshTokenService; private final Key accessSecretKey; private final Key refreshSecretKey; + private final long accessExpiration; private final long refreshExpiration; public JwtTokenService( JwtTokenUtil jwtTokenUtil, JwtTokenGenerator jwtTokenGenerator, - UserPrincipalService userPrincipalService, + UserAuthenticationService userAuthenticationService, RefreshTokenService refreshTokenService, JwtProperties jwtProperties ) { this.jwtTokenUtil = jwtTokenUtil; this.jwtTokenGenerator = jwtTokenGenerator; - this.userPrincipalService = userPrincipalService; + this.userAuthenticationService = userAuthenticationService; this.refreshTokenService = refreshTokenService; this.accessSecretKey = jwtTokenUtil.getSigningKey(jwtProperties.getAccessToken().getSecret()); this.refreshSecretKey = jwtTokenUtil.getSigningKey(jwtProperties.getRefreshToken().getSecret()); + this.accessExpiration = jwtProperties.getAccessToken().getExpiration(); this.refreshExpiration = jwtProperties.getRefreshToken().getExpiration(); } @@ -70,6 +73,8 @@ public class JwtTokenService { */ public String generateAccessToken(HttpServletResponse response, Authentication authentication) { String accessToken = jwtTokenGenerator.generateAccessToken(authentication); + ResponseCookie cookie = setTokenToCookie(JwtTokenRule.ACCESS_PREFIX.getValue(), accessToken, accessExpiration); + response.addHeader(JwtTokenRule.JWT_ISSUE_HEADER.getValue(), cookie.toString()); response.setHeader(HttpHeaders.AUTHORIZATION, JwtTokenRule.BEARER_PREFIX.getValue() + accessToken); return accessToken; } @@ -97,13 +102,13 @@ public class JwtTokenService { * @param maxAgeSeconds 쿠키 유효 시간(초) * @return 생성된 ResponseCookie 객체 */ - private ResponseCookie setTokenToCookie(String tokenPrefix, String token, long maxAgeSeconds) { + private ResponseCookie setTokenToCookie(String tokenPrefix, String token, long maxAgeMinutes) { return ResponseCookie.from(tokenPrefix, token) .path("/") - .maxAge(maxAgeSeconds * 60) + .maxAge(Duration.ofMinutes(maxAgeMinutes)) .httpOnly(true) - .sameSite("Lax") - .secure(false) + .sameSite("None") + .secure(true) .build(); } @@ -178,7 +183,7 @@ public class JwtTokenService { * @return 생성된 Authentication 객체 */ public Authentication getAuthentication(String token) { - UserDetails user = userPrincipalService.getUser(getUserPk(token)); + UserDetails user = userAuthenticationService.getUserDetails(getUserPk(token)); return new UsernamePasswordAuthenticationToken(user, "", null); } @@ -229,7 +234,9 @@ public class JwtTokenService { * @param response HTTP 응답 객체 */ public void deleteCookie(HttpServletResponse response) { + Cookie accessCookie = jwtTokenUtil.resetToken(JwtTokenRule.ACCESS_PREFIX); Cookie refreshCookie = jwtTokenUtil.resetToken(JwtTokenRule.REFRESH_PREFIX); + response.addCookie(accessCookie); response.addCookie(refreshCookie); } diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenUtil.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenUtil.java index 780566a..9843dea 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenUtil.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenUtil.java @@ -17,7 +17,6 @@ import io.jsonwebtoken.Jwts; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SignatureException; -import lombok.extern.slf4j.Slf4j; /** * JWT 토큰 관련 유틸리티 기능을 제공하는 클래스입니다. @@ -27,7 +26,6 @@ import lombok.extern.slf4j.Slf4j; * @author mindol * @version 1.0 */ -@Slf4j @Component public class JwtTokenUtil { diff --git a/batch-quartz/src/main/java/com/spring/infra/security/service/UserAuthenticationService.java b/batch-quartz/src/main/java/com/spring/infra/security/service/UserAuthenticationService.java new file mode 100644 index 0000000..cc57a46 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/infra/security/service/UserAuthenticationService.java @@ -0,0 +1,7 @@ +package com.spring.infra.security.service; + +import org.springframework.security.core.userdetails.UserDetails; + +public interface UserAuthenticationService { + UserDetails getUserDetails(String key); +} diff --git a/batch-quartz/src/main/java/com/spring/web/controller/SignController.java b/batch-quartz/src/main/java/com/spring/web/controller/SignController.java index 7bc7bc6..f42eb76 100644 --- a/batch-quartz/src/main/java/com/spring/web/controller/SignController.java +++ b/batch-quartz/src/main/java/com/spring/web/controller/SignController.java @@ -1,5 +1,8 @@ package com.spring.web.controller; +import java.util.List; +import java.util.stream.Collectors; + import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -12,8 +15,11 @@ import com.spring.domain.user.entity.AgentUserRole; public class SignController { @GetMapping - public String signIn(Model model) { - model.addAttribute("roles", AgentUserRole.values()); + public String signIn(Model model) { + List roles = List.of(AgentUserRole.values()).stream() + .filter(role -> !role.getRole().equals(AgentUserRole.ROLE_SUPER.name())) + .collect(Collectors.toList()); + model.addAttribute("roles", roles); return "pages/sign/sign-in"; } diff --git a/batch-quartz/src/main/java/com/spring/web/controller/UserController.java b/batch-quartz/src/main/java/com/spring/web/controller/UserController.java new file mode 100644 index 0000000..597d450 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/web/controller/UserController.java @@ -0,0 +1,26 @@ +package com.spring.web.controller; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.spring.domain.user.entity.AgentUserRole; + +@Controller +@RequestMapping("/user") +public class UserController { + + @GetMapping("/management") + public String management(Model model) { + List roles = List.of(AgentUserRole.values()).stream() + .filter(role -> !role.getRole().equals(AgentUserRole.ROLE_SUPER.name())) + .collect(Collectors.toList()); + model.addAttribute("roles", roles); + return "pages/user/user-management"; + } + +} diff --git a/batch-quartz/src/main/resources/application.yml b/batch-quartz/src/main/resources/application.yml index 5ea7ac5..1fca658 100644 --- a/batch-quartz/src/main/resources/application.yml +++ b/batch-quartz/src/main/resources/application.yml @@ -42,18 +42,18 @@ spring: jpa: open-in-view: false database-platform: org.hibernate.dialect.H2Dialect + #show-sql: true hibernate: ddl-auto: create properties: hibernate: dialect: org.hibernate.dialect.H2Dialect "[format_sql]": true # 쿼리 로그 포맷 (정렬) - "[show_sql]": false # 쿼리 로그 출력 + #"[show_sql]": true # 쿼리 로그 출력 "[highlight_sql]": true # 쿼리 하이라이트 "[use_sql_comments]": true # SQL 주석 사용 naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl - show-sql: true batch: job: @@ -133,6 +133,7 @@ logging: level: org: hibernate: - SQL: DEBUG + SQL: debug type: - descriptor: TRACE \ No newline at end of file + descriptor: + sql: trace \ No newline at end of file diff --git a/batch-quartz/src/main/resources/static/js/apis/sign-api.js b/batch-quartz/src/main/resources/static/js/apis/sign-api.js index 227f2ad..65754c3 100644 --- a/batch-quartz/src/main/resources/static/js/apis/sign-api.js +++ b/batch-quartz/src/main/resources/static/js/apis/sign-api.js @@ -25,7 +25,7 @@ const signService = { }, changePassword: async (userId, newPassword) => { - const response = await apiClient.post('/api/user/password-change', { userId, newPassword }); + const response = await apiClient.post('/api/user/change-password', { userId, newPassword }); return response.data.data; } diff --git a/batch-quartz/src/main/resources/static/js/apis/user-api.js b/batch-quartz/src/main/resources/static/js/apis/user-api.js new file mode 100644 index 0000000..6351413 --- /dev/null +++ b/batch-quartz/src/main/resources/static/js/apis/user-api.js @@ -0,0 +1,18 @@ +import apiClient from '../common/axios-instance.js'; + +const userService = { + getUserList: async () => { + const response = await apiClient.get('/api/user'); + return response.data; + }, + + changeRoleApprove: async (users) => { + await apiClient.put('/api/user/change-role-approve', users); + }, + + deleteUser: async (userId) => { + await apiClient.delete(`/api/user/${userId}`); + } +}; + +export default userService; \ No newline at end of file diff --git a/batch-quartz/src/main/resources/static/js/pages/user/user-management.js b/batch-quartz/src/main/resources/static/js/pages/user/user-management.js new file mode 100644 index 0000000..5a0c693 --- /dev/null +++ b/batch-quartz/src/main/resources/static/js/pages/user/user-management.js @@ -0,0 +1,59 @@ +import userService from '../../apis/user-api.js'; + +let users = []; // 사용자 목록을 저장할 배열 + +document.addEventListener('DOMContentLoaded', () => { + loadUserList(); + document.getElementById('saveChanges').addEventListener('click', saveChanges); +}); + +const loadUserList = async () => { + users = await userService.getUserList(); + const userTableBody = document.getElementById('userTableBody'); + userTableBody.innerHTML = ''; // 기존 내용 초기화 + + users.forEach(user => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${user.userId} + ${user.email} + ${user.userName} + + + + + + + + + + `; + userTableBody.appendChild(row); + }); +}; + +const saveChanges = async () => { + const updatedUsers = users.map(user => { + const selectElement = document.querySelector(`select[data-user-id="${user.id}"]`); + const checkboxElement = document.querySelector(`input[data-user-id="${user.id}"]`); + + return { + id: user.id, + role: selectElement.value, + isApproved: checkboxElement.checked + }; + }); + + await userService.updateUsers(updatedUsers); // 배열로 사용자 정보를 전송 + alert('모든 변경 사항이 저장되었습니다.'); + loadUserList(); // 사용자 목록 새로 고침 +}; + +const deleteUser = async (userId) => { + await userService.deleteUser(userId); + alert('사용자가 삭제되었습니다.'); + loadUserList(); // 사용자 목록 새로 고침 +}; \ No newline at end of file diff --git a/batch-quartz/src/main/resources/templates/pages/sign/sign-in.html b/batch-quartz/src/main/resources/templates/pages/sign/sign-in.html index 608b1d4..1ad5ac4 100644 --- a/batch-quartz/src/main/resources/templates/pages/sign/sign-in.html +++ b/batch-quartz/src/main/resources/templates/pages/sign/sign-in.html @@ -79,6 +79,12 @@ +
+
+ + +
+
diff --git a/batch-quartz/src/main/resources/templates/pages/user/user-management.html b/batch-quartz/src/main/resources/templates/pages/user/user-management.html new file mode 100644 index 0000000..f7f89c4 --- /dev/null +++ b/batch-quartz/src/main/resources/templates/pages/user/user-management.html @@ -0,0 +1,33 @@ + + + + User Management + + +
+
+

회원 관리

+ + + + + + + + + + + + + +
아이디이메일사용자명권한승인 여부작업
+ +
+
+ + + + \ No newline at end of file