마지막으로 9단계와 10단계를 도전했다.
- 9단계 : 권한 확인
🔻 조건
- 유저에 권한을 추가합니다.
- 권한은 관리자, 일반 사용자 두 가지가 존재합니다.
- JWT를 발급할 때 유저의 권한 정보를 함께 넣어줍니다.
- 일정 수정 및 삭제는 관리자 권한이 있는 유저만 할 수 있습니다.
⚠️ 예외 처리
- 권한이 없는 유저의 경우 403을 반환합니다.
Jwt를 생성하면서도 권한도 같이 넣을 수 있으면서도, 그러한 권한을 선택할 열거형 UserRoleEnum을 생성한다.
권한은 사용자, 관리자로 총 2개 지정
public enum UserRoleEnum {
USER(Authority.USER), // 사용자 권한
ADMIN(Authority.ADMIN); // 관리자 권한
private final String authority;
UserRoleEnum(String authority) {
this.authority = authority;
}
public String getAuthority() {
return this.authority;
}
public static class Authority {
public static final String USER = "ROLE_USER";
public static final String ADMIN = "ROLE_ADMIN";
}
}
이러한 권한이 있는 컬럼을 만들기 위해 User에 권한 필드를 추가하고, UserRequestDto에도 추가한다. 또한 UserService에서도 유저 생성할 때 사용자 권한도 확인한다.
권한 필드를 추가한다.
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;
User 클래스 생성자를 만들때 권한도 넣는다.
public User(String username, String email, String password, UserRoleEnum role) {
this.username = username;
this.email = email;
this.password = password;
this.role = role;
}
사용자 요청 데이터를 전달할 때 권한과 관리자 키도 포함한다.
@Getter
@Setter
public class UserRequestDto {
private String username;
private String password;
private String email;
private boolean admin = false;
private String adminToken = "";
}
입력받은 사용자 권한을 확인
// 사용자 ROLE 확인
UserRoleEnum role = UserRoleEnum.USER; // 기본값: USER
if (userRequestDto.isAdmin()) {
if (!ADMIN_TOKEN.equals(userRequestDto.getAdminToken())) {
throw new IllegalArgumentException("관리자 암호가 틀려 등록이 불가능합니다.");
}
role = UserRoleEnum.ADMIN;
}
JWT를 생성할 때 권한도 함께
// JWT 생성
String token = jwtUtil.createToken(user.getUsername(), role);
jwt를 생성할 때 권한도 함께 넣은 JwtUtil과 AuthFilter도 수정 및 추가
token을 생성할 때 권한도 추가
public String createToken(String username, UserRoleEnum role) {
Date date = new Date();
return BEARER_PREFIX +
Jwts.builder()
.setSubject(username)
.claim(AUTHORIZATION_KEY, role.getAuthority()) // 사용자 권한
.setExpiration(new Date(date.getTime() + TOKEN_TIME))
.setIssuedAt(date)
.signWith(key, signatureAlgorithm)
.compact();
}
'HttpServletRequest'로 변환한 request에 나중에 권한을 확인하기 위해 해당 값을 저장
request.setAttribute("role", info.get(JwtUtil.AUTHORIZATION_KEY));
유저, 댓글, 할일 등의 CRUD을 하면서 권한도 확인한다.(TodoController, UserController, CommentController)
String role = (String) httpServletRequest.getAttribute("role");
if (role == null || (!UserRoleEnum.USER.getAuthority().equals(role) && !UserRoleEnum.ADMIN.getAuthority().equals(role))) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
String role = (String) httpServletRequest.getAttribute("role");
if (role == null || !UserRoleEnum.ADMIN.getAuthority().equals(role)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
9단계도 잘 실행이 된다. 이제는 10단계를 할 것이다.
- 10단계 : 외부 API 조회
🔻 조건
- 날씨 정보 데이터 API를 활용하여 오늘의 날씨를 조회할 수 있습니다.
- RestTemplate을 사용해 날씨를 조회합니다.
- 일정에 날씨 필드를 추가합니다.
- 일정 생성 시에 날씨 정보를 생성일 기준으로 저장할 수 있습니다.
외부 API를 조회할 WeatherApIService를 생성한다.
@Slf4j(topic = "Weather API")
@Service
public class WeatherApIService {
private final RestTemplate restTemplate;
public WeatherApIService(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
public String getWeatherToday() {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("링크 이름")
.build()
.toUri();
log.info("uri = " + uri);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
return fromsontoString(responseEntity.getBody());
}
private String fromsontoString(String responseEntity) {
// 현재 날짜 가져오기
String todayDate = LocalDate.now().format(DateTimeFormatter.ofPattern("MM-dd"));
// JSON 응답 처리
JSONArray weatherArray = new JSONArray(responseEntity);
for (int i=0; i<weatherArray.length(); i++) {
JSONObject weatherObject = weatherArray.getJSONObject(i);
String date = weatherObject.getString("date");
String weather = weatherObject.getString("weather");
// 현재 날짜와 일치하는 지 확인
if (todayDate.equals(date)) {
return weather;
}
}
return "No weather data about date in Weather API";
}
}
Todo 엔티티에 날씨 컬럼을 추가하고, TodoResponseDto에 응답 받을 때 날씨를 추가한다.
@Column(name = "weather")
private String weather;
public Todo(String title, String content, String weather) {
this.title = title;
this.content = content;
this.weather = weather;
}
밑에 추가
private String weather;
this.weather = todo.getWeather();
일정을 생성할 때 날씨도 넣기 위해 TodoService에 추가
// 날씨 정보 조회
String weather = weatherAPIService.getWeatherToday();
// RequestDto -> Entity
Todo todo = new Todo(requestDto.getTitle(), requestDto.getContent(), weather);
날씨 데이터가 저장된다.