Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 익명 로그인 API 구현 #84

Merged
merged 1 commit into from
Nov 4, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/members/login", "/h2-console/**", "/error", "/favicon.ico").permitAll()
.requestMatchers("/api/members/login", "/api/members/anonymous-login", "/h2-console/**",
"/error", "/favicon.ico").permitAll()
.requestMatchers("/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ private OpenApiCustomizer addSecurityItemToAllEndpointsExceptLogin() {
return openApi -> {
SecurityRequirement securityRequirement = new SecurityRequirement().addList("bearerAuth");
openApi.getPaths().forEach((path, item) -> {
if (!"/api/members/login".equals(path)) {
if (!"/api/members/login".equals(path) && !"/api/members/anonymous-login".equals(path)) {
item.readOperations().forEach(operation -> {
operation.addSecurityItem(securityRequirement);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.potatocake.everymoment.dto.SuccessResponse;
import com.potatocake.everymoment.dto.request.MemberLoginRequest;
import com.potatocake.everymoment.dto.response.AnonymousLoginResponse;
import com.potatocake.everymoment.dto.response.JwtResponse;
import com.potatocake.everymoment.dto.response.MemberDetailResponse;
import com.potatocake.everymoment.dto.response.MemberMyResponse;
Expand Down Expand Up @@ -39,6 +40,15 @@ public class MemberController {

private final MemberService memberService;

@Operation(summary = "익명 로그인", description = "회원번호로 로그인하거나 새로운 익명 계정을 생성합니다.")
@ApiResponse(responseCode = "200", description = "익명 로그인 성공")
@GetMapping("/anonymous-login")
public ResponseEntity<SuccessResponse> anonymousLogin(@Parameter(description = "기기에 저장된 회원번호 (없을 경우 새로운 계정 생성)")
@RequestParam(required = false) Long number) {
AnonymousLoginResponse response = memberService.anonymousLogin(number);
return ResponseEntity.ok(SuccessResponse.ok(response));
}

@Operation(summary = "로그인", description = "회원 번호와 닉네임으로 로그인합니다.")
@ApiResponse(responseCode = "200", description = "로그인 성공", content = @Content(schema = @Schema(implementation = JwtResponse.class)))
@PostMapping("/login")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.potatocake.everymoment.dto.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Builder;
import lombok.Getter;

@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
public class AnonymousLoginResponse {

private Long number;
private String token;

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.potatocake.everymoment.repository;

import com.potatocake.everymoment.entity.Member;
import jakarta.persistence.LockModeType;
import java.util.Optional;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Window;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;

public interface MemberRepository extends JpaRepository<Member, Long> {

Expand All @@ -15,4 +18,8 @@ public interface MemberRepository extends JpaRepository<Member, Long> {

Window<Member> findByNicknameContaining(String nickname, ScrollPosition position, Pageable pageable);

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT CASE WHEN MIN(m.number) > 0 OR MIN(m.number) IS NULL THEN -1 ELSE MIN(m.number) - 1 END FROM Member m")
Long findNextAnonymousNumber();

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

import static org.springframework.data.domain.Sort.Direction.ASC;

import com.potatocake.everymoment.dto.response.AnonymousLoginResponse;
import com.potatocake.everymoment.dto.response.FriendRequestStatus;
import com.potatocake.everymoment.dto.response.MemberDetailResponse;
import com.potatocake.everymoment.dto.response.MemberMyResponse;
Expand All @@ -13,6 +14,7 @@
import com.potatocake.everymoment.repository.FriendRepository;
import com.potatocake.everymoment.repository.FriendRequestRepository;
import com.potatocake.everymoment.repository.MemberRepository;
import com.potatocake.everymoment.util.JwtUtil;
import com.potatocake.everymoment.util.PagingUtil;
import com.potatocake.everymoment.util.S3FileUploader;
import java.util.List;
Expand All @@ -35,6 +37,7 @@ public class MemberService {
private final FriendRepository friendRepository;
private final PagingUtil pagingUtil;
private final S3FileUploader s3FileUploader;
private final JwtUtil jwtUtil;

@Transactional(readOnly = true)
public MemberSearchResponse searchMembers(String nickname, Long key, int size, Long currentMemberId) {
Expand Down Expand Up @@ -72,6 +75,39 @@ public MemberMyResponse getMemberInfo(Long memberId) {
.build();
}

public AnonymousLoginResponse anonymousLogin(Long memberNumber) {
if (memberNumber != null) {
// 기존 회원번호로 로그인 시도
return memberRepository.findByNumber(memberNumber)
.map(member -> AnonymousLoginResponse.builder()
.token(jwtUtil.create(member.getId()))
.build())
.orElseGet(this::createAnonymousLoginResponse);
}

// 새로운 익명 회원 생성 및 응답
return createAnonymousLoginResponse();
}

private AnonymousLoginResponse createAnonymousLoginResponse() {
Member newMember = createAnonymousMember();
return AnonymousLoginResponse.builder()
.number(newMember.getNumber())
.token(jwtUtil.create(newMember.getId()))
.build();
}

private Member createAnonymousMember() {
Long nextNumber = memberRepository.findNextAnonymousNumber();

Member member = Member.builder()
.nickname("Anonymous")
.number(nextNumber)
.build();

return memberRepository.save(member);
}

public void updateMemberInfo(Long id, MultipartFile profileImage, String nickname) {
Member member = memberRepository.findById(id)
.orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND));
Expand Down
Loading