Skip to content

Commit 981572a

Browse files
authored
[BACKEND] 토큰 권한 수정 및 관리자 용 로그인 추가 (#84)
## 📝작업 내용 - 이메일, 비밀번호 입력으로 로그인 - ADMIN 권한 추가 및 jwt 권한 세부 설정 - POST users/login: 관리자용 로그인(이메일, 비밀번호로 로그인 가능) - DELETE users/all: 관리자만 삭제 가능하도록 변경, 관리자 제외 삭제 - data.sql 테스트 용 기본 유저 추가
1 parent d1d989f commit 981572a

File tree

15 files changed

+102
-33
lines changed

15 files changed

+102
-33
lines changed

backend/src/main/java/com/cmg/comtogether/common/config/SwaggerConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public OpenAPI openAPI() {
3636
@Bean
3737
public OpenApiCustomizer oauthSecurityIgnoreCustomizer() {
3838
return openApi -> openApi.getPaths().forEach((path, pathItem) -> {
39-
if (path.startsWith("/oauth") || path.startsWith("/refresh") || path.startsWith("/users/all")) {
39+
if (path.startsWith("/users/login") || path.startsWith("/oauth") || path.startsWith("/refresh")) {
4040
pathItem.readOperations().forEach(operation -> operation.setSecurity(Collections.emptyList()));
4141
}
4242
});

backend/src/main/java/com/cmg/comtogether/common/exception/ErrorCode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public enum ErrorCode {
2020
TOKEN_EXPIRED(401, "AUTH-002", "토큰이 만료되었습니다."),
2121
INVALID_TOKEN(401, "AUTH-003", "유효하지 않은 토큰입니다."),
2222
FORBIDDEN(403, "AUTH-004", "접근 권한이 없습니다."),
23+
INVALID_PASSWORD(401, "AUTH-005", "비밀번호가 일치하지 않습니다."),
2324

2425
// 카카오 API
2526
OAUTH_INVALID_CODE(400, "OAUTH-000", "유효하지 않은 인가 코드입니다."),

backend/src/main/java/com/cmg/comtogether/common/security/CustomUserDetails.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,17 @@ public CustomUserDetails(User user) {
1919

2020
@Override
2121
public Collection<? extends GrantedAuthority> getAuthorities() {
22-
// Role 기반 권한
23-
return Collections.singleton(() -> "ROLE_USER");
22+
return Collections.singleton(() -> "ROLE_" + user.getRole().name());
2423
}
2524

2625
@Override
2726
public String getPassword() {
28-
return null;
27+
return user.getPassword();
2928
}
3029

3130
@Override
3231
public String getUsername() {
33-
return String.valueOf(user.getUserId());
32+
return user.getEmail();
3433
}
3534

3635
@Override

backend/src/main/java/com/cmg/comtogether/common/security/CustomUserDetailsService.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@ public class CustomUserDetailsService implements UserDetailsService {
1616
private final UserRepository userRepository;
1717

1818
@Override
19-
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
20-
User user = userRepository.findById(Long.valueOf(userId))
19+
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
20+
User user = userRepository.findByEmail(email)
21+
.orElseThrow(() -> new UsernameNotFoundException(ErrorCode.USER_NOT_FOUND.getMessage()));
22+
return new CustomUserDetails(user);
23+
}
24+
25+
public UserDetails loadUserById(Long userId) {
26+
User user = userRepository.findById(userId)
2127
.orElseThrow(() -> new UsernameNotFoundException(ErrorCode.USER_NOT_FOUND.getMessage()));
2228
return new CustomUserDetails(user);
2329
}

backend/src/main/java/com/cmg/comtogether/common/security/config/SecurityConfig.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, CorsConfigurat
4141
"/v3/api-docs/**",
4242
"/products/**",
4343
"/guide/**",
44-
"/users/all",
45-
"/glossary/**"
44+
"/glossary/**",
45+
"/users/login"
4646
).permitAll()
47+
.requestMatchers(
48+
"/users/all"
49+
).hasRole("ADMIN")
4750
.anyRequest().authenticated()
4851
)
4952
.exceptionHandling(ex -> ex

backend/src/main/java/com/cmg/comtogether/jwt/filter/JwtAuthenticationFilter.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@
1010
import lombok.RequiredArgsConstructor;
1111
import lombok.extern.slf4j.Slf4j;
1212
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
13-
import org.springframework.security.core.authority.SimpleGrantedAuthority;
1413
import org.springframework.security.core.context.SecurityContextHolder;
1514
import org.springframework.security.core.userdetails.UserDetails;
1615
import org.springframework.stereotype.Component;
1716
import org.springframework.web.filter.OncePerRequestFilter;
1817

1918
import java.io.IOException;
20-
import java.util.List;
2119

2220
@Slf4j
2321
@Component
@@ -43,19 +41,14 @@ protected void doFilterInternal(HttpServletRequest request,
4341
try {
4442
if (token != null) {
4543
jwtTokenProvider.validateToken(token);
46-
4744
Long userId = jwtTokenProvider.getUserId(token);
48-
String role = "ROLE_USER";
49-
50-
UserDetails userDetails =
51-
customUserDetailsService.loadUserByUsername(String.valueOf(userId));
45+
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
5246

5347
UsernamePasswordAuthenticationToken authentication =
5448
new UsernamePasswordAuthenticationToken(
5549
userDetails,
5650
null,
57-
List.of(new SimpleGrantedAuthority(role))
58-
);
51+
userDetails.getAuthorities());
5952

6053
SecurityContextHolder.getContext().setAuthentication(authentication);
6154
}

backend/src/main/java/com/cmg/comtogether/jwt/service/JwtService.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,23 @@
77
import com.cmg.comtogether.jwt.util.JwtTokenProvider;
88
import com.cmg.comtogether.jwt.dto.TokenDto;
99
import com.cmg.comtogether.user.entity.User;
10+
import com.cmg.comtogether.user.repository.UserRepository;
1011
import lombok.RequiredArgsConstructor;
1112
import org.springframework.stereotype.Service;
13+
import org.springframework.transaction.annotation.Transactional;
1214

1315
@Service
1416
@RequiredArgsConstructor
1517
public class JwtService {
1618

1719
private final JwtTokenProvider jwtTokenProvider;
1820
private final RefreshTokenRepository refreshTokenRepository;
21+
private final UserRepository userRepository;
1922

23+
@Transactional
2024
public TokenDto generateToken(User user) {
21-
String accessToken = jwtTokenProvider.createAccessToken(user.getUserId());
22-
String refreshToken = jwtTokenProvider.createRefreshToken(user.getUserId());
25+
String accessToken = jwtTokenProvider.createAccessToken(user);
26+
String refreshToken = jwtTokenProvider.createRefreshToken(user);
2327

2428
refreshTokenRepository.findByUserId(user.getUserId())
2529
.ifPresentOrElse(
@@ -33,14 +37,16 @@ public TokenDto generateToken(User user) {
3337
.build();
3438
}
3539

40+
@Transactional
3641
public TokenDto refreshAccessToken(String refreshToken) {
3742
jwtTokenProvider.validateToken(refreshToken);
3843
RefreshToken stored = refreshTokenRepository.findByRefreshToken(refreshToken)
3944
.orElseThrow(() -> new BusinessException(ErrorCode.UNAUTHORIZED));
4045

4146
Long userId = jwtTokenProvider.getUserId(refreshToken);
42-
String newAccessToken = jwtTokenProvider.createAccessToken(userId);
43-
String newRefreshToken = jwtTokenProvider.createRefreshToken(userId);
47+
User user = userRepository.findById(userId).orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
48+
String newAccessToken = jwtTokenProvider.createAccessToken(user);
49+
String newRefreshToken = jwtTokenProvider.createRefreshToken(user);
4450

4551
stored.updateToken(newRefreshToken);
4652
refreshTokenRepository.save(stored);

backend/src/main/java/com/cmg/comtogether/jwt/util/JwtTokenProvider.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.cmg.comtogether.common.exception.BusinessException;
44
import com.cmg.comtogether.common.exception.ErrorCode;
5+
import com.cmg.comtogether.user.entity.User;
56
import io.jsonwebtoken.ExpiredJwtException;
67
import io.jsonwebtoken.Jwts;
78
import io.jsonwebtoken.MalformedJwtException;
@@ -25,21 +26,21 @@ public class JwtTokenProvider {
2526
@Value("${jwt.refresh-token-expire-time}")
2627
private long refreshTokenExpireTime;
2728

28-
public String createAccessToken(Long userId) {
29-
return createToken(userId, accessTokenExpireTime);
29+
public String createAccessToken(User user) {
30+
return createToken(user, accessTokenExpireTime);
3031
}
3132

32-
public String createRefreshToken(Long userId) {
33-
return createToken(userId, refreshTokenExpireTime);
33+
public String createRefreshToken(User user) {
34+
return createToken(user, refreshTokenExpireTime);
3435
}
3536

36-
private String createToken(Long userId, long validity) {
37+
private String createToken(User user, long validity) {
3738
Date now = new Date();
3839
Date expiry = new Date(now.getTime() + validity);
3940

4041
return Jwts.builder()
41-
.setSubject(String.valueOf(userId))
42-
.claim("auth", "ROLE_USER")
42+
.setSubject(String.valueOf(user.getUserId()))
43+
.claim("auth", user.getRole())
4344
.claim("jti", UUID.randomUUID().toString())
4445
.setIssuedAt(now)
4546
.setExpiration(expiry)

backend/src/main/java/com/cmg/comtogether/user/controller/UserController.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.cmg.comtogether.common.response.ApiResponse;
44
import com.cmg.comtogether.common.security.CustomUserDetails;
5+
import com.cmg.comtogether.jwt.dto.TokenDto;
6+
import com.cmg.comtogether.user.dto.LoginRequestDto;
57
import com.cmg.comtogether.user.dto.UserInitializeRequestDto;
68
import com.cmg.comtogether.user.dto.UserResponseDto;
79
import com.cmg.comtogether.user.entity.User;
@@ -21,6 +23,13 @@ public class UserController {
2123

2224
private final UserService userService;
2325

26+
@PostMapping("/login")
27+
public ResponseEntity<ApiResponse<TokenDto>> login(
28+
@Valid @RequestBody LoginRequestDto loginRequestDto) {
29+
TokenDto tokenDto = userService.login(loginRequestDto.getEmail(), loginRequestDto.getPassword());
30+
return ResponseEntity.ok(ApiResponse.success(tokenDto));
31+
}
32+
2433
@PostMapping("/logout")
2534
public ResponseEntity<ApiResponse<Void>> logout(@AuthenticationPrincipal CustomUserDetails userDetails) {
2635
userService.logout(userDetails.getUser().getUserId());
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.cmg.comtogether.user.dto;
2+
3+
import jakarta.validation.constraints.Email;
4+
import jakarta.validation.constraints.NotBlank;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
9+
@Getter
10+
@Builder
11+
@AllArgsConstructor
12+
public class LoginRequestDto {
13+
14+
@Email
15+
@NotBlank(message = "이메일은 필수 값 입니다.")
16+
private String email;
17+
18+
@NotBlank(message = "비밀번호는 필수 값 입니다.")
19+
private String password;
20+
}

0 commit comments

Comments
 (0)