TIL

TIL - 2024/06/25

기석김 2024. 6. 25. 18:47

팀 프로젝트가 끝났다. 나는 네이버 로그인을 마지막에 시도했지만, 뭔가 다 됐는데 안됐다.

ouath 생활코딩 영상을보고 좀 더 이해를 해야될거같다. 회고는 여기에 작성을 했다

https://kiseokkm.tistory.com/88

 

NBCamp Java 5th: Chapter 4 OutSourcing Project KPT 회고

팀프로젝트가 시작됐다.나는 리더를 모르고 신청을안했다. 사실 할까말까 고민하다가 안했다. 근데 우리팀에는 리더가 없다.할껄 그랬다. 나는 리더하고싶은 사람이 없을때 뽑기보다는 내가 자

kiseokkm.tistory.com

 

팀원들 모두 열심히 해주고, 소통도 잘 됐다. 온라인상에서 원래는 회의나 대화를할때 거의 캠을 끄고 진행을 하는데

이번 조는 다들 적극적이고 매우 좋았다. 리더가 미숙했지만, 모두가 다 리더처럼 잘 도와주셨다.

 

발표할때 다른조들도 보니깐 열심히 잘 한거같다. 모두 같이 성장하고있는거 보니깐 매우 좋다. 

 

일단 네이버 로그인 코드다. 아직 구현은 하지 못했다.

 

WebSecurityConfig

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http.csrf( (csrf) -> csrf.disable());

        http.sessionManagement( (sessionManagement) -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.authorizeHttpRequests( (authorizeHttpRequests) ->
            authorizeHttpRequests
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                .requestMatchers("/users/login", "/users/signup", "/api/users/refresh", "/users/login/kakao/**", "/users/login/naver/**").permitAll()
                .requestMatchers(HttpMethod.GET, "/orders", "/stores").permitAll()
                .requestMatchers("/admin/**").hasAuthority("ADMIN")
                .anyRequest().authenticated()
        );

 

Controller를 그냥 user에 넣어도 됐는데, 일단 만들어놨다. 추후에 바꿀예정이다

@RestController
@RequiredArgsConstructor
@RequestMapping("/users")
public class NaverLoginController {
    private final NaverLoginService naverLoginService;

    @GetMapping("/login/naver")
    public ResponseEntity<StatusCommonResponse> naverLogin(@RequestParam String code, @RequestParam String state, HttpServletResponse response) throws Exception {
        naverLoginService.loginWithNaver(code, state, response);

        StatusCommonResponse commonResponse = new StatusCommonResponse(200, "네이버 로그인 성공");
        return ResponseEntity.ok().body(commonResponse);
    }
}

 

Service

@Slf4j
@Service
@RequiredArgsConstructor
public class NaverLoginService {

    private final UserRepository userRepository;
    private final JwtUtil jwtUtil;
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    private final PasswordEncoder passwordEncoder;

    public void loginWithNaver(String code, String state, HttpServletResponse response) throws Exception {

        String accessToken = getAccessToken(code, state);

        NaverUserInfoDto naverUserInfo = getNaverUserInfo(accessToken);

        User naverUser = registerNaverUserIfNeeded(naverUserInfo);

        String jwtAccessToken = jwtUtil.generateAccessToken(naverUser.getUserId(), naverUser.getUserName(), naverUser.getUserAuth());
        String jwtRefreshToken = jwtUtil.generateRefreshToken(naverUser.getUserId(), naverUser.getUserName(), naverUser.getUserAuth());

        ResponseCookie refreshTokenCookie = jwtUtil.generateRefreshTokenCookie(jwtRefreshToken);

        naverUser.updateRefreshToken(jwtRefreshToken);
        userRepository.save(naverUser);

        response.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + jwtAccessToken);
        response.addHeader(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString());
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("application/json;charset=UTF-8");
    }

    private String getAccessToken(String code, String state) throws Exception {
        URI uri = UriComponentsBuilder
            .fromUriString("https://nid.naver.com/oauth2.0/token")
            .queryParam("grant_type", "authorization_code")
            .queryParam("client_id", "8mWUC54LGL2OeCnJ7vtZ")
            .queryParam("client_secret", "c_kAL5shZd")
            .queryParam("code", code)
            .queryParam("state", state)
            .build()
            .toUri();

        ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);

        JsonNode jsonNode = objectMapper.readTree(response.getBody());
        JsonNode accessTokenNode = jsonNode.get("access_token");

        if (accessTokenNode == null) {
            log.error("Failed to retrieve access token: {}", response.getBody());
            throw new IllegalStateException("Failed to retrieve access token");
        }

        return accessTokenNode.asText();
    }

    private NaverUserInfoDto getNaverUserInfo(String accessToken) throws Exception {
        URI uri = UriComponentsBuilder
            .fromUriString("https://openapi.naver.com/v1/nid/me")
            .build()
            .toUri();

        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + accessToken);
        HttpEntity<Void> entity = new HttpEntity<>(headers);

        ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);

        JsonNode jsonNode = objectMapper.readTree(response.getBody()).get("response");

        if (jsonNode == null || jsonNode.get("name") == null || jsonNode.get("email") == null) {
            throw new IllegalStateException("네이버 사용자 정보 응답에서 필수 필드가 누락");
        }

        String name = jsonNode.get("name").asText();
        String email = jsonNode.get("email").asText();

        return new NaverUserInfoDto(name, email);
    }

    private User registerNaverUserIfNeeded(NaverUserInfoDto naverUserInfo) {
        String email = naverUserInfo.getEmail();
        Optional<User> optionalUser = userRepository.findByEmail(email);
        User naverUser;

        if (optionalUser.isPresent()) {
            naverUser = optionalUser.get();
        } else {
            String password = passwordEncoder.encode(UUID.randomUUID().toString());
            naverUser = new User(
                email,
                password,
                naverUserInfo.getName(),
                email,
                UserStatus.ACTIVE,
                UserAuth.USER,
                null,
                null
            );
            userRepository.save(naverUser);
        }

        return naverUser;
    }
}

dto

@Getter
@NoArgsConstructor
public class NaverUserInfoDto {
    private String name;
    private String email;

    public NaverUserInfoDto(String name, String email) {
        this.name = name;
        this.email = email;
    }
}

User에도 추가 해줬다.

/**
 * 사용자 엔티티 클래스 데이터베이스의 사용자 정보를 나타내는 클래스
 */
@Entity
@Getter
@NoArgsConstructor
@Table(name = "users")
public class User extends TimeStamp {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true, length = 50)
    private String userId;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false, length = 50)
    private String userName;

    @Column(nullable = false, unique = true, length = 50)
    private String email;

    @Column(length = 100)
    private String intro;

    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    private UserAuth userAuth;

    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    private UserStatus userStatus;

    @Column(unique = true)
    private String refreshToken;

    @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
    private List<Menu> menu = new ArrayList<>();

    @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
    private List<Review> comments = new ArrayList<>();

    private Long kakaoId;

    private Long naverId;

    @ElementCollection(fetch = FetchType.EAGER) // FetchType 설정 추가
    @CollectionTable(name = "past_passwords", joinColumns = @JoinColumn(name = "user_id"))
    @Column(name = "password")
    private List<String> pastPasswords = new ArrayList<>();

    public User(SignupRequestDto requestDto, UserStatus userStatus, UserAuth userAuth) {
        this.userId = requestDto.getUserId();
        this.password = requestDto.getPassword();
        this.userName = requestDto.getUserName();
        this.email = requestDto.getEmail();
        this.intro = requestDto.getIntro();
        this.userStatus = userStatus;
        this.userAuth = userAuth;
        this.pastPasswords.add(this.password);
    }

    public User(String userId, String password, String userName, String email, UserStatus userStatus, UserAuth userAuth, Long kakaoId) {
        this.userId = userId;
        this.password = password;
        this.userName = userName;
        this.email = email;
        this.userStatus = userStatus;
        this.userAuth = userAuth;
        this.kakaoId = kakaoId;
        this.pastPasswords.add(this.password);
    }

    public User(String userId, String password, String userName, String email, UserStatus userStatus, UserAuth userAuth, Long kakaoId, Long naverId) {
        this.userId = userId;
        this.password = password;
        this.userName = userName;
        this.email = email;
        this.userStatus = userStatus;
        this.userAuth = userAuth;
        this.kakaoId = kakaoId;
        this.naverId = naverId;
        this.pastPasswords.add(this.password);
    }



    public void updateUserStatus(UserStatus userStatus) {
        this.userStatus = userStatus;
    }

    public void updateRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }

    public void updatePassword(String newPassword) {
        if (this.pastPasswords.size() >= 3) {
            this.pastPasswords.remove(0);
        }
        this.pastPasswords.add(newPassword);
        this.password = newPassword;
    }

    public void encryptionPassword(String password) {
        this.password = password;
    }

    public void updateUserName(String userName) {
        this.userName = userName;
    }

    public void updateIntro(String intro) {
        this.intro = intro;
    }

    public void updateProfile(AdminUserProfileRequestDto requestDto) {
        this.userName = requestDto.getUserName();
        this.intro = requestDto.getIntro();
    }

    public void updateAuth(UserAuth userAuth) {
        this.userAuth = userAuth;
    }

    public User kakaoIdUpdate(Long kakaoId) {
        this.kakaoId = kakaoId;
        return this;
    }
    public User naverIdUpdate(Long naverId) {
        this.naverId = naverId;
        return this;
    }
}

 

properties에도 추가를 해줬다.

spring.security.oauth2.client.registration.naver.client-id=8mWUC54LGL2OeCnJ7vtZ
spring.security.oauth2.client.registration.naver.client-secret=c_kAL5shZd
spring.security.oauth2.client.registration.naver.scope=name,email
spring.security.oauth2.client.registration.naver.client-name=Naver
spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.naver.redirect-uri=http://localhost:8080/login/oauth2/code/naver

spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize
spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token
spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me
spring.security.oauth2.client.provider.naver.user-name-attribute=response

 

최종코드 : https://github.com/GreedyPeople/GreedyPeople