Good day!
In one project, I needed to save contacts to Google Contacts. This is easy - you just need to log in through OAuth to Google and get the access key. But the fact is that in this case a transition is made to the Google site, where the authorization and confirmation of the application’s access to the contact data actually takes place. I intended to do work with a contact in the iframe, and in order to prevent clickjacking, Google does not allow it. So you need to do something so that the OAuth page opens in the main window, not in the frame. My solution is under the cut.
For a start, I tried to make a separate pop-up window - but it was regularly blocked by the browser, and I decided to abandon this idea. The next idea was to make a kind of “ascent” of the OAuth page from the frame to the main window.
In my project, the server part is written in Java for Google App Engine, and Google API 4 Java is used to work with Google Data. In version 1.7, two classes appeared - AbstractAppEngineAuthorizationCodeServlet and AbstractAppEngineAuthorizationCodeCallbackServlet. These classes assume the “inner kitchen” of authorization through OAuth, leaving the developer only to create the logic of interaction with Google services after authorization. Unfortunately, the redirect to the authorization page is rigidly sewn into them, and I did not want to rewrite these classes myself. I had to resort to the filter system.
')
Filters are a mechanism that allows you to perform specified actions before or after a page is formed by a servlet. In my case, the filter should detect the redirect that occurred during the servlet operation (to the OAuth page) and give the browser a page in which the redirected URL will be opened not in the frame, but in the main window using Javascript. The first idea was to look for the Location tag in the response headers, extract its value and make a JS redirect on it. But getting response header values appeared only in Servlets 3.0 (J2EE 6), and the Google Apps Engine (or the GAE plugin for Netbeans) uses only J2EE5. I had to invent other ways.
On the Internet, I came up with an interesting idea - to use the Decorator pattern to respond to get the value of the redirect. As a result of the implementation of this idea, such a filter was obtained (some methods were omitted to simplify the example):
public class Frame2Window implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { final StringBuffer loc = new StringBuffer(); loc.setLength(0); HttpServletResponseWrapper wrappedResp = new HttpServletResponseWrapper((HttpServletResponse)response) { @Override public void sendRedirect(String location) throws IOException{ loc.setLength(0); loc.append(location); } }; chain.doFilter(request, wrappedResp); if(loc.length() > 0) { HttpServletRequest req = (HttpServletRequest) request; req.setAttribute("newUrl", loc.toString()); request.getRequestDispatcher("/pages/util/frame2window.jsp").include(request, response); } } catch (Throwable t) {
In the doFilter method, we wrap the servlet response in a decorator — an anonymous class that inherits from HttpServletResponseWrapper. In this class, we override the sendRedirect method so that the passed URL is stored in a local variable of the doFilter method. The variable loc used in the closure must be declared as final, but remain modifiable - this is achieved by defining it not as a String, but as a StringBuffer.
For direct redirect from the frame to the main window, JSP is used:
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JS Redirect</title> </head> <script type="text/javascript"> (function() { if(window != window.top) window.top.location = "<%= request.getAttribute("newUrl") %>"; })(); </script> <body> </body> </html>