commit
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<ChangeUserRoleApproveRequest> requests) {
|
||||
userManagementService.changeRoleApprove(requests);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class PasswordChangeRequest {
|
||||
public class ChangePasswordRequest {
|
||||
|
||||
@NotBlank(message = "사용자ID는 필수값 입니다.")
|
||||
private final String userId;
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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<? extends GrantedAuthority> getAuthorities() {
|
||||
return Arrays.stream(AgentUserRole.values())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
@@ -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<ChangeUserRoleApproveRequest> requests) {
|
||||
for (ChangeUserRoleApproveRequest request : requests) {
|
||||
AgentUser user = agentUserRepository.findByUserId(request.getUserId()).orElseThrow(UserNotFoundException::new);
|
||||
user.changeUserRole(request.getUserRole());
|
||||
user.changeApproved(request.isApproved());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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, "토큰이 유효하지 않습니다."),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.spring.infra.security.service;
|
||||
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public interface UserAuthenticationService {
|
||||
UserDetails getUserDetails(String key);
|
||||
}
|
||||
@@ -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<AgentUserRole> 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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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<AgentUserRole> 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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
descriptor:
|
||||
sql: trace
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
18
batch-quartz/src/main/resources/static/js/apis/user-api.js
Normal file
18
batch-quartz/src/main/resources/static/js/apis/user-api.js
Normal file
@@ -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;
|
||||
@@ -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 = `
|
||||
<td>${user.userId}</td>
|
||||
<td>${user.email}</td>
|
||||
<td>${user.userName}</td>
|
||||
<td>
|
||||
<select class="form-select" data-user-id="${user.id}" data-user-role="${user.userRole}">
|
||||
<option value="USER" ${user.userRole === 'USER' ? 'selected' : ''}>USER</option>
|
||||
<option value="ADMIN" ${user.userRole === 'ADMIN' ? 'selected' : ''}>ADMIN</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" ${user.isApproved ? 'checked' : ''} data-user-id="${user.id}" data-user-approved="${user.isApproved}">
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" onclick="deleteUser('${user.id}')">삭제</button>
|
||||
</td>
|
||||
`;
|
||||
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(); // 사용자 목록 새로 고침
|
||||
};
|
||||
@@ -79,6 +79,12 @@
|
||||
<input type="text" class="form-control" id="userName" name="userName" placeholder="사용자명" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-envelope"></i></span>
|
||||
<input type="email" class="form-control" id="email" name="email" placeholder="이메일" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-shield-lock"></i></span>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{layouts/layout}"
|
||||
layout:fragment="content" lang="ko" xml:lang="ko">
|
||||
<head>
|
||||
<title>User Management</title>
|
||||
</head>
|
||||
<body>
|
||||
<section>
|
||||
<div class="container">
|
||||
<h2 class="mt-4">회원 관리</h2>
|
||||
<table class="table table-striped mt-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>아이디</th>
|
||||
<th>이메일</th>
|
||||
<th>사용자명</th>
|
||||
<th>권한</th>
|
||||
<th>승인 여부</th>
|
||||
<th>작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="userTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
<button class="btn btn-primary" id="saveChanges">저장</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script type="module" th:src="@{/js/pages/user/user-management.js}" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user