Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@
</table>

### 7. 관련 문서
1. [API문서](https://moddo.kro.kr/docs/index.html)
1. [API문서](https://api.moddo.kr/docs/index.html)
47 changes: 25 additions & 22 deletions src/main/java/com/dnd/moddo/global/jwt/auth/JwtAuth.java
Original file line number Diff line number Diff line change
@@ -1,40 +1,43 @@
package com.dnd.moddo.global.jwt.auth;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.dnd.moddo.global.jwt.exception.MissingTokenException;
import com.dnd.moddo.global.jwt.exception.TokenInvalidException;
import com.dnd.moddo.global.jwt.properties.JwtConstants;
import com.dnd.moddo.global.jwt.utill.JwtUtil;
import com.dnd.moddo.global.security.auth.AuthDetailsService;
import lombok.RequiredArgsConstructor;

import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class JwtAuth {
private final JwtUtil jwtUtil;
private final AuthDetailsService authDetailsService;
private final JwtUtil jwtUtil;
private final AuthDetailsService authDetailsService;

public Authentication getAuthentication(String token, String expectedTokenType) {
Claims claims = jwtUtil.getJwt(token).getBody();
public Authentication getAuthentication(String token, String expectedTokenType) {
Claims claims = jwtUtil.getJwt(token).getBody();

if (isNotExpectedToken(token, expectedTokenType)) {
throw new MissingTokenException();
}
if (isNotExpectedToken(token, expectedTokenType)) {
throw new MissingTokenException();
}

UserDetails userDetails = authDetailsService.loadUserByUsername(claims.get(JwtConstants.EMAIL.message).toString());
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
UserDetails userDetails = authDetailsService.loadUserByUsername(
claims.get(JwtConstants.AUTH_ID.message).toString());
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}

private boolean isNotExpectedToken(String token, String expectedTokenType) {
if (token == null || token.isEmpty()) {
throw new TokenInvalidException();
}
private boolean isNotExpectedToken(String token, String expectedTokenType) {
if (token == null || token.isEmpty()) {
throw new TokenInvalidException();
}

String role = jwtUtil.getJwt(token).getHeader().get(JwtConstants.TYPE.message).toString();
return !role.equals(expectedTokenType);
}
String role = jwtUtil.getJwt(token).getHeader().get(JwtConstants.TYPE.message).toString();
return !role.equals(expectedTokenType);
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
package com.dnd.moddo.global.security.auth;

import com.dnd.moddo.domain.user.exception.UserNotFoundException;
import com.dnd.moddo.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.dnd.moddo.domain.user.exception.UserNotFoundException;
import com.dnd.moddo.domain.user.repository.UserRepository;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
public class AuthDetailsService implements UserDetailsService {

private final UserRepository userRepository;
private final UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email)
.map(AuthDetails::new)
.orElseThrow(() -> new UserNotFoundException(email));
}
@Override
public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
return userRepository.findById(Long.parseLong(id))
.map(AuthDetails::new)
.orElseThrow(() -> new UserNotFoundException(id));
}
Comment on lines +21 to +26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

id 파싱 오류 처리 및 예외 메시지 오버로드 수정

  • Long.parseLong(id)에서 NumberFormatException 발생 시 현재 흐름상 500 가능성이 큽니다. 보안/안정성 측면에서 인증 실패(UsernameNotFoundException 등)로 매핑하는 것이 안전합니다.
  • not found 시 UserNotFoundException(Long) 오버로드를 써야 “아이디” 메시지가 노출됩니다. 현재 String 오버로드가 선택되어 “이메일” 메시지가 노출됩니다.

아래처럼 수정해 주세요.

- @Override
- public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
-     return userRepository.findById(Long.parseLong(id))
-         .map(AuthDetails::new)
-         .orElseThrow(() -> new UserNotFoundException(id));
- }
+ @Override
+ public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
+     if (id == null) {
+         throw new UsernameNotFoundException("id is null");
+     }
+     final Long userId;
+     try {
+         userId = Long.parseLong(id);
+     } catch (NumberFormatException e) {
+         // 인증 실패로 취급하여 401/404로 매핑되도록 UsernameNotFoundException 사용
+         throw new UsernameNotFoundException("invalid id: " + id);
+     }
+     return userRepository.findById(userId)
+         .map(AuthDetails::new)
+         .orElseThrow(() -> new UserNotFoundException(userId));
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
return userRepository.findById(Long.parseLong(id))
.map(AuthDetails::new)
.orElseThrow(() -> new UserNotFoundException(id));
}
@Override
public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
if (id == null) {
throw new UsernameNotFoundException("id is null");
}
final Long userId;
try {
userId = Long.parseLong(id);
} catch (NumberFormatException e) {
// 인증 실패로 취급하여 401/404로 매핑되도록 UsernameNotFoundException 사용
throw new UsernameNotFoundException("invalid id: " + id);
}
return userRepository.findById(userId)
.map(AuthDetails::new)
.orElseThrow(() -> new UserNotFoundException(userId));
}
🤖 Prompt for AI Agents
In src/main/java/com/dnd/moddo/global/security/auth/AuthDetailsService.java
around lines 21 to 26, handle NumberFormatException from Long.parseLong by
catching it and rethrowing a UsernameNotFoundException (so malformed id maps to
authentication failure), and when looking up the user use the Long overload of
UserNotFoundException for the not-found case (i.e., parse id to long first, then
call userRepository.findById(parsedId).map(AuthDetails::new).orElseThrow(() ->
new UserNotFoundException(parsedId))). Ensure no NumberFormatException can
escape and that the correct UserNotFoundException(Long) constructor is used.

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void setUp(final WebApplicationContext context,
.withRequestDefaults(
modifyUris()
.scheme("https")
.host("moddo.kro.kr")
.host("api.moddo.kr")
.removePort(),
prettyPrint()
)
Expand Down
Loading