Skip to content

Commit

Permalink
refactor: #45 URL pattern && MethodSecurity 방식으로 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
psychology50 committed Dec 21, 2023
1 parent b9130bd commit 4559af4
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 34 deletions.
39 changes: 28 additions & 11 deletions src/main/java/com/kcy/fitapet/domain/member/api/AccountApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import com.kcy.fitapet.domain.member.type.MemberAttrType;
import com.kcy.fitapet.domain.notification.type.NotificationType;
import com.kcy.fitapet.global.common.response.SuccessResponse;
import com.kcy.fitapet.global.common.response.code.ErrorCode;
import com.kcy.fitapet.global.common.response.code.StatusCode;
import com.kcy.fitapet.global.common.response.exception.GlobalErrorException;
import com.kcy.fitapet.global.common.security.authentication.CustomUserDetails;
import com.kcy.fitapet.global.common.redis.sms.SmsPrefix;
Expand All @@ -20,6 +22,8 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

Expand All @@ -34,9 +38,12 @@ public class AccountApi {
private final MemberAccountService memberAccountService;

@Operation(summary = "프로필 조회")
@GetMapping("")
public ResponseEntity<?> getProfile(@AuthenticationPrincipal CustomUserDetails user) {
AccountProfileRes member = memberAccountService.getProfile(user.getUserId());
@GetMapping("/{id}")
@PreAuthorize("isAuthenticated() and #id == principal.userId")
public ResponseEntity<?> getProfile(
@PathVariable("id") Long id,
@AuthenticationPrincipal CustomUserDetails user) {
AccountProfileRes member = memberAccountService.getProfile(id, user.getUserId());
return ResponseEntity.ok(SuccessResponse.from(member));
}

Expand All @@ -45,24 +52,28 @@ public ResponseEntity<?> getProfile(@AuthenticationPrincipal CustomUserDetails u
@Parameters({
@Parameter(name = "uid", description = "확인할 ID", required = true)
})
@PreAuthorize("isAnonymous()")
public ResponseEntity<?> getExistsUid(@RequestParam("uid") @NotBlank String uid) {
boolean exists = memberAccountService.existsUid(uid);
return ResponseEntity.ok(SuccessResponse.from(Map.of("valid", exists)));
}

@Operation(summary = "프로필(비밀번호/이름) 수정")
@Parameters({
@Parameter(name = "id", description = "수정할 프로필 ID", required = true),
@Parameter(name = "type", description = "수정할 프로필 타입", required = true),
@Parameter(name = "req", description = "수정할 프로필 정보")
})
@PutMapping("")
@PutMapping("/{id}")
@PreAuthorize("isAuthenticated() and #id == principal.userId")
public ResponseEntity<?> putProfile(
@PathVariable Long id,
@AuthenticationPrincipal CustomUserDetails user,
@RequestParam("type") @NotBlank MemberAttrType type,
@RequestBody ProfilePatchReq req
) {
log.info("type: {}", type);
memberAccountService.updateProfile(user.getUserId(), req, type);
memberAccountService.updateProfile(id, user.getUserId(), req, type);

return ResponseEntity.ok(SuccessResponse.noContent());
}
Expand All @@ -74,6 +85,7 @@ public ResponseEntity<?> putProfile(
@Parameter(name = "code", description = "인증번호", required = true),
@Parameter(name = "req", description = "uid(이름)/password(비밀번호)")
})
@PreAuthorize("isAnonymous()")
public ResponseEntity<?> postSearchIdOrPassword(
@RequestParam("type") @NotBlank SmsPrefix type,
@RequestParam("code") @NotBlank String code,
Expand All @@ -89,12 +101,17 @@ public ResponseEntity<?> postSearchIdOrPassword(
}

@Operation(summary = "알림 on/off")
@Parameter(name = "type", description = "알림 타입", example = "care or memo or schedule", required = true)
@GetMapping("/notify")
public ResponseEntity<?> putNotify(@AuthenticationPrincipal CustomUserDetails user, @RequestParam("type") @NotBlank NotificationType type) {
memberAccountService.updateNotification(user.getUserId(), type);
@Parameters({
@Parameter(name = "id", description = "알림 설정할 ID", required = true),
@Parameter(name = "type", description = "알림 타입", example = "care or memo or schedule", required = true)
})
@GetMapping("/{id}/notify")
@PreAuthorize("isAuthenticated() and #id == principal.userId")
public ResponseEntity<?> putNotify(
@PathVariable Long id,
@AuthenticationPrincipal CustomUserDetails user,
@RequestParam("type") @NotBlank NotificationType type) {
memberAccountService.updateNotification(id, user.getUserId(), type);
return ResponseEntity.ok(SuccessResponse.noContent());
}


}
6 changes: 6 additions & 0 deletions src/main/java/com/kcy/fitapet/domain/member/api/AuthApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

Expand Down Expand Up @@ -64,6 +65,7 @@ public class AuthApi {
@ApiResponse(responseCode = "4xx", description = "에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
})
@PostMapping("/register")
@PreAuthorize("isAnonymous()")
public ResponseEntity<?> signUp(@RequestHeader("Authorization") @NotBlank String accessToken, @RequestBody @Valid SignUpReq dto) {
Map<String, String> tokens = memberAuthService.register(accessToken, dto);
return getResponseEntity(tokens);
Expand All @@ -80,6 +82,7 @@ public ResponseEntity<?> signUp(@RequestHeader("Authorization") @NotBlank String
@ApiResponse(responseCode = "4xx", description = "에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
})
@PostMapping("/register-sms")
@PreAuthorize("isAnonymous()")
public ResponseEntity<?> registerSmsAuthorization(
@RequestParam(value = "code", required = false) String code,
@RequestBody @Valid SmsReq dto) {
Expand Down Expand Up @@ -108,6 +111,7 @@ public ResponseEntity<?> registerSmsAuthorization(
@ApiResponse(responseCode = "4xx", description = "에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
})
@PostMapping("/search-sms")
@PreAuthorize("isAnonymous()")
public ResponseEntity<?> searchSmsAuthorization(
@RequestParam(value = "type") SmsPrefix type,
@RequestParam(value = "code", required = false) String code,
Expand All @@ -131,6 +135,7 @@ public ResponseEntity<?> searchSmsAuthorization(
@ApiResponse(responseCode = "4xx", description = "에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
})
@PostMapping("/login")
@PreAuthorize("isAnonymous()")
public ResponseEntity<?> signIn(@RequestHeader(value = "Authorization", required = false) String accessToken, @RequestBody @Valid SignInReq dto) {
if (accessToken != null)
throw new GlobalErrorException(ErrorCode.ALREADY_LOGIN_USER);
Expand All @@ -149,6 +154,7 @@ public ResponseEntity<?> signIn(@RequestHeader(value = "Authorization", required
@ApiResponse(responseCode = "4xx", description = "에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
})
@GetMapping("/logout")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> signOut(
@AccessTokenInfo AccessToken accessToken,
@CookieValue(value = "refreshToken", required = false) @Valid String refreshToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.kcy.fitapet.domain.notification.type.NotificationType;
import com.kcy.fitapet.global.common.redis.sms.SmsCertificationService;
import com.kcy.fitapet.global.common.redis.sms.SmsPrefix;
import com.kcy.fitapet.global.common.response.code.ErrorCode;
import com.kcy.fitapet.global.common.response.code.StatusCode;
import com.kcy.fitapet.global.common.response.exception.GlobalErrorException;
import lombok.RequiredArgsConstructor;
Expand All @@ -31,7 +32,7 @@ public class MemberAccountService {
private final PasswordEncoder bCryptPasswordEncoder;

@Transactional(readOnly = true)
public AccountProfileRes getProfile(Long userId) {
public AccountProfileRes getProfile(Long requestId, Long userId) {
Member member = memberSearchService.findById(userId);
return AccountProfileRes.from(member);
}
Expand All @@ -42,7 +43,7 @@ public boolean existsUid(String uid) {
}

@Transactional
public void updateProfile(Long userId, ProfilePatchReq req, MemberAttrType type) {
public void updateProfile(Long requestId, Long userId, ProfilePatchReq req, MemberAttrType type) {
Member member = memberSearchService.findById(userId);

if (type == MemberAttrType.NAME) {
Expand Down Expand Up @@ -75,7 +76,7 @@ public void overwritePassword(AccountSearchReq req, String code, SmsPrefix prefi
}

@Transactional
public void updateNotification(Long userId, NotificationType type) {
public void updateNotification(Long requestId, Long userId, NotificationType type) {
Member member = memberSearchService.findById(userId);
member.updateNotificationFromType(type);
}
Expand Down Expand Up @@ -118,11 +119,11 @@ private void validatePassword(Member member, String prePassword, String newPassw
}

/**
* redis에 해당 전화번호에 대한 정보가 저장되어 있는 지 확인하고, <br/>
* in-memory에 해당 전화번호에 대한 정보가 저장되어 있는 지 확인하고, <br/>
* {"phone:"인증번호"} 형태의 인증번호가 일치함을 확인
* @param phone
* @param code
* @param prefix
* @param phone : 전화번호
* @param code : 인증번호
* @param prefix : 인증번호 타입
*/
private void validatePhone(String phone, String code, SmsPrefix prefix) {
if (!smsCertificationService.existsCertificationNumber(phone, prefix)) {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/kcy/fitapet/domain/oauth2/api/OAuthApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.kcy.fitapet.domain.oauth2.api;

import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "OAuth API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/auth/oauth")
@Slf4j
public class OAuthApi {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
public class SuccessResponse<T> {
@Schema(description = "응답 상태", defaultValue = "success")
private final String status = "success";
@Schema(description = "응답 코드", example = "200")
@Schema(description = "응답 코드", example = "data or no_content")
private Map<String, T> data;

@Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ private void sendAuthError(HttpServletResponse response, AuthErrorException e) t
response.setContentType("application/json;charset=UTF-8");
response.setStatus(e.getErrorCode().getHttpStatus().value());

AuthErrorResponse errorResponse = new AuthErrorResponse(e.getErrorCode().name(), e.getErrorCode().getMessage());
AuthErrorResponse errorResponse = new AuthErrorResponse("error", e.getErrorCode().getMessage());
objectMapper.writeValue(response.getWriter(), errorResponse);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.kcy.fitapet.global.common.security.oauth2;

public interface OAuth2Provider {

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public record AuthErrorResponse(String code, String message) {
@Override public String toString() {
return String.format("AuthErrorResponse(code=%s, message=%s)", code, message);
return "AuthErrorResponse(code=" + code + ", message=" + message + ")";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,10 @@
public class SecurityConfig {
private final JwtSecurityConfig jwtSecurityConfig;

private final String[] publicEndpoints = {
"/api/v1/test", "/api/v1/test/**",
private static final String[] publicReadOnlyPublicEndpoints = {
"/favicon.ico",

// Swagger
"/api-docs/**", "/v3/api-docs/**", "/swagger-ui/**", "/swagger",

// API
"/api/v1/auth/register", "/api/v1/auth/login", "/api/v1/auth/refresh",
"/api/v1/auth/register-sms/**", "/api/v1/auth/search-sms/**",
"/api/v1/accounts/search", "/api/v1/accounts/search/**",
};
private static final String[] publicReadOnlyPublicEndpoints = {
"/api/v1/accounts/exists", "/api/v1/accounts/exists/**"
};

@Bean
Expand Down Expand Up @@ -72,9 +62,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests(
auth -> auth.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.requestMatchers(HttpMethod.OPTIONS, "*").permitAll()
.requestMatchers(publicEndpoints).permitAll()
.requestMatchers(HttpMethod.GET, publicReadOnlyPublicEndpoints).permitAll()
.anyRequest().authenticated()
.anyRequest().permitAll()
)
.exceptionHandling(
exception -> exception
Expand Down

0 comments on commit 4559af4

Please sign in to comment.