Skip to content

Commit 67e9860

Browse files
committed
refactor(security): JWT/Principal에 team 노출 및 권한 체크 개선
1 parent c1af13a commit 67e9860

File tree

3 files changed

+43
-31
lines changed

3 files changed

+43
-31
lines changed

src/main/java/inha/gdgoc/domain/auth/controller/AuthController.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import inha.gdgoc.domain.auth.service.MailService;
1515
import inha.gdgoc.domain.auth.service.RefreshTokenService;
1616
import inha.gdgoc.domain.user.entity.User;
17+
import inha.gdgoc.domain.user.enums.TeamType;
1718
import inha.gdgoc.domain.user.enums.UserRole;
1819
import inha.gdgoc.domain.user.repository.UserRepository;
1920
import inha.gdgoc.global.config.jwt.TokenProvider;
@@ -165,15 +166,30 @@ public ResponseEntity<ApiResponse<Void, Void>> resetPassword(@RequestBody Passwo
165166
* 예) /api/v1/auth/LEAD, /api/v1/auth/ORGANIZER, /api/v1/auth/ADMIN
166167
*/
167168
@GetMapping("/{role}")
168-
public ResponseEntity<ApiResponse<Void, ?>> checkRole(@AuthenticationPrincipal TokenProvider.CustomUserDetails me, @PathVariable UserRole role) {
169+
public ResponseEntity<ApiResponse<Void, ?>> checkRoleOrTeam(@AuthenticationPrincipal TokenProvider.CustomUserDetails me, @PathVariable UserRole role, @RequestParam(value = "team", required = false) TeamType requiredTeam) {
170+
// 1) 인증 체크
169171
if (me == null) {
170172
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
171173
.body(ApiResponse.error(GlobalErrorCode.UNAUTHORIZED_USER.getStatus()
172174
.value(), GlobalErrorCode.UNAUTHORIZED_USER.getMessage(), null));
173175
}
174176

175-
if (UserRole.hasAtLeast(me.getRole(), role)) {
176-
return ResponseEntity.ok(ApiResponse.ok("ROLE_CHECK_PASSED", null));
177+
// 2) role check
178+
final boolean roleOk = UserRole.hasAtLeast(me.getRole(), role);
179+
180+
// 3) team check if team parameter exists
181+
boolean teamOk = false;
182+
if (requiredTeam != null) {
183+
if (UserRole.hasAtLeast(me.getRole(), UserRole.ORGANIZER)) {
184+
teamOk = true;
185+
} else {
186+
teamOk = (me.getTeam() != null && me.getTeam() == requiredTeam);
187+
}
188+
}
189+
190+
// 4) OR 조건으로 최종 판정
191+
if (roleOk || teamOk) {
192+
return ResponseEntity.ok(ApiResponse.ok("ROLE_OR_TEAM_CHECK_PASSED", null));
177193
}
178194

179195
return ResponseEntity.status(HttpStatus.FORBIDDEN)

src/main/java/inha/gdgoc/global/config/jwt/TokenProvider.java

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
11
package inha.gdgoc.global.config.jwt;
22

3-
import static inha.gdgoc.global.exception.GlobalErrorCode.INVALID_JWT_REQUEST;
4-
53
import inha.gdgoc.domain.auth.enums.LoginType;
64
import inha.gdgoc.domain.user.entity.User;
75
import inha.gdgoc.domain.user.enums.TeamType;
86
import inha.gdgoc.domain.user.enums.UserRole;
97
import inha.gdgoc.global.exception.BusinessException;
108
import io.jsonwebtoken.*;
11-
import java.time.Duration;
12-
import java.util.Base64;
13-
import java.util.Collection;
14-
import java.util.Collections;
15-
import java.util.Date;
16-
import java.util.Set;
179
import lombok.Getter;
1810
import lombok.RequiredArgsConstructor;
1911
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -22,9 +14,15 @@
2214
import org.springframework.security.core.authority.SimpleGrantedAuthority;
2315
import org.springframework.stereotype.Service;
2416

17+
import java.time.Duration;
18+
import java.util.*;
19+
20+
import static inha.gdgoc.global.exception.GlobalErrorCode.INVALID_JWT_REQUEST;
21+
2522
@RequiredArgsConstructor
2623
@Service
2724
public class TokenProvider {
25+
2826
private final JwtProperties jwtProperties;
2927

3028
// 자체 로그인용 토큰 생성
@@ -44,8 +42,7 @@ public String generateRefreshToken(User user, Duration expiredAt, LoginType logi
4442
return makeToken(new Date(now.getTime() + expiredAt.toMillis()), user, loginType);
4543
}
4644

47-
public Claims validToken(String token) throws ExpiredJwtException, UnsupportedJwtException,
48-
MalformedJwtException, SignatureException, IllegalArgumentException {
45+
public Claims validToken(String token) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
4946
return getClaims(token);
5047
}
5148

@@ -62,32 +59,31 @@ public Authentication getAuthentication(String token) {
6259
String roleStr = claims.get("role", String.class);
6360
if (roleStr == null) throw new BusinessException(INVALID_JWT_REQUEST);
6461
UserRole userRole = UserRole.valueOf(roleStr);
65-
String roleName = "ROLE_" + userRole.name();
66-
Set<SimpleGrantedAuthority> authorities =
67-
Collections.singleton(new SimpleGrantedAuthority(roleName));
6862

69-
// team (선택) - 토큰에는 enum name(String)으로 저장됨. null/오타는 무시.
63+
// 권한 세트 구성
64+
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
65+
// 1) 역할 권한
66+
authorities.add(new SimpleGrantedAuthority("ROLE_" + userRole.name()));
67+
68+
// 2) 팀 권한 (선택)
7069
TeamType team = null;
7170
String teamStr = claims.get("team", String.class);
7271
if (teamStr != null && !teamStr.isBlank()) {
7372
try {
7473
team = TeamType.valueOf(teamStr);
74+
authorities.add(new SimpleGrantedAuthority("TEAM_" + team.name()));
7575
} catch (IllegalArgumentException ignored) {
76-
// 구버전 토큰 또는 잘못된 값이면 null 유지
7776
}
7877
}
7978

80-
CustomUserDetails userDetails =
81-
new CustomUserDetails(userId, username, "", authorities, userRole, team);
79+
CustomUserDetails userDetails = new CustomUserDetails(userId, username, "", authorities, userRole, team);
8280

8381
return new UsernamePasswordAuthenticationToken(userDetails, null, authorities);
8482
}
8583

8684
private String makeToken(Date expiry, User user, LoginType loginType) {
8785
Date now = new Date();
88-
String issuer = (loginType == LoginType.SELF_SIGNUP)
89-
? jwtProperties.getSelfIssuer()
90-
: jwtProperties.getGoogleIssuer();
86+
String issuer = (loginType == LoginType.SELF_SIGNUP) ? jwtProperties.getSelfIssuer() : jwtProperties.getGoogleIssuer();
9187

9288
// team: enum name 저장(예: "PR_DESIGN"), 없으면 null
9389
String teamEnumName = (user.getTeam() == null) ? null : user.getTeam().name();
@@ -102,8 +98,8 @@ private String makeToken(Date expiry, User user, LoginType loginType) {
10298
.claim("loginType", loginType.name())
10399
.claim("role", user.getUserRole().name())
104100
.claim("team", teamEnumName)
105-
.signWith(SignatureAlgorithm.HS256,
106-
Base64.getEncoder().encodeToString(jwtProperties.getSecretKey().getBytes()))
101+
.signWith(SignatureAlgorithm.HS256, Base64.getEncoder()
102+
.encodeToString(jwtProperties.getSecretKey().getBytes()))
107103
.compact();
108104
}
109105

@@ -116,16 +112,12 @@ private Claims getClaims(String token) {
116112

117113
@Getter
118114
public static class CustomUserDetails extends org.springframework.security.core.userdetails.User {
115+
119116
private final Long userId;
120117
private final UserRole role;
121118
private final TeamType team;
122119

123-
public CustomUserDetails(Long userId,
124-
String username,
125-
String password,
126-
Collection<? extends GrantedAuthority> authorities,
127-
UserRole role,
128-
TeamType team) {
120+
public CustomUserDetails(Long userId, String username, String password, Collection<? extends GrantedAuthority> authorities, UserRole role, TeamType team) {
129121
super(username, password, authorities);
130122
this.userId = userId;
131123
this.role = role;

src/main/java/inha/gdgoc/global/exception/BusinessException.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ public BusinessException(ErrorCode errorCode) {
1212
this.errorCode = errorCode;
1313
}
1414

15+
public BusinessException(ErrorCode errorCode, String message) {
16+
super(message);
17+
this.errorCode = errorCode;
18+
}
1519
}

0 commit comments

Comments
 (0)