This repository has been archived by the owner on Nov 29, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 481
/
HttpSessionStorage.java
179 lines (163 loc) · 6.61 KB
/
HttpSessionStorage.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* Copyright 2009 Vladimir Schäfer
*
* 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
*
* https://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 org.springframework.security.saml.storage;
import org.opensaml.xml.XMLObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.saml.parser.SAMLObject;
import org.springframework.util.Assert;
import org.springframework.web.util.WebUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Set;
/**
* Class implements storage of SAML messages and uses HttpSession as underlying dataStore. As the XMLObjects
* can't be serialized (which could lead to problems during failover), the messages are transformed into SAMLObject
* which internally marshalls the content into XML during serialization.
*
* Messages are populated to a Hashtable and stored inside HttpSession. The Hashtable is lazily initialized
* during first attempt to create or retrieve a message.
*
* @author Vladimir Schäfer
*/
public class HttpSessionStorage implements SAMLMessageStorage {
/**
* Class logger.
*/
protected final Logger log = LoggerFactory.getLogger(getClass());
/**
* Session the storage operates on.
*/
private final HttpSession session;
/**
* Internal storage for messages, corresponding to the object in session.
*/
private Hashtable<String, SAMLObject<XMLObject>> internalMessages;
/**
* Session key for storage of the hashtable.
*/
private static final String SAML_STORAGE_KEY = "_springSamlStorageKey";
/**
* Creates the storage object. The session is manipulated only once caller tries to store
* or retrieve a message.
*
* In case request doesn't already have a started session, it will be created.
*
* @param request request to load/store internalMessages from
*/
public HttpSessionStorage(HttpServletRequest request) {
Assert.notNull(request, "Request must be set");
this.session = request.getSession(true);
}
/**
* Creates the storage object. The session is manipulated only once caller tries to store
* or retrieve a message.
*
* @param session session to load/store internalMessages from
*/
public HttpSessionStorage(HttpSession session) {
Assert.notNull(session, "Session must be set");
this.session = session;
}
/**
* Stores a request message into the repository. RequestAbstractType must have an ID
* set. Any previous message with the same ID will be overwritten.
*
* @param messageID ID of message
* @param message message to be stored
*/
public void storeMessage(String messageID, XMLObject message) {
log.debug("Storing message {} to session {}", messageID, session.getId());
Hashtable<String, SAMLObject<XMLObject>> messages = getMessages();
messages.put(messageID, new SAMLObject<XMLObject>(message));
updateSession(messages);
}
/**
* Returns previously stored message with the given ID or null, if there is no message
* stored.
* <p>
* Message is stored in String format and must be unmarshalled into XMLObject. Call to this
* method may thus be expensive.
* <p>
* Messages are automatically cleared upon successful reception, as we presume that there
* are never multiple ongoing SAML exchanges for the same session. This saves memory used by
* the session.
*
* @param messageID ID of message to retrieve
* @return message found or null
*/
public XMLObject retrieveMessage(String messageID) {
Hashtable<String, SAMLObject<XMLObject>> messages = getMessages();
SAMLObject o = messages.get(messageID);
if (o == null) {
log.debug("Message {} not found in session {}", messageID, session.getId());
return null;
} else {
log.debug("Message {} found in session {}, clearing", messageID, session.getId());
messages.clear();
updateSession(messages);
return o.getObject();
}
}
/**
* @return all internalMessages currently stored in the storage
*/
public Set<String> getAllMessages() {
Hashtable<String, SAMLObject<XMLObject>> messages = getMessages();
return Collections.unmodifiableSet(messages.keySet());
}
/**
* Provides message storage hashtable. Table is lazily initialized when user tries to store or retrieve
* the first message.
*
* @return message storage
*/
private Hashtable<String, SAMLObject<XMLObject>> getMessages() {
if (internalMessages == null) {
internalMessages = initializeSession();
}
return internalMessages;
}
/**
* Call to the method tries to load internalMessages hashtable object from the session, if the object doesn't exist
* it will be created and stored.
* <p>
* Method synchronizes on session mutex to prevent two threads from overwriting each others hashtable.
*/
@SuppressWarnings("unchecked")
private Hashtable<String, SAMLObject<XMLObject>> initializeSession() {
Hashtable<String, SAMLObject<XMLObject>> messages = (Hashtable<String, SAMLObject<XMLObject>>) session.getAttribute(SAML_STORAGE_KEY);
if (messages == null) {
final Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
messages = (Hashtable<String, SAMLObject<XMLObject>>) session.getAttribute(SAML_STORAGE_KEY);
if (messages == null) {
messages = new Hashtable<String, SAMLObject<XMLObject>>();
updateSession(messages);
}
}
}
return messages;
}
/**
* Updates session with the internalMessages key. Some application servers require session value to be updated
* in order to replicate the session across nodes or persist it correctly.
*/
private void updateSession(Hashtable<String, SAMLObject<XMLObject>> messages) {
session.setAttribute(SAML_STORAGE_KEY, messages);
}
}