001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.socket.server.support;
018
019import java.util.Collection;
020import java.util.Collections;
021import java.util.Enumeration;
022import java.util.Map;
023
024import javax.servlet.http.HttpSession;
025
026import org.springframework.http.server.ServerHttpRequest;
027import org.springframework.http.server.ServerHttpResponse;
028import org.springframework.http.server.ServletServerHttpRequest;
029import org.springframework.lang.Nullable;
030import org.springframework.web.socket.WebSocketHandler;
031import org.springframework.web.socket.WebSocketSession;
032import org.springframework.web.socket.server.HandshakeInterceptor;
033
034/**
035 * An interceptor to copy information from the HTTP session to the "handshake
036 * attributes" map to made available via{@link WebSocketSession#getAttributes()}.
037 *
038 * <p>Copies a subset or all HTTP session attributes and/or the HTTP session id
039 * under the key {@link #HTTP_SESSION_ID_ATTR_NAME}.
040 *
041 * @author Rossen Stoyanchev
042 * @since 4.0
043 */
044public class HttpSessionHandshakeInterceptor implements HandshakeInterceptor {
045
046        /**
047         * The name of the attribute under which the HTTP session id is exposed when
048         * {@link #setCopyHttpSessionId(boolean) copyHttpSessionId} is "true".
049         */
050        public static final String HTTP_SESSION_ID_ATTR_NAME = "HTTP.SESSION.ID";
051
052
053        private final Collection<String> attributeNames;
054
055        private boolean copyAllAttributes;
056
057        private boolean copyHttpSessionId = true;
058
059        private boolean createSession;
060
061
062        /**
063         * Default constructor for copying all HTTP session attributes and the HTTP
064         * session id.
065         * @see #setCopyAllAttributes
066         * @see #setCopyHttpSessionId
067         */
068        public HttpSessionHandshakeInterceptor() {
069                this.attributeNames = Collections.emptyList();
070                this.copyAllAttributes = true;
071        }
072
073        /**
074         * Constructor for copying specific HTTP session attributes and the HTTP
075         * session id.
076         * @param attributeNames session attributes to copy
077         * @see #setCopyAllAttributes
078         * @see #setCopyHttpSessionId
079         */
080        public HttpSessionHandshakeInterceptor(Collection<String> attributeNames) {
081                this.attributeNames = Collections.unmodifiableCollection(attributeNames);
082                this.copyAllAttributes = false;
083        }
084
085
086        /**
087         * Return the configured attribute names to copy (read-only).
088         */
089        public Collection<String> getAttributeNames() {
090                return this.attributeNames;
091        }
092
093        /**
094         * Whether to copy all attributes from the HTTP session. If set to "true",
095         * any explicitly configured attribute names are ignored.
096         * <p>By default this is set to either "true" or "false" depending on which
097         * constructor was used (default or with attribute names respectively).
098         * @param copyAllAttributes whether to copy all attributes
099         */
100        public void setCopyAllAttributes(boolean copyAllAttributes) {
101                this.copyAllAttributes = copyAllAttributes;
102        }
103
104        /**
105         * Whether to copy all HTTP session attributes.
106         */
107        public boolean isCopyAllAttributes() {
108                return this.copyAllAttributes;
109        }
110
111        /**
112         * Whether the HTTP session id should be copied to the handshake attributes
113         * under the key {@link #HTTP_SESSION_ID_ATTR_NAME}.
114         * <p>By default this is "true".
115         * @param copyHttpSessionId whether to copy the HTTP session id.
116         */
117        public void setCopyHttpSessionId(boolean copyHttpSessionId) {
118                this.copyHttpSessionId = copyHttpSessionId;
119        }
120
121        /**
122         * Whether to copy the HTTP session id to the handshake attributes.
123         */
124        public boolean isCopyHttpSessionId() {
125                return this.copyHttpSessionId;
126        }
127
128        /**
129         * Whether to allow the HTTP session to be created while accessing it.
130         * <p>By default set to {@code false}.
131         * @see javax.servlet.http.HttpServletRequest#getSession(boolean)
132         */
133        public void setCreateSession(boolean createSession) {
134                this.createSession = createSession;
135        }
136
137        /**
138         * Whether the HTTP session is allowed to be created.
139         */
140        public boolean isCreateSession() {
141                return this.createSession;
142        }
143
144
145        @Override
146        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
147                        WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
148
149                HttpSession session = getSession(request);
150                if (session != null) {
151                        if (isCopyHttpSessionId()) {
152                                attributes.put(HTTP_SESSION_ID_ATTR_NAME, session.getId());
153                        }
154                        Enumeration<String> names = session.getAttributeNames();
155                        while (names.hasMoreElements()) {
156                                String name = names.nextElement();
157                                if (isCopyAllAttributes() || getAttributeNames().contains(name)) {
158                                        attributes.put(name, session.getAttribute(name));
159                                }
160                        }
161                }
162                return true;
163        }
164
165        @Nullable
166        private HttpSession getSession(ServerHttpRequest request) {
167                if (request instanceof ServletServerHttpRequest) {
168                        ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
169                        return serverRequest.getServletRequest().getSession(isCreateSession());
170                }
171                return null;
172        }
173
174        @Override
175        public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
176                        WebSocketHandler wsHandler, @Nullable Exception ex) {
177        }
178
179}