admin管理员组

文章数量:1122846

I am working on a calendar project using Google OAuth2 and the Google Calendar API. I have implemented a SuccessHandler to automatically save Credentials for Google Calendar API usage, differentiated by email, whenever a user logs in via Google OAuth2.

The issue is that when I call the API to fetch calendar events, even though there is an existing Credential for the email provided in the request parameter, the account selection window keeps appearing. After selecting an account, the OAuth authentication succeeds, but instead of executing the logic to fetch the calendar events, the Handler intercepts the request and processes it.

Here are my requirements:

When a user logs in via Google OAuth2:

Check if a Credential corresponding to the user's email exists in the file system. If a Credential exists, return a project-specific JWT token. If a Credential does not exist, create and save a new Credential.

When calling the API to fetch calendar events via the Google Calendar API:

Check if a Credential corresponding to the email provided in the request parameter exists in the file system. If a Credential exists, fetch the calendar events immediately without additional authentication steps. If a Credential does not exist, prompt the user to log in via OAuth2 to generate and save a new Credential.

I have attached my relevant code below for your reference. Thank you for your help.

@RequiredArgsConstructor
@Component
@Slf4j
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private final JwtTokenProvider jwtTokenProvider;
    private final GoogleAuthorizationService authorizationService;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
        String email = oAuth2User.getAttribute("email");

        if (email == null) {
            log.error("OAuth2 provider did not return email");
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Email not provided by OAuth2 provider");
            return;
        }

        try {
            authorizationService.getCredentials(email);
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }

        JwtToken jwtToken = jwtTokenProvider.generateToken(authentication);

        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");

        ObjectMapper objectMapper = new ObjectMapper();
        response.getWriter().write(objectMapper.writeValueAsString(jwtToken));
    }
}
@Service
public class GoogleAuthorizationService {

    private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
    private static final String TOKENS_DIRECTORY_PATH = "tokens";

    @Value("${google.credentials.file-path}")
    private String credentialsFilePath;

    @Value("#{'${google.scopes}'.split(',')}")
    private List<String> scopes;

    @Value("${spring.security.oauth2.client.registration.google.client-id}")
    private String clientId;

    @Value("${spring.security.oauth2.client.registration.google.client-secret}")
    private String clientSecret;

    public Credential getCredentials(String email) throws IOException, GeneralSecurityException {
        InputStream in = new FileInputStream(credentialsFilePath);
        if (in == null) {
            throw new FileNotFoundException("Resource not found: " + credentialsFilePath);
        }

        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
        NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

        FileDataStoreFactory dataStoreFactory = new FileDataStoreFactory(
                new java.io.File(TOKENS_DIRECTORY_PATH + "/" + sanitizeEmail(email))
        );

        Credential credential = getStoredCredential(dataStoreFactory, email);
        if (credential != null) {
            return credential;
        }

        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
                httpTransport, JSON_FACTORY, clientSecrets, scopes)
                .setDataStoreFactory(dataStoreFactory)
                .setAccessType("offline")
                .build();

        LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
        return new AuthorizationCodeInstalledApp(flow, receiver).authorize(email);
    }

    private String sanitizeEmail(String email) {
        return email.replaceAll("@", "_at_").replaceAll("\\.", "_dot_");
    }

    private Credential getStoredCredential(DataStoreFactory dataStoreFactory, String email) throws IOException, GeneralSecurityException {
        DataStore<StoredCredential> dataStore = StoredCredential.getDefaultDataStore(dataStoreFactory);
        StoredCredential storedCredential = dataStore.get(email);

        if (storedCredential != null) {
            Credential credential = new Credential.Builder(BearerToken.authorizationHeaderAccessMethod())
                    .setTransport(GoogleNetHttpTransport.newTrustedTransport())
                    .setJsonFactory(JSON_FACTORY)
                    .setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret))
                    .setTokenServerUrl(new GenericUrl(";))
                    .build()
                    .setAccessToken(storedCredential.getAccessToken())
                    .setRefreshToken(storedCredential.getRefreshToken());

            if (credential.getAccessToken() == null || credential.getExpiresInSeconds() != null && credential.getExpiresInSeconds() <= 0) {
                credential.refreshToken();
            }

            return credential;
        }
        return null;
    }
}
@Slf4j
@RequiredArgsConstructor
@Service
public class CalendarService {

    private static final String APPLICATION_NAME = "Google Calendar API";

    private final GoogleAuthorizationService googleAuthorizationService;

    public List<Event> getUpcomingEventsForCurrentMonth(String email, LocalDate date) throws IOException, GeneralSecurityException {
        Calendar service = new Calendar.Builder(
                GoogleNetHttpTransport.newTrustedTransport(),
                GsonFactory.getDefaultInstance(),
                googleAuthorizationService.getCredentials(email))
                .setApplicationName(APPLICATION_NAME)
                .build();

        ZonedDateTime startOfMonth = date.withDayOfMonth(1).atStartOfDay(ZoneId.of("Asia/Seoul"));
        ZonedDateTime endOfMonth = date.withDayOfMonth(date.lengthOfMonth()).atTime(23, 59, 59).atZone(ZoneId.of("Asia/Seoul"));

        DateTime timeMin = new DateTime(startOfMonth.toInstant().toEpochMilli());
        DateTime timeMax = new DateTime(endOfMonth.toInstant().toEpochMilli());
        log.info("Fetching events from {} to {}", timeMin, timeMax);

        Events events = service.events().list("primary")
                .setMaxResults(500)
                .setTimeMin(timeMin)
                .setTimeMax(timeMax)
                .setOrderBy("startTime")
                .setSingleEvents(true)
                .setTimeZone("Asia/Seoul")
                .execute();

        return events.getItems();
    }
}
@Slf4j
@RequiredArgsConstructor
@RestController
public class CalendarController {
    private final TaskFacadeService taskFacadeService;

    @GetMapping("/calendar/tasks/month")
    public ResponseEntity<Boolean> getUpcomingEvents(@RequestParam String email) throws IOException, GeneralSecurityException {
        boolean response = taskFacadeService.mapEventsToTasks(email, LocalDate.now());
        return ResponseEntity.ok(response);
    }
}

本文标签: javaGoogle Calendar API duplicated AuthorizationStack Overflow