| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.user.server.rpc; |
| |
| import com.google.gwt.user.client.rpc.RpcTokenException; |
| import com.google.gwt.user.client.rpc.XsrfToken; |
| import com.google.gwt.user.client.rpc.XsrfTokenService; |
| import com.google.gwt.user.server.Util; |
| import com.google.gwt.util.tools.Utility; |
| |
| import javax.servlet.http.Cookie; |
| |
| /** |
| * EXPERIMENTAL and subject to change. Do not use this in production code. |
| * <p> |
| * |
| * </p> |
| * RPC service to generate XSRF tokens. |
| * <p> |
| * Sample use of {@link XsrfTokenService}: |
| * |
| * <ol> |
| * <li> Add {@link XsrfTokenServiceServlet} to {@code web.xml}: |
| * |
| * <pre> |
| * <servlet> |
| * <servlet-name>xsrf</servlet-name> |
| * <servlet-class> |
| * com.google.gwt.user.server.rpc.XsrfTokenServiceServlet |
| * </servlet-class> |
| * </servlet> |
| * <servlet-mapping> |
| * <servlet-name>xsrf</servlet-name> |
| * <url-pattern>/gwt/xsrf</url-pattern> |
| * </servlet-mapping> |
| * </pre> |
| * |
| * <li> Specify session cookie name that is used for authentication. MD5 hash of |
| * the session cookie's value will be used as an XSRF token: |
| * |
| * <pre> |
| * <context-param> |
| * <param-name>gwt.xsrf.session_cookie_name</param-name> |
| * <param-value>JSESSIONID</param-value> |
| * </context-param> |
| * </pre> |
| * |
| * <li> To enforce XSRF token validation on each method call either mark RPC |
| * interface as XSRF protected using {@link XsrfProtect} annotation or extend |
| * {@link com.google.gwt.user.client.rpc.XsrfProtectedService} instead of |
| * RemoteService. Use {@link NoXsrfProtect} to mark methods as not requiring |
| * XSRF protection: |
| * |
| * <pre class="code"> |
| * public interface MyRpcService extends XsrfProtectedService { |
| * public void doStuff(); |
| * } |
| * </pre> |
| * |
| * <li> Ensure that RPC's servlet implementation extends {@link |
| * XsrfProtectedServiceServlet} instead of {@link RemoteServiceServlet}: |
| * |
| * <pre class="code"> |
| * public class MyRpcServiceServlet extends XsrfProtectedServiceServlet |
| * implements MyRpcService { |
| * |
| * public void doStuff() { |
| * // ... |
| * } |
| * } |
| * </pre> |
| * |
| * <li> Obtain {@link XsrfToken} and set it on the RPC end point: |
| * |
| * <pre class="code"> |
| * XsrfTokenServiceAsync xsrf = (XsrfTokenServiceAsync)GWT.create(XsrfTokenService.class); |
| * |
| * ((ServiceDefTarget)xsrf).setServiceEntryPoint(GWT.getModuleBaseURL() + "xsrf"); |
| * |
| * xsrf.getNewXsrfToken(new AsyncCallback<XsrfToken>() { |
| * public void onSuccess(XsrfToken result) { |
| * MyRpcServiceAsync rpc = (MyRpcServiceAsync)GWT.create(MyRpcService.class); |
| * ((HasRpcToken) rpc).setRpcToken(result); |
| * // make XSRF protected RPC call |
| * rpc.doStuff(new AsyncCallback<Void>() { |
| * // ... |
| * }); |
| * |
| * } |
| * |
| * public void onFailure(Throwable caught) { |
| * try { |
| * throw caught; |
| * } catch (RpcTokenException e) { |
| * // Can be thrown for several reasons: |
| * // - duplicate session cookie, which may be a sign of a cookie |
| * // overwrite attack |
| * // - XSRF token cannot be generated because session cookie isn't |
| * // present |
| * } catch (Throwable e) { |
| * // unexpected |
| * } |
| * }); |
| * </pre> |
| * </ol> |
| * </p> |
| * |
| * @see XsrfProtectedServiceServlet |
| * @see XsrfProtect |
| * @see NoXsrfProtect |
| */ |
| public class XsrfTokenServiceServlet extends RemoteServiceServlet |
| implements XsrfTokenService { |
| |
| /** |
| * Session cookie name initialization parameter. |
| */ |
| public static final String COOKIE_NAME_PARAM = |
| "gwt.xsrf.session_cookie_name"; |
| |
| static final String COOKIE_NAME_NOT_SET_ERROR_MSG = |
| "Session cookie name not set! Use '" + COOKIE_NAME_PARAM + |
| "' context-param to specify session cookie name"; |
| |
| /** |
| * Session cookie name. Cookie's value is used to generate XSRF cookie. |
| */ |
| private String sessionCookieName = null; |
| |
| /** |
| * Default constructor. |
| */ |
| public XsrfTokenServiceServlet() { |
| this(null); |
| } |
| |
| /** |
| * Alternative constructor that accepts session cookie name instead of getting |
| * it from {@link javax.servlet.ServletConfig} or {@link |
| * javax.servlet.ServletContext}. |
| */ |
| public XsrfTokenServiceServlet(String sessionCookieName) { |
| this.sessionCookieName = sessionCookieName; |
| } |
| |
| /** |
| * Generates and returns new XSRF token. |
| */ |
| public XsrfToken getNewXsrfToken() { |
| return new XsrfToken(generateTokenValue()); |
| } |
| |
| /** |
| * Servlet initialization. |
| */ |
| @Override |
| public void init() { |
| // do not overwrite values set via constructor |
| if (sessionCookieName == null) { |
| sessionCookieName = getInitParameterValue(COOKIE_NAME_PARAM); |
| } |
| if (sessionCookieName == null) { |
| throw new IllegalStateException(COOKIE_NAME_NOT_SET_ERROR_MSG); |
| } |
| } |
| |
| /** |
| * Generates new XSRF token. |
| * |
| * @return session cookie MD5 hash. |
| */ |
| private String generateTokenValue() { |
| if (sessionCookieName == null) { |
| throw new IllegalStateException(COOKIE_NAME_NOT_SET_ERROR_MSG); |
| } |
| // generate XSRF cookie using session cookie |
| Cookie sessionCookie = Util.getCookie(getThreadLocalRequest(), |
| sessionCookieName, false); |
| if (sessionCookie == null || sessionCookie.getValue() == null || |
| sessionCookie.getValue().length() == 0) { |
| throw new RpcTokenException("Session cookie is not set or empty! " + |
| "Unable to generate XSRF cookie"); |
| } |
| byte[] cookieBytes = sessionCookie.getValue().getBytes(); |
| return Utility.toHexString(Utility.getMd5Digest(cookieBytes)); |
| } |
| |
| /** |
| * Retrieves and returns specified initialization parameter first from |
| * {@link ServletConfig} followed by {@link ServletContext}, if former returns |
| * {@code null}. |
| */ |
| private String getInitParameterValue(String name) { |
| String paramValue = null; |
| paramValue = getServletConfig().getInitParameter(name); |
| if (paramValue == null) { |
| paramValue = getServletContext().getInitParameter(name); |
| } |
| return paramValue; |
| } |
| } |