Como autenticar usuários com o Java

Nesta parte do tutorial do Bookshelf para Java, você aprenderá a criar um fluxo de login para os usuários, além de usar as informações do perfil para fornecer a eles funcionalidades personalizadas.

Com o Google Identity Platform, você acessa facilmente as informações sobre os usuários e, ao mesmo tempo, garante que as credenciais de login deles sejam gerenciadas de maneira segura pelo Google. O OAuth 2.0 facilita o fornecimento de um fluxo de login para todos os usuários do app e concede ao seu app o acesso a informações básicas de perfil sobre os usuários autenticados.

Esta página faz parte de um tutorial com várias páginas. Para começar do início e ver as instruções de configuração, acesse App Bookshelf em Java.

Como criar um ID do cliente de aplicativo da Web

Com um ID do cliente do aplicativo da Web, seu aplicativo autoriza usuários e acessa as APIs do Google em nome deles.

  1. Acesse a seção de credenciais no Console do Google Cloud Platform.

  2. Clique na tela de consentimento do OAuth. Para o nome do produto, digite Java Bookshelf App. Preencha os campos opcionais relevantes. Clique em Salvar.

  3. Clique em Criar credenciais > ID do cliente do OAuth.

  4. Em Tipo de aplicativo, selecione Aplicativo da Web.

  5. Em Nome, digite Java Bookshelf Client.

  6. Em URIs de direcionamento autorizado, insira os seguintes URLs, um de cada vez. Substitua [YOUR_PROJECT_ID] pelo código do projeto:

    http://localhost:8080/oauth2callback
    http://[YOUR_PROJECT_ID].appspot.com/oauth2callback
    https://[YOUR_PROJECT_ID].appspot.com/oauth2callback
    http://[YOUR_PROJECT_ID].appspot-preview.com/oauth2callback
    https://[YOUR_PROJECT_ID].appspot-preview.com/oauth2callback

  7. Clique em Criar.

  8. Copie o ID do cliente e a chave secreta do cliente e salve para uso posterior.

Como definir configurações

1. No diretório getting-started-java/bookshelf/4-auth, abra pom.xml para edição.

  1. Na seção <properties>, defina callback.host como [YOUR_PROJECT_ID].appspot.com.

  2. Defina bookshelf.clientID como o ID do cliente criado anteriormente.

  3. Defina bookshelf.clientSecret como a chave secreta do cliente criada anteriormente.

  4. Salve e feche pom.xml.

Como executar o aplicativo na máquina local

Para executar o aplicativo localmente:

  1. No diretório getting-started-java/bookshelf/4-auth, digite o seguinte comando para iniciar um servidor da Web local:

    mvn -Plocal clean jetty:run-exploded -DprojectID=[YOUR-PROJECT-ID]
  2. No seu navegador, acesse http://localhost:8080.

Agora, navegue nas páginas da Web do app, faça login usando sua Conta do Google e adicione livros. Para vê-los, basta usar o link Meus Livros na parte superior da barra de navegação.

Como implantar o aplicativo no ambiente flexível do App Engine

  1. Digite este comando para implantar o app:

    mvn appengine:deploy -DprojectID=YOUR-PROJECT-ID
    
  2. No navegador da Web, digite este endereço. Substitua [YOUR_PROJECT_ID] pelo código do projeto:

    https://[YOUR_PROJECT_ID].appspot-preview.com
    

Para atualizar o app, implante a versão atualizada digitando o mesmo comando usado para implantá-lo pela primeira vez. A implantação cria uma nova versão do aplicativo e a define como padrão. As versões mais antigas são mantidas, bem como as instâncias de VM associadas. Esteja ciente de que essas versões e as instâncias de VM são recursos passíveis de cobrança.

Reduza custos excluindo as versões não padrão do app.

Para excluir uma versão do aplicativo:

  1. No Console do GCP, acesse a página "Versões do App Engine".

    Acessar a página Versões

  2. Clique na caixa de seleção ao lado da versão do aplicativo não padrão que você quer excluir.
  3. Clique no botão Excluir na parte superior da página para excluir a versão do aplicativo.

Para informações detalhadas sobre a remoção de recursos faturáveis, consulte a seção Como fazer a limpeza na etapa final deste tutorial.

Estrutura do aplicativo

Veja no diagrama os componentes do aplicativo e como eles se conectam.

Amostra de estrutura de autenticação

Como entender o código

Nesta seção, vamos analisar o código do aplicativo e explicar como ele funciona.

Quando o usuário clica em Login, o LoginServlet é invocado e faz o seguinte:

  1. Salva algum state aleatório para ajudar a evitar falsificações de solicitações.

  2. Salva o destino para onde ir após o login.

  3. Usa a biblioteca de cliente de APIs do Google, especificamente GoogleAuthorizationCodeFlow, para gerar uma solicitação de retorno de chamada ao Google e processar os logins. Este aplicativo especifica os escopos "email" e "profile" para poder exibir a imagem e o e-mail do usuário em cada página:

@WebServlet(name = "login", value = "/login")
@SuppressWarnings("serial")
public class LoginServlet extends HttpServlet {

  private static final Collection<String> SCOPES = Arrays.asList("email", "profile");
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();
  private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  private GoogleAuthorizationCodeFlow flow;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws IOException, ServletException {

    String state = new BigInteger(130, new SecureRandom()).toString(32);  // prevent request forgery
    req.getSession().setAttribute("state", state);

    if (req.getAttribute("loginDestination") != null) {
      req
          .getSession()
          .setAttribute("loginDestination", (String) req.getAttribute("loginDestination"));
    } else {
      req.getSession().setAttribute("loginDestination", "/books");
    }

    flow = new GoogleAuthorizationCodeFlow.Builder(
        HTTP_TRANSPORT,
        JSON_FACTORY,
        getServletContext().getInitParameter("bookshelf.clientID"),
        getServletContext().getInitParameter("bookshelf.clientSecret"),
        SCOPES)
        .build();

    // Callback url should be the one registered in Google Developers Console
    String url =
        flow.newAuthorizationUrl()
            .setRedirectUri(getServletContext().getInitParameter("bookshelf.callback"))
            .setState(state)            // Prevent request forgery
            .build();
    resp.sendRedirect(url);
  }
}

O Google redireciona o usuário para o URL /oauth2callback. Depois que o usuário faz login, o método Oauth2CallbackServlet doGet:

  1. verifica se o login não é uma falsificação, comparando nosso state com o state salvo da sessão;

  2. exclui o state salvo da sessão;

  3. recebe a resposta tokenResponse;

  4. usa a tokenResponse para conseguir uma Credential;

  5. usa a credential para criar uma requestFactory;

  6. usa a request para conseguir o jsonIdentity;

  7. extrai o e-mail, uma imagem e um código;

  8. redireciona o loginDestination salvo na etapa anterior.

@WebServlet(name = "oauth2callback", value = "/oauth2callback")
@SuppressWarnings("serial")
public class Oauth2CallbackServlet extends HttpServlet {

  private static final Collection<String> SCOPES = Arrays.asList("email", "profile");
  private static final String USERINFO_ENDPOINT
      = "https://www.googleapis.com/plus/v1/people/me/openIdConnect";
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();
  private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  private GoogleAuthorizationCodeFlow flow;

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException,
      ServletException {

    // Ensure that this is no request forgery going on, and that the user
    // sending us this connect request is the user that was supposed to.
    if (req.getSession().getAttribute("state") == null
        || !req.getParameter("state").equals((String) req.getSession().getAttribute("state"))) {
      resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      resp.sendRedirect("/books");
      return;
    }

    req.getSession().removeAttribute("state");     // Remove one-time use state.

    flow = new GoogleAuthorizationCodeFlow.Builder(
        HTTP_TRANSPORT,
        JSON_FACTORY,
        getServletContext().getInitParameter("bookshelf.clientID"),
        getServletContext().getInitParameter("bookshelf.clientSecret"),
        SCOPES).build();

    final TokenResponse tokenResponse =
        flow.newTokenRequest(req.getParameter("code"))
            .setRedirectUri(getServletContext().getInitParameter("bookshelf.callback"))
            .execute();

    req.getSession().setAttribute("token", tokenResponse.toString()); // Keep track of the token.
    final Credential credential = flow.createAndStoreCredential(tokenResponse, null);
    final HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(credential);

    final GenericUrl url = new GenericUrl(USERINFO_ENDPOINT);      // Make an authenticated request.
    final HttpRequest request = requestFactory.buildGetRequest(url);
    request.getHeaders().setContentType("application/json");

    final String jsonIdentity = request.execute().parseAsString();
    @SuppressWarnings("unchecked")
    HashMap<String, String> userIdResult =
        new ObjectMapper().readValue(jsonIdentity, HashMap.class);
    // From this map, extract the relevant profile info and store it in the session.
    req.getSession().setAttribute("userEmail", userIdResult.get("email"));
    req.getSession().setAttribute("userId", userIdResult.get("sub"));
    req.getSession().setAttribute("userImageUrl", userIdResult.get("picture"));
    resp.sendRedirect((String) req.getSession().getAttribute("loginDestination"));
  }
}

LogoutServlet exclui session e cria uma nova:

@WebServlet(name = "logout", value = "/logout")
@SuppressWarnings("serial")
public class LogoutServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws IOException, ServletException {
    // you can also make an authenticated request to logout, but here we choose to
    // simply delete the session variables for simplicity
    HttpSession session =  req.getSession(false);
    if (session != null) {
      session.invalidate();
    }
    // rebuild session
    req.getSession();
  }
}
Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…