문의 시, 사용하시는 개발환경과 디벨로퍼스 앱ID를 알려주세요.
사용하는 개발환경은 스프링입니다. 앱 ID는 1117371입니다.
loadUser 메소드가 호출이 안돼서
핸들러까지 가지 못해서 오류가 발생하는 것으로 추측이 됩니다.
하지만 loadUser 메소드가 호출이 안되는 이유를 모르겠습니다.
2024-08-08 23:15:39 - CustomOauth2UserService 빈 등록 완료
2024-08-08 23:15:39 - Kakao Client ID: --- ( 정확함 ! )
2024-08-08 23:15:39 - Kakao Redirect URI: http://localhost:8080/auth/kakao/login/callback
2024-08-08 23:15:39 - Kakao Client Secret: --- ( 정확함 ! )
2024-08-08 23:15:40 - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2024-08-08 23:15:40 - Global AuthenticationManager configured with AuthenticationProvider bean with name authenticationProvider
2024-08-08 23:15:40 - Global AuthenticationManager configured with an AuthenticationProvider bean. UserDetailsService beans will not be used for username/password login. Consider removing the AuthenticationProvider bean. Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider.
2024-08-08 23:15:40 - OAuth2 UserService 설정됨
2024-08-08 23:09:52 - 카카오 로그인 콜백 호출됨, code: Y7q2BjArT2XztyH8LGv7UEW-WixVunHIJsRHSRhlwYuKTNrpYEwCwAAAAAQKKiURAAABkTJTGPop9hBbJybEWQ, state: 3s0asdt0
2024-08-08 23:09:52 - 카카오 토큰 요청 응답: <200 OK OK,{"access_token":"TYTJx6NkJSuHA-0PU0Vl_D59gWDVjj13AAAAAQo9cusAAAGRMlMaMUPPWzORmYVE","token_type":"bearer","refresh_token":"lF18mHJgYpNMFCs5JMQQOBbhZNHdBBwfAAAAAgo9cusAAAGRMlMaLkPPWzORmYVE","id_token":"eyJraWQiOiI5ZjI1MmRhZGQ1ZjIzM2Y5M2QyZmE1MjhkMTJmZWEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJlMzQzZWY5ZjY5NWJkMjZkOThhZTU2ZDBmYTI0ZTk2YSIsInN1YiI6IjM2NTYyODQ3OTQiLCJhdXRoX3RpbWUiOjE3MjMxMjYxOTIsImlzcyI6Imh0dHBzOi8va2F1dGgua2FrYW8uY29tIiwibmlja25hbWUiOiLsnbTtmITsmrAiLCJleHAiOjE3MjMxNDc3OTIsImlhdCI6MTcyMzEyNjE5MiwicGljdHVyZSI6Imh0dHBzOi8vdDEua2FrYW9jZG4ubmV0L2FjY291bnRfaW1hZ2VzL2RlZmF1bHRfcHJvZmlsZS5qcGVnLnR3Zy50aHVtYi5SMTEweDExMCJ9.lg3iAVqFfZK55RlL1cACaEXDGuOHBO-sgQgvWuBCjo6GIaGmrhZzC94qf6Ac70ZaQ21kyE2F_WVQq9bcF1JzgKKDet6jVwYm6hbMVSaZ30YGGf3Yw52XK1pnuimXg2cioLkueBPko6CUiggBcQSuRhlL5IUZX-AQmf22pXsUzvmC04jjkvl2c-5_7t-ZUNL2C26E2Yt7gI1ZmWuvCs--ZJSQEXNFkx55OVKqrSTZFMRDzn7KkzoDjHU_qld9DWeN48ZU3v4jPtI4LSV1Q3mQn-Vgx1L72vPL-sZHk819ocnCuPJcZ4__vGwXpJPUvYYcEp9mq4pN4DH5jk--p50Y5w","expires_in":21599,"scope":"profile_image openid profile_nickname","refresh_token_expires_in":5183999},[Date:"Thu, 08 Aug 2024 14:09:52 GMT", Content-Type:"application/json;charset=utf-8", Transfer-Encoding:"chunked", Connection:"keep-alive", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-XSS-Protection:"1; mode=block", X-Frame-Options:"DENY", X-Content-Type-Options:"nosniff", Kakao:"Talk", Access-Control-Allow-Origin:"*", Access-Control-Allow-Methods:"GET, POST, OPTIONS", Access-Control-Allow-Headers:"Authorization, KA, Origin, X-Requested-With, Content-Type, Accept"]>
2024-08-08 23:09:52 - 카카오 엑세스 토큰 요청 성공
2024-08-08 23:09:52 - 카카오 사용자 정보 요청 응답: <200 OK OK,{id=3656284794, connected_at=2024-08-08T09:12:59Z, properties={nickname=이현우, profile_image=http://t1.kakaocdn.net/account_images/default_profile.jpeg.twg.thumb.R640x640, thumbnail_image=http://t1.kakaocdn.net/account_images/default_profile.jpeg.twg.thumb.R110x110}, kakao_account={profile_nickname_needs_agreement=false, profile_image_needs_agreement=false, profile={nickname=이현우, thumbnail_image_url=http://t1.kakaocdn.net/account_images/default_profile.jpeg.twg.thumb.R110x110, profile_image_url=http://t1.kakaocdn.net/account_images/default_profile.jpeg.twg.thumb.R640x640, is_default_image=true, is_default_nickname=false}}},[Date:"Thu, 08 Aug 2024 14:09:52 GMT", Server:"Apache", Access-Control-Allow-Origin:"*", Access-Control-Allow-Methods:"GET, POST, PUT, DELETE, OPTIONS", Access-Control-Allow-Headers:"Content-Type,X-Requested-With,Accept,Authorization,Origin,KA,Cache-Control,Pragma", X-Request-ID:"74a5e752-54bc-4c55-ba35-9adbfbd894d7", Quota-Type:"INC_AND_CHECK", Content-Type:"application/json;charset=UTF-8", Content-Length:"673", Keep-Alive:"timeout=10, max=500", Connection:"Keep-Alive"]>
2024-08-08 23:09:52 - 카카오 로그인 성공, 사용자 정보: {id=3656284794, connected_at=2024-08-08T09:12:59Z, properties={개인정보 대체}, kakao_account={profile_nickname_needs_agreement=false, profile_image_needs_agreement=false, profile={개인정보 대체}}}
2024-08-08 23:09:53 - Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [148] milliseconds.
2024-08-08 23:09:53 - SecurityContextHolder 설정 완료, 인증 정보: UsernamePasswordAuthenticationToken [Principal={id=3656284794, connected_at=2024-08-08T09:12:59Z, properties={정보 받아옴. 개인정보라 대체}, kakao_account={profile_nickname_needs_agreement=false, profile_image_needs_agreement=false, profile={정보 받아옴]]
2024-08-08 23:09:53 - Securing GET /home_user
2024-08-08 23:09:53 - Set SecurityContextHolder to anonymous SecurityContext
2024-08-08 23:09:53 - Saved request http://localhost:8080/home_user?continue to session
2024-08-08 23:09:53 - 권한 인증 에러 error: Full authentication is required to access this resource
시큐리티설정 다 잘 한거 같은데 어디가 문제인지 모르겠습니다.
살려주세요…
http
.oauth2Login(oauth -> oauth
.loginPage("/home/login")
.userInfoEndpoint(userInfo -> {
log.info("OAuth2 UserService 설정됨");
userInfo.userService(customOauth2UserService);
})
.successHandler((request, response, authentication) -> {
log.info("OAuth2 로그인 성공: " + authentication.getName());
SecurityContextHolder.getContext().setAuthentication(authentication);
new SimpleUrlAuthenticationSuccessHandler("/home_user").onAuthenticationSuccess(request, response, authentication);
})
.failureHandler((request, response, exception) -> {
log.error("OAuth2 로그인 실패: " + exception.getMessage());
response.sendRedirect("/home/login?error=true");
})
);
http
.exceptionHandling(exception -> {
log.info("Configuring exceptionHandling");
exception.authenticationEntryPoint((request, response, authException) -> {
**log.error("권한 인증 에러 error: {}", authException.getMessage());**
response.sendRedirect("/home/login?error=true");
});
});
@Slf4j
@Controller
public class NaverCallbackHandler {
@Autowired
NaverApi naverApi;
private static final String FIXED_STATE = "3s0asdt0";
@GetMapping("/auth/naver/login/callback")
public String handleNaverCallback(@RequestParam("code") String code,
@RequestParam("state") String state,
HttpServletRequest request) {
log.info("네이버 로그인 콜백 호출됨, code: {}, state: {}", code, state);
if (!FIXED_STATE.equals(state)) {
log.error("잘못된 state 값입니다.");
return "redirect:/home/login?error=true";
}
String accessToken = getAccessToken(code, state);
if (accessToken == null) {
log.error("액세스 토큰을 가져오지 못했습니다.");
return "redirect:/home/login?error=true";
}
// 이후 액세스 토큰을 사용하여 사용자 정보를 받아오는 로직을 추가합니다.
Map<String, Object> userInfo = getUserInfo(accessToken);
if (userInfo == null) {
log.error("사용자 정보를 가져오지 못했습니다.");
return "redirect:/home/login?error=true";
}
log.info("로그인 성공, 사용자 정보: {}", userInfo);
// 세션에 사용자 정보 저장
request.getSession().setAttribute("user", userInfo);
Authentication authentication = new UsernamePasswordAuthenticationToken(userInfo, null,
List.of(new SimpleGrantedAuthority("ROLE_USER")));
SecurityContextHolder.getContext().setAuthentication(authentication);
log.info("SecurityContextHolder 설정 완료, 인증 정보: {}", SecurityContextHolder.getContext().getAuthentication());
return "redirect:/home_user";
}
private String getAccessToken(String code, String state) {
RestTemplate restTemplate = new RestTemplate();
String tokenUrl = "https://nid.naver.com/oauth2.0/token";
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(tokenUrl)
.queryParam("grant_type", "authorization_code")
.queryParam("client_id", naverApi.getNaverClientId())
.queryParam("client_secret", naverApi.getNaverClientSecret())
.queryParam("code", code)
.queryParam("state", state);
ResponseEntity<Map<String, Object>> response = restTemplate.exchange(
uriBuilder.toUriString(),
HttpMethod.POST,
null,
new ParameterizedTypeReference<>() {});
log.info("토큰 요청 응답: {}", response);
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
Map<String, Object> body = response.getBody();
log.info("토큰 응답 바디: {}", body);
return (String) body.get("access_token");
} else {
log.error("액세스 토큰 요청 실패: {}, 응답 바디: {}", response.getStatusCode(), response.getBody());
return null;
}
}
private Map<String, Object> getUserInfo(String accessToken) {
RestTemplate restTemplate = new RestTemplate();
String userInfoUrl = "https://openapi.naver.com/v1/nid/me";
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
HttpEntity<String> entity = new HttpEntity<>("", headers);
ResponseEntity<Map<String, Object>> response = restTemplate.exchange(
userInfoUrl,
HttpMethod.GET,
entity,
new ParameterizedTypeReference<>() {});
log.info("사용자 정보 요청 응답: {}", response);
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
return (Map<String, Object>) response.getBody().get("response");
} else {
log.error("사용자 정보 요청 실패: {}", response.getStatusCode());
return null;
}
}
}
@Service
@Slf4j
@RequiredArgsConstructor
public class CustomOauth2UserService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
@PostConstruct
public void init() {
log.info("CustomOauth2UserService 빈 등록 완료");
}
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
log.info("loadUser 메서드 호출됨");
OAuth2User oAuth2User = super.loadUser(userRequest);
log.info("userRequest.getAttributes : {}", super.loadUser(userRequest).getAttributes());
String provider = userRequest.getClientRegistration().getClientName();
OAuth2UserInfo oAuth2UserInfo = null;
if(provider.equals("Naver")){
log.info("네이버 로그인");
oAuth2UserInfo = new NaverUserDetails(oAuth2User.getAttributes());
} else if(provider.equals("Kakao")){
log.info("카카오 로그인");
oAuth2UserInfo = new KaKaoUserDetails(oAuth2User.getAttributes());
}
String providerId = oAuth2UserInfo.getProviderId();
String userId = provider + "_" + providerId;
String name = oAuth2UserInfo.getName();
String nickname = oAuth2UserInfo.getNickname();
String role = "ROLE_USER";
Optional<User> userOptional = Optional.ofNullable(userRepository.findByUserId(userId));
User user;
if (userOptional.isEmpty()) {
user = new User();
user.setUserId(userId);
user.setName(name);
user.setPassword(null);
user.setRole(role);
user.setNickname(nickname);
user.setProvider(provider);
userRepository.save(user);
log.info("새로운 사용자 저장: {}", userId);
} else {
user = userOptional.get();
log.info("기존 사용자 로그인: {}", userId);
}
return new CustomUserDetail(user, oAuth2User.getAttributes());
}
}
애플리케이션 설정은 잘 했습니다. 여러번 검토 했습니다!!