SEC-1125: Created separate web module spring-security-web
This commit is contained in:
-131
@@ -1,131 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.concurrent;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
import org.springframework.security.ui.logout.LogoutHandler;
|
||||
import org.springframework.security.ui.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.util.UrlUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Filter required by concurrent session handling package.
|
||||
* <p>
|
||||
* This filter performs two functions. First, it calls
|
||||
* {@link org.springframework.security.concurrent.SessionRegistry#refreshLastRequest(String)} for each request
|
||||
* so that registered sessions always have a correct "last update" date/time. Second, it retrieves a
|
||||
* {@link org.springframework.security.concurrent.SessionInformation} from the <code>SessionRegistry</code>
|
||||
* for each request and checks if the session has been marked as expired.
|
||||
* If it has been marked as expired, the configured logout handlers will be called (as happens with
|
||||
* {@link org.springframework.security.ui.logout.LogoutFilter}), typically to invalidate the session.
|
||||
* A redirect to the expiredURL specified will be performed, and the session invalidation will cause an
|
||||
* {@link org.springframework.security.ui.session.HttpSessionDestroyedEvent} to be published via the
|
||||
* {@link org.springframework.security.ui.session.HttpSessionEventPublisher} registered in <code>web.xml</code>.</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ConcurrentSessionFilter extends SpringSecurityFilter implements InitializingBean {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private SessionRegistry sessionRegistry;
|
||||
private String expiredUrl;
|
||||
private LogoutHandler[] handlers = new LogoutHandler[] {new SecurityContextLogoutHandler()};
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(sessionRegistry, "SessionRegistry required");
|
||||
Assert.isTrue(expiredUrl == null || UrlUtils.isValidRedirectUrl(expiredUrl),
|
||||
expiredUrl + " isn't a valid redirect URL");
|
||||
}
|
||||
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
HttpSession session = request.getSession(false);
|
||||
|
||||
if (session != null) {
|
||||
SessionInformation info = sessionRegistry.getSessionInformation(session.getId());
|
||||
|
||||
if (info != null) {
|
||||
if (info.isExpired()) {
|
||||
// Expired - abort processing
|
||||
doLogout(request, response);
|
||||
|
||||
String targetUrl = determineExpiredUrl(request, info);
|
||||
|
||||
if (targetUrl != null) {
|
||||
targetUrl = request.getContextPath() + targetUrl;
|
||||
response.sendRedirect(response.encodeRedirectURL(targetUrl));
|
||||
} else {
|
||||
response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +
|
||||
"logins being attempted as the same user).");
|
||||
response.flushBuffer();
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
// Non-expired - update last request date/time
|
||||
info.refreshLastRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
protected String determineExpiredUrl(HttpServletRequest request, SessionInformation info) {
|
||||
return expiredUrl;
|
||||
}
|
||||
|
||||
private void doLogout(HttpServletRequest request, HttpServletResponse response) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
for (int i = 0; i < handlers.length; i++) {
|
||||
handlers[i].logout(request, response, auth);
|
||||
}
|
||||
}
|
||||
|
||||
public void setExpiredUrl(String expiredUrl) {
|
||||
this.expiredUrl = expiredUrl;
|
||||
}
|
||||
|
||||
public void setSessionRegistry(SessionRegistry sessionRegistry) {
|
||||
this.sessionRegistry = sessionRegistry;
|
||||
}
|
||||
|
||||
public void setLogoutHandlers(LogoutHandler[] handlers) {
|
||||
Assert.notNull(handlers);
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.CONCURRENT_SESSION_FILTER;
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,14 @@ import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Represents a record of a session within the Spring Security framework.
|
||||
* <p>This is primarily used for concurrent session support.</p>
|
||||
* <p>Sessions have three states: active, expired, and destroyed. A session can that is invalidated by
|
||||
* <p>
|
||||
* This is primarily used for concurrent session support.
|
||||
* <p>
|
||||
* Sessions have three states: active, expired, and destroyed. A session can that is invalidated by
|
||||
* <code>session.invalidate()</code> or via Servlet Container management is considered "destroyed". An "expired"
|
||||
* session, on the other hand, is a session that Spring Security wants to end because it was selected for removal for
|
||||
* some reason (generally as it was the least recently used session and the maximum sessions for the user were
|
||||
* reached). An "expired" session is removed as soon as possible by a <code>Filter</code>.</p>
|
||||
* reached). An "expired" session is removed as soon as possible by a <code>Filter</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.security.ui.session.HttpSessionDestroyedEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link org.springframework.security.concurrent.SessionRegistry}
|
||||
* which also listens for {@link org.springframework.security.ui.session.HttpSessionDestroyedEvent}s
|
||||
* published in the Spring application context.
|
||||
*
|
||||
* <p>
|
||||
* NB: It is important that you register the {@link org.springframework.security.ui.session.HttpSessionEventPublisher} in
|
||||
* <code>web.xml</code> so that this class is notified of sessions that expire.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SessionRegistryImpl implements SessionRegistry, ApplicationListener {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(SessionRegistryImpl.class);
|
||||
|
||||
// ~ Instance fields ===============================================================================================
|
||||
|
||||
/** <principal:Object,SessionIdSet> */
|
||||
private Map<Object,Set<String>> principals = Collections.synchronizedMap(new HashMap<Object,Set<String>>());
|
||||
/** <sessionId:Object,SessionInformation> */
|
||||
private Map<String, SessionInformation> sessionIds = Collections.synchronizedMap(new HashMap<String, SessionInformation>());
|
||||
|
||||
// ~ Methods =======================================================================================================
|
||||
|
||||
public Object[] getAllPrincipals() {
|
||||
return principals.keySet().toArray();
|
||||
}
|
||||
|
||||
public SessionInformation[] getAllSessions(Object principal, boolean includeExpiredSessions) {
|
||||
Set<String> sessionsUsedByPrincipal = principals.get(principal);
|
||||
|
||||
if (sessionsUsedByPrincipal == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<SessionInformation> list = new ArrayList<SessionInformation>();
|
||||
|
||||
synchronized (sessionsUsedByPrincipal) {
|
||||
for (String sessionId : sessionsUsedByPrincipal) {
|
||||
SessionInformation sessionInformation = getSessionInformation(sessionId);
|
||||
|
||||
if (sessionInformation == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (includeExpiredSessions || !sessionInformation.isExpired()) {
|
||||
list.add(sessionInformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (SessionInformation[]) list.toArray(new SessionInformation[0]);
|
||||
}
|
||||
|
||||
public SessionInformation getSessionInformation(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
|
||||
return (SessionInformation) sessionIds.get(sessionId);
|
||||
}
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof HttpSessionDestroyedEvent) {
|
||||
String sessionId = ((HttpSession) event.getSource()).getId();
|
||||
removeSessionInformation(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshLastRequest(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
|
||||
SessionInformation info = getSessionInformation(sessionId);
|
||||
|
||||
if (info != null) {
|
||||
info.refreshLastRequest();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void registerNewSession(String sessionId, Object principal) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
Assert.notNull(principal, "Principal required as per interface contract");
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Registering session " + sessionId +", for principal " + principal);
|
||||
}
|
||||
|
||||
if (getSessionInformation(sessionId) != null) {
|
||||
removeSessionInformation(sessionId);
|
||||
}
|
||||
|
||||
sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));
|
||||
|
||||
Set<String> sessionsUsedByPrincipal = principals.get(principal);
|
||||
|
||||
if (sessionsUsedByPrincipal == null) {
|
||||
sessionsUsedByPrincipal = Collections.synchronizedSet(new HashSet<String>(4));
|
||||
principals.put(principal, sessionsUsedByPrincipal);
|
||||
}
|
||||
|
||||
sessionsUsedByPrincipal.add(sessionId);
|
||||
}
|
||||
|
||||
public void removeSessionInformation(String sessionId) {
|
||||
Assert.hasText(sessionId, "SessionId required as per interface contract");
|
||||
|
||||
SessionInformation info = getSessionInformation(sessionId);
|
||||
|
||||
if (info == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing session " + sessionId + " from set of registered sessions");
|
||||
}
|
||||
|
||||
sessionIds.remove(sessionId);
|
||||
|
||||
Set<String> sessionsUsedByPrincipal = principals.get(info.getPrincipal());
|
||||
|
||||
if (sessionsUsedByPrincipal == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing session " + sessionId + " from principal's set of registered sessions");
|
||||
}
|
||||
|
||||
synchronized (sessionsUsedByPrincipal) {
|
||||
sessionsUsedByPrincipal.remove(sessionId);
|
||||
|
||||
if (sessionsUsedByPrincipal.size() == 0) {
|
||||
// No need to keep object in principals Map anymore
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing principal " + info.getPrincipal() + " from registry");
|
||||
}
|
||||
principals.remove(info.getPrincipal());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package org.springframework.security.context;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Used to pass the incoming request to {@link SecurityContextRepository#loadContext(HttpRequestResponseHolder)},
|
||||
* allowing the method to swap the request for a wrapped version, as well as returning the <tt>SecurityContext</tt>
|
||||
* value.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class HttpRequestResponseHolder {
|
||||
HttpServletRequest request;
|
||||
HttpServletResponse response;
|
||||
|
||||
public HttpRequestResponseHolder(HttpServletRequest request, HttpServletResponse response) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
HttpServletRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
void setRequest(HttpServletRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
HttpServletResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
void setResponse(HttpServletResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
-199
@@ -1,199 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.context;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
|
||||
/**
|
||||
* Populates the {@link SecurityContextHolder} with information obtained from
|
||||
* the <code>HttpSession</code>.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* The <code>HttpSession</code> will be queried to retrieve the
|
||||
* <code>SecurityContext</code> that should be stored against the
|
||||
* <code>SecurityContextHolder</code> for the duration of the web request. At
|
||||
* the end of the web request, any updates made to the
|
||||
* <code>SecurityContextHolder</code> will be persisted back to the
|
||||
* <code>HttpSession</code> by this filter.
|
||||
* </p>
|
||||
* <p/>
|
||||
* If a valid <code>SecurityContext</code> cannot be obtained from the
|
||||
* <code>HttpSession</code> for whatever reason, a fresh
|
||||
* <code>SecurityContext</code> will be created and used instead. The created
|
||||
* object will be of the instance defined by the {@link #setContextClass(Class)}
|
||||
* method (which defaults to {@link org.springframework.security.context.SecurityContextImpl}.
|
||||
* </p>
|
||||
* <p/>
|
||||
* No <code>HttpSession</code> will be created by this filter if one does not
|
||||
* already exist. If at the end of the web request the <code>HttpSession</code>
|
||||
* does not exist, a <code>HttpSession</code> will <b>only</b> be created if
|
||||
* the current contents of the <code>SecurityContextHolder</code> are not
|
||||
* {@link java.lang.Object#equals(java.lang.Object)} to a <code>new</code>
|
||||
* instance of {@link #setContextClass(Class)}. This avoids needless
|
||||
* <code>HttpSession</code> creation, but automates the storage of changes
|
||||
* made to the <code>SecurityContextHolder</code>. There is one exception to
|
||||
* this rule, that is if the {@link #forceEagerSessionCreation} property is
|
||||
* <code>true</code>, in which case sessions will always be created
|
||||
* irrespective of normal session-minimisation logic (the default is
|
||||
* <code>false</code>, as this is resource intensive and not recommended).
|
||||
* </p>
|
||||
* <p/>
|
||||
* This filter will only execute once per request, to resolve servlet container
|
||||
* (specifically Weblogic) incompatibilities.
|
||||
* </p>
|
||||
* <p/>
|
||||
* If for whatever reason no <code>HttpSession</code> should <b>ever</b> be
|
||||
* created (eg this filter is only being used with Basic authentication or
|
||||
* similar clients that will never present the same <code>jsessionid</code>
|
||||
* etc), the {@link #setAllowSessionCreation(boolean)} should be set to
|
||||
* <code>false</code>. Only do this if you really need to conserve server
|
||||
* memory and ensure all classes using the <code>SecurityContextHolder</code>
|
||||
* are designed to have no persistence of the <code>SecurityContext</code>
|
||||
* between web requests. Please note that if {@link #forceEagerSessionCreation}
|
||||
* is <code>true</code>, the <code>allowSessionCreation</code> must also be
|
||||
* <code>true</code> (setting it to <code>false</code> will cause a startup
|
||||
* time error).
|
||||
* </p>
|
||||
* <p/>
|
||||
* This filter MUST be executed BEFORE any authentication processing mechanisms.
|
||||
* Authentication processing mechanisms (eg BASIC, CAS processing filters etc)
|
||||
* expect the <code>SecurityContextHolder</code> to contain a valid
|
||||
* <code>SecurityContext</code> by the time they execute.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Patrick Burleson
|
||||
* @author Luke Taylor
|
||||
* @author Martin Algesten
|
||||
*
|
||||
* @deprecated Use SecurityContextPersistenceFilter instead.
|
||||
*
|
||||
* @version $Id$
|
||||
*/
|
||||
public class HttpSessionContextIntegrationFilter extends SecurityContextPersistenceFilter implements InitializingBean {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
public static final String SPRING_SECURITY_CONTEXT_KEY = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Class<? extends SecurityContext> contextClass = SecurityContextImpl.class;
|
||||
|
||||
/**
|
||||
* Indicates if this filter can create a <code>HttpSession</code> if
|
||||
* needed (sessions are always created sparingly, but setting this value to
|
||||
* <code>false</code> will prohibit sessions from ever being created).
|
||||
* Defaults to <code>true</code>. Do not set to <code>false</code> if
|
||||
* you are have set {@link #forceEagerSessionCreation} to <code>true</code>,
|
||||
* as the properties would be in conflict.
|
||||
*/
|
||||
private boolean allowSessionCreation = true;
|
||||
|
||||
/**
|
||||
* Indicates if this filter is required to create a <code>HttpSession</code>
|
||||
* for every request before proceeding through the filter chain, even if the
|
||||
* <code>HttpSession</code> would not ordinarily have been created. By
|
||||
* default this is <code>false</code>, which is entirely appropriate for
|
||||
* most circumstances as you do not want a <code>HttpSession</code>
|
||||
* created unless the filter actually needs one. It is envisaged the main
|
||||
* situation in which this property would be set to <code>true</code> is
|
||||
* if using other filters that depend on a <code>HttpSession</code>
|
||||
* already existing, such as those which need to obtain a session ID. This
|
||||
* is only required in specialised cases, so leave it set to
|
||||
* <code>false</code> unless you have an actual requirement and are
|
||||
* conscious of the session creation overhead.
|
||||
*/
|
||||
private boolean forceEagerSessionCreation = false;
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>SecurityContext</code> will be cloned from
|
||||
* the <code>HttpSession</code>. The default is to simply reference (ie
|
||||
* the default is <code>false</code>). The default may cause issues if
|
||||
* concurrent threads need to have a different security identity from other
|
||||
* threads being concurrently processed that share the same
|
||||
* <code>HttpSession</code>. In most normal environments this does not
|
||||
* represent an issue, as changes to the security identity in one thread is
|
||||
* allowed to affect the security identitiy in other threads associated with
|
||||
* the same <code>HttpSession</code>. For unusual cases where this is not
|
||||
* permitted, change this value to <code>true</code> and ensure the
|
||||
* {@link #contextClass} is set to a <code>SecurityContext</code> that
|
||||
* implements {@link Cloneable} and overrides the <code>clone()</code>
|
||||
* method.
|
||||
*/
|
||||
private boolean cloneFromHttpSession = false;
|
||||
|
||||
// private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
private HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
|
||||
public HttpSessionContextIntegrationFilter() throws ServletException {
|
||||
super.setSecurityContextRepository(repo);
|
||||
}
|
||||
|
||||
public boolean isCloneFromHttpSession() {
|
||||
return cloneFromHttpSession;
|
||||
}
|
||||
|
||||
public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
|
||||
this.cloneFromHttpSession = cloneFromHttpSession;
|
||||
repo.setCloneFromHttpSession(cloneFromHttpSession);
|
||||
}
|
||||
|
||||
public boolean isAllowSessionCreation() {
|
||||
return allowSessionCreation;
|
||||
}
|
||||
|
||||
public void setAllowSessionCreation(boolean allowSessionCreation) {
|
||||
this.allowSessionCreation = allowSessionCreation;
|
||||
repo.setAllowSessionCreation(allowSessionCreation);
|
||||
}
|
||||
|
||||
protected Class<? extends SecurityContext> getContextClass() {
|
||||
return contextClass;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setContextClass(Class secureContext) {
|
||||
this.contextClass = secureContext;
|
||||
repo.setSecurityContextClass(secureContext);
|
||||
}
|
||||
|
||||
public boolean isForceEagerSessionCreation() {
|
||||
return forceEagerSessionCreation;
|
||||
}
|
||||
|
||||
public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
|
||||
this.forceEagerSessionCreation = forceEagerSessionCreation;
|
||||
super.setForceEagerSessionCreation(forceEagerSessionCreation);
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.HTTP_SESSION_CONTEXT_FILTER;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (forceEagerSessionCreation && !allowSessionCreation) {
|
||||
throw new IllegalArgumentException(
|
||||
"If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-369
@@ -1,369 +0,0 @@
|
||||
package org.springframework.security.context;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.AuthenticationTrustResolver;
|
||||
import org.springframework.security.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A <tt>SecurityContextRepository</tt> implementation which stores the security context in the HttpSession between
|
||||
* requests.
|
||||
* <p>
|
||||
* The <code>HttpSession</code> will be queried to retrieve the <code>SecurityContext</code> in the <tt>loadContext</tt>
|
||||
* method (using the key {@link #SPRING_SECURITY_CONTEXT_KEY}). If a valid <code>SecurityContext</code> cannot be
|
||||
* obtained from the <code>HttpSession</code> for whatever reason, a fresh <code>SecurityContext</code> will be created
|
||||
* and returned instead. The created object will be an instance of the class set using the
|
||||
* {@link #setSecurityContextClass(Class)} method. If this hasn't been set, a {@link SecurityContextImpl} will be returned.
|
||||
* <p>
|
||||
* When <tt>saveContext</tt> is called, the context will be stored under the same key, provided
|
||||
* <ol>
|
||||
* <li>The value has changed</li>
|
||||
* <li>The configured <tt>AuthenticationTrustResolver</tt> does not report that the contents represent an anonymous
|
||||
* user</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* With the standard configuration, no <code>HttpSession</code> will be created during <tt>loadContext</tt> if one does
|
||||
* not already exist. When <tt>saveContext</tt> is called at the end of the web request, and no session exists, a new
|
||||
* <code>HttpSession</code> will <b>only</b> be created if the supplied <tt>SecurityContext</tt> is not equal
|
||||
* to a <code>new</code> instance of the {@link #setContextClass(Class) contextClass} (or an empty
|
||||
* <tt>SecurityContextImpl</tt> if the class has not been set. This avoids needless <code>HttpSession</code> creation,
|
||||
* but automates the storage of changes made to the context during the request. Note that if
|
||||
* {@link SecurityContextPersistenceFilter} is configured to eagerly create sessions, then the session-minimisation
|
||||
* logic applied here will not make any difference. If you are using eager session creation, then you should
|
||||
* ensure that the <tt>allowSessionCreation</tt> property of this class is set to <tt>true</tt> (the default).
|
||||
* <p>
|
||||
* If for whatever reason no <code>HttpSession</code> should <b>ever</b> be created (e.g. Basic authentication is being
|
||||
* used or similar clients that will never present the same <code>jsessionid</code> etc), then
|
||||
* {@link #setAllowSessionCreation(boolean) allowSessionCreation} should be set to <code>false</code>.
|
||||
* Only do this if you really need to conserve server memory and ensure all classes using the
|
||||
* <code>SecurityContextHolder</code> are designed to have no persistence of the <code>SecurityContext</code>
|
||||
* between web requests.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class HttpSessionSecurityContextRepository implements SecurityContextRepository {
|
||||
public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private Class<? extends SecurityContext> securityContextClass = null;
|
||||
/** SecurityContext instance used to check for equality with default (unauthenticated) content */
|
||||
private Object contextObject = new SecurityContextImpl();
|
||||
private boolean cloneFromHttpSession = false;
|
||||
private boolean allowSessionCreation = true;
|
||||
private boolean disableUrlRewriting = false;
|
||||
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
/**
|
||||
* Gets the security context from the session (if available) and returns it.
|
||||
* <p>
|
||||
* If the session is null, the context object is null or the context object stored in the session
|
||||
* is not an instance of <tt>SecurityContext</tt>, a new context object will be generated and
|
||||
* returned.
|
||||
* <p>
|
||||
* If <tt>cloneFromHttpSession</tt> is set to true, it will attempt to clone the context object first
|
||||
* and return the cloned instance.
|
||||
*/
|
||||
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
|
||||
HttpServletRequest request = requestResponseHolder.getRequest();
|
||||
HttpServletResponse response = requestResponseHolder.getResponse();
|
||||
HttpSession httpSession = request.getSession(false);
|
||||
|
||||
SecurityContext context = readSecurityContextFromSession(httpSession);
|
||||
|
||||
if (context == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No SecurityContext was available from the HttpSession: " + httpSession +". " +
|
||||
"A new one will be created.");
|
||||
}
|
||||
context = generateNewContext();
|
||||
|
||||
}
|
||||
|
||||
requestResponseHolder.setResponse(new SaveToSessionResponseWrapper(response, request,
|
||||
httpSession != null, context.hashCode()));
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
|
||||
SaveToSessionResponseWrapper responseWrapper = (SaveToSessionResponseWrapper)response;
|
||||
// saveContext() might already be called by the response wrapper
|
||||
// if something in the chain called sendError() or sendRedirect(). This ensures we only call it
|
||||
// once per request.
|
||||
if (!responseWrapper.isContextSaved() ) {
|
||||
responseWrapper.saveContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param httpSession the session obtained from the request.
|
||||
*/
|
||||
private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
|
||||
if (httpSession == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No HttpSession currently exists");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Session exists, so try to obtain a context from it.
|
||||
|
||||
Object contextFromSession = httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY);
|
||||
|
||||
if (contextFromSession == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession returned null object for SPRING_SECURITY_CONTEXT");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// We now have the security context object from the session.
|
||||
if (!(contextFromSession instanceof SecurityContext)) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
|
||||
+ contextFromSession + "'; are you improperly modifying the HttpSession directly "
|
||||
+ "(you should always use SecurityContextHolder) or using the HttpSession attribute "
|
||||
+ "reserved for this class?");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Clone if required (see SEC-356)
|
||||
if (cloneFromHttpSession) {
|
||||
contextFromSession = cloneContext(contextFromSession);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: '" + contextFromSession + "'");
|
||||
}
|
||||
|
||||
// Everything OK. The only non-null return from this method.
|
||||
|
||||
return (SecurityContext) contextFromSession;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context the object which was stored under the security context key in the HttpSession.
|
||||
* @return the cloned SecurityContext object. Never null.
|
||||
*/
|
||||
private Object cloneContext(Object context) {
|
||||
Object clonedContext = null;
|
||||
Assert.isInstanceOf(Cloneable.class, context,
|
||||
"Context must implement Cloneable and provide a Object.clone() method");
|
||||
try {
|
||||
Method m = context.getClass().getMethod("clone", new Class[]{});
|
||||
if (!m.isAccessible()) {
|
||||
m.setAccessible(true);
|
||||
}
|
||||
clonedContext = m.invoke(context, new Object[]{});
|
||||
} catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
|
||||
return clonedContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, calls {@link SecurityContextHolder#createEmptyContext()} to obtain a new context (there should be
|
||||
* no context present in the holder when this method is called). Using this approach the context creation
|
||||
* strategy is decided by the {@link SecurityContextHolderStrategy} in use. The default implementations
|
||||
* will return a new <tt>SecurityContextImpl</tt>.
|
||||
* <p>
|
||||
* An alternative way of customizing the <tt>SecurityContext</tt> implementation is by setting the
|
||||
* <tt>securityContextClass</tt> property. In this case, the method will attempt to invoke the no-args
|
||||
* constructor on the supplied class instead and return the created instance.
|
||||
*
|
||||
* @return a new SecurityContext instance. Never null.
|
||||
*/
|
||||
SecurityContext generateNewContext() {
|
||||
SecurityContext context = null;
|
||||
|
||||
if (securityContextClass == null) {
|
||||
context = SecurityContextHolder.createEmptyContext();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
try {
|
||||
context = securityContextClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
ReflectionUtils.handleReflectionException(e);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setSecurityContextClass(Class contextClass) {
|
||||
if (contextClass == null || (!SecurityContext.class.isAssignableFrom(contextClass))) {
|
||||
throw new IllegalArgumentException("securityContextClass must implement SecurityContext "
|
||||
+ "(typically use org.springframework.security.context.SecurityContextImpl; existing class is "
|
||||
+ contextClass + ")");
|
||||
}
|
||||
|
||||
this.securityContextClass = contextClass;
|
||||
contextObject = generateNewContext();
|
||||
}
|
||||
|
||||
public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
|
||||
this.cloneFromHttpSession = cloneFromHttpSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true (the default), a new session will be created if to store the security context if it is determined
|
||||
* that it's contents are different from the default.
|
||||
*
|
||||
* @param allowSessionCreation
|
||||
*/
|
||||
public void setAllowSessionCreation(boolean allowSessionCreation) {
|
||||
this.allowSessionCreation = allowSessionCreation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the use of session identifiers in URLs to be disabled. Off by default.
|
||||
*
|
||||
* @param disableUrlRewriting set to <tt>true</tt> to disable URL encoding methods in the response wrapper
|
||||
* and prevent the use of <tt>jsessionid</tt> parameters.
|
||||
*/
|
||||
public void setDisableUrlRewriting(boolean disableUrlRewriting) {
|
||||
this.disableUrlRewriting = disableUrlRewriting;
|
||||
}
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
/**
|
||||
* Wrapper that is applied to every request/response to update the <code>HttpSession<code> with
|
||||
* the <code>SecurityContext</code> when a <code>sendError()</code> or <code>sendRedirect</code>
|
||||
* happens. See SEC-398.
|
||||
* <p>
|
||||
* Stores the necessary state from the start of the request in order to make a decision about whether
|
||||
* the security context has changed before saving it.
|
||||
*/
|
||||
class SaveToSessionResponseWrapper extends SaveContextOnUpdateOrErrorResponseWrapper {
|
||||
|
||||
private HttpServletRequest request;
|
||||
private boolean httpSessionExistedAtStartOfRequest;
|
||||
private int contextHashBeforeChainExecution;
|
||||
|
||||
/**
|
||||
* Takes the parameters required to call <code>saveContext()</code> successfully in
|
||||
* addition to the request and the response object we are wrapping.
|
||||
*
|
||||
* @param request the request object (used to obtain the session, if one exists).
|
||||
* @param httpSessionExistedAtStartOfRequest indicates whether there was a session in place before the
|
||||
* filter chain executed. If this is true, and the session is found to be null, this indicates that it was
|
||||
* invalidated during the request and a new session will now be created.
|
||||
* @param contextHashBeforeChainExecution the hashcode of the context before the filter chain executed.
|
||||
* The context will only be stored if it has a different hashcode, indicating that the context changed
|
||||
* during the request.
|
||||
*/
|
||||
SaveToSessionResponseWrapper(HttpServletResponse response, HttpServletRequest request,
|
||||
boolean httpSessionExistedAtStartOfRequest,
|
||||
int contextHashBeforeChainExecution) {
|
||||
super(response, disableUrlRewriting);
|
||||
this.request = request;
|
||||
this.httpSessionExistedAtStartOfRequest = httpSessionExistedAtStartOfRequest;
|
||||
this.contextHashBeforeChainExecution = contextHashBeforeChainExecution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the supplied security context in the session (if available) and if it has changed since it was
|
||||
* set at the start of the request. If the AuthenticationTrustResolver identifies the current user as
|
||||
* anonymous, then the context will not be stored.
|
||||
*
|
||||
* @param context the context object obtained from the SecurityContextHolder after the request has
|
||||
* been processed by the filter chain. SecurityContextHolder.getContext() cannot be used to obtain
|
||||
* the context as it has already been cleared by the time this method is called.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
void saveContext(SecurityContext context) {
|
||||
HttpSession httpSession = request.getSession(false);
|
||||
|
||||
if (httpSession == null) {
|
||||
httpSession = createNewSessionIfAllowed(context);
|
||||
}
|
||||
|
||||
// If HttpSession exists, store current SecurityContextHolder contents but only if
|
||||
// the SecurityContext has actually changed (see JIRA SEC-37)
|
||||
if (httpSession != null && context.hashCode() != contextHashBeforeChainExecution) {
|
||||
// See SEC-776
|
||||
// TODO: Move this so that a session isn't created if user is anonymous
|
||||
if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContext contents are anonymous - context will not be stored in HttpSession. ");
|
||||
}
|
||||
} else {
|
||||
httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContext stored to HttpSession: '" + context + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HttpSession createNewSessionIfAllowed(SecurityContext context) {
|
||||
if (httpSessionExistedAtStartOfRequest) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession is now null, but was not null at start of request; "
|
||||
+ "session was invalidated, so do not create a new session");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!allowSessionCreation) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("The HttpSession is currently null, and the "
|
||||
+ "HttpSessionContextIntegrationFilter is prohibited from creating an HttpSession "
|
||||
+ "(because the allowSessionCreation property is false) - SecurityContext thus not "
|
||||
+ "stored for next request");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
// Generate a HttpSession only if we need to
|
||||
|
||||
if (contextObject.equals(context)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession is null, but SecurityContext has not changed from default: ' "
|
||||
+ context
|
||||
+ "'; not creating HttpSession or storing SecurityContext");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("HttpSession being created as SecurityContext is non-default");
|
||||
}
|
||||
|
||||
try {
|
||||
return request.getSession(true);
|
||||
} catch (IllegalStateException e) {
|
||||
// Response must already be committed, therefore can't create a new session
|
||||
logger.warn("Failed to create a session, as response has been committed. Unable to store" +
|
||||
" SecurityContext.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -26,7 +26,7 @@ import org.springframework.util.Assert;
|
||||
* @version $Id$
|
||||
*
|
||||
* @see java.lang.ThreadLocal
|
||||
* @see org.springframework.security.context.SecurityContextPersistenceFilter
|
||||
* @see org.springframework.security.context.web.SecurityContextPersistenceFilter
|
||||
*/
|
||||
public class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
package org.springframework.security.context;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
/**
|
||||
* Base class for response wrappers which encapsulate the logic for storing a security context and which
|
||||
* store the with the <code>SecurityContext</code> when a <code>sendError()</code> or <code>sendRedirect</code>
|
||||
* happens. See issue SEC-398.
|
||||
* <p>
|
||||
* Sub-classes should implement the {@link #saveContext(SecurityContext context)} method.
|
||||
* <p>
|
||||
* Support is also provided for disabling URL rewriting
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Marten Algesten
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public abstract class SaveContextOnUpdateOrErrorResponseWrapper extends HttpServletResponseWrapper {
|
||||
|
||||
private boolean contextSaved = false;
|
||||
/* See SEC-1052 */
|
||||
private boolean disableUrlRewriting;
|
||||
|
||||
/**
|
||||
* @param response the response to be wrapped
|
||||
* @param disableUrlRewriting turns the URL encoding methods into null operations, preventing the use
|
||||
* of URL rewriting to add the session identifier as a URL parameter.
|
||||
*/
|
||||
public SaveContextOnUpdateOrErrorResponseWrapper(HttpServletResponse response, boolean disableUrlRewriting) {
|
||||
super(response);
|
||||
this.disableUrlRewriting = disableUrlRewriting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the logic for storing the security context.
|
||||
*
|
||||
* @param context the <tt>SecurityContext</tt> instance to store
|
||||
*/
|
||||
abstract void saveContext(SecurityContext context);
|
||||
|
||||
/**
|
||||
* Makes sure the session is updated before calling the
|
||||
* superclass <code>sendError()</code>
|
||||
*/
|
||||
@Override
|
||||
public final void sendError(int sc) throws IOException {
|
||||
doSaveContext();
|
||||
super.sendError(sc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the session is updated before calling the
|
||||
* superclass <code>sendError()</code>
|
||||
*/
|
||||
@Override
|
||||
public final void sendError(int sc, String msg) throws IOException {
|
||||
doSaveContext();
|
||||
super.sendError(sc, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the context is stored before calling the
|
||||
* superclass <code>sendRedirect()</code>
|
||||
*/
|
||||
@Override
|
||||
public final void sendRedirect(String location) throws IOException {
|
||||
doSaveContext();
|
||||
super.sendRedirect(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls <code>saveContext()</code> with the current contents of the <tt>SecurityContextHolder</tt>.
|
||||
*/
|
||||
private void doSaveContext() {
|
||||
saveContext(SecurityContextHolder.getContext());
|
||||
contextSaved = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String encodeRedirectUrl(String url) {
|
||||
if (disableUrlRewriting) {
|
||||
return url;
|
||||
}
|
||||
return super.encodeRedirectUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String encodeRedirectURL(String url) {
|
||||
if (disableUrlRewriting) {
|
||||
return url;
|
||||
}
|
||||
return super.encodeRedirectURL(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String encodeUrl(String url) {
|
||||
if (disableUrlRewriting) {
|
||||
return url;
|
||||
}
|
||||
return super.encodeUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String encodeURL(String url) {
|
||||
if (disableUrlRewriting) {
|
||||
return url;
|
||||
}
|
||||
return super.encodeURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the response wrapper has called <code>saveContext()</code> because of an error or redirect.
|
||||
*/
|
||||
public final boolean isContextSaved() {
|
||||
return contextSaved;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,7 +43,7 @@ import java.lang.reflect.Constructor;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*
|
||||
* @see org.springframework.security.context.HttpSessionContextIntegrationFilter
|
||||
* @see org.springframework.security.context.web.HttpSessionContextIntegrationFilter
|
||||
*/
|
||||
public class SecurityContextHolder {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
@@ -138,7 +138,7 @@ public class SecurityContextHolder {
|
||||
/**
|
||||
* Delegates the creation of a new, empty context to the configured strategy.
|
||||
*/
|
||||
static SecurityContext createEmptyContext() {
|
||||
public static SecurityContext createEmptyContext() {
|
||||
return strategy.createEmptyContext();
|
||||
}
|
||||
|
||||
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
package org.springframework.security.context;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
|
||||
/**
|
||||
* Populates the {@link SecurityContextHolder} with information obtained from
|
||||
* the configured {@link SecurityContextRepository} prior to the request and stores it back in the repository
|
||||
* once the request has completed. By default it uses an {@link HttpSessionSecurityContextRepository}. See this
|
||||
* class for information <tt>HttpSession</tt> related configuration options.
|
||||
* <p>
|
||||
* This filter will only execute once per request, to resolve servlet container (specifically Weblogic)
|
||||
* incompatibilities.
|
||||
* <p>
|
||||
* This filter MUST be executed BEFORE any authentication processing mechanisms. Authentication processing mechanisms
|
||||
* (e.g. BASIC, CAS processing filters etc) expect the <code>SecurityContextHolder</code> to contain a valid
|
||||
* <code>SecurityContext</code> by the time they execute.
|
||||
* <p>
|
||||
* This is essentially a refactoring of the old <tt>HttpSessionContextIntegrationFilter</tt> to delegate
|
||||
* the storage issues to a separate strategy, allowing for more customization in the way the security context is
|
||||
* maintained between requests.
|
||||
* <p>
|
||||
* The <tt>forceEagerSessionCreation</tt> property can be used to ensure that a session is always available before
|
||||
* the filter chain executes (the default is <code>false</code>, as this is resource intensive and not recommended).
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class SecurityContextPersistenceFilter extends SpringSecurityFilter {
|
||||
|
||||
static final String FILTER_APPLIED = "__spring_security_scpf_applied";
|
||||
|
||||
private SecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||
|
||||
private boolean forceEagerSessionCreation = false;
|
||||
|
||||
@Override
|
||||
protected void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
if (request.getAttribute(FILTER_APPLIED) != null) {
|
||||
// ensure that filter is only applied once per request
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
||||
|
||||
if (forceEagerSessionCreation) {
|
||||
HttpSession session = request.getSession();
|
||||
logger.debug("Eagerly created session: " + session.getId());
|
||||
}
|
||||
|
||||
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
|
||||
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
|
||||
|
||||
try {
|
||||
SecurityContextHolder.setContext(contextBeforeChainExecution);
|
||||
|
||||
chain.doFilter(holder.getRequest(), holder.getResponse());
|
||||
|
||||
} finally {
|
||||
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
|
||||
// Crucial removal of SecurityContextHolder contents - do this before anything else.
|
||||
SecurityContextHolder.clearContext();
|
||||
repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
|
||||
request.removeAttribute(FILTER_APPLIED);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContextHolder now cleared, as request processing completed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityContextRepository(SecurityContextRepository repo) {
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
|
||||
this.forceEagerSessionCreation = forceEagerSessionCreation;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.SECURITY_CONTEXT_FILTER;
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package org.springframework.security.context;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Strategy used for persisting a {@link SecurityContext} between requests.
|
||||
* <p>
|
||||
* Used by {@link SecurityContextPersistenceFilter} to obtain the context which should be used for the current thread
|
||||
* of execution and to store the context once it has been removed from thread-local storage and the request has
|
||||
* completed.
|
||||
* <p>
|
||||
* The persistence mechanism used will depend on the implementation, but most commonly the <tt>HttpSession</tt> will
|
||||
* be used to store the context.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*
|
||||
* @see SecurityContextPersistenceFilter
|
||||
* @see HttpSessionSecurityContextRepository
|
||||
* @see SaveContextOnUpdateOrErrorResponseWrapper
|
||||
*/
|
||||
public interface SecurityContextRepository {
|
||||
|
||||
/**
|
||||
* Obtains the security context for the supplied request. For an unauthenticated user, an empty context
|
||||
* implementation should be returned. This method should not return null.
|
||||
* <p>
|
||||
* The use of the <tt>HttpRequestResponseHolder</tt> parameter allows implementations to return wrapped versions of
|
||||
* the request or response (or both), allowing them to access implementation-specific state for the request.
|
||||
* The values obtained from the holder will be passed on to the filter chain and also to the <tt>saveContext</tt>
|
||||
* method when it is finally called. Implementations may wish to return a subclass of
|
||||
* {@link SaveContextOnUpdateOrErrorResponseWrapper} as the response object, which guarantees that the context is
|
||||
* persisted when an error or redirect occurs.
|
||||
*
|
||||
* @param requestResponseHolder holder for the current request and response for which the context should be loaded.
|
||||
*
|
||||
* @return The security context which should be used for the current request, never null.
|
||||
*/
|
||||
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
|
||||
|
||||
/**
|
||||
* Stores the security context on completion of a request.
|
||||
*
|
||||
* @param context the non-null context which was obtained f
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response);
|
||||
}
|
||||
+1
-1
@@ -25,7 +25,7 @@ import org.springframework.util.Assert;
|
||||
* @version $Id$
|
||||
*
|
||||
* @see java.lang.ThreadLocal
|
||||
* @see org.springframework.security.context.SecurityContextPersistenceFilter
|
||||
* @see org.springframework.security.context.web.SecurityContextPersistenceFilter
|
||||
*/
|
||||
public class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
Classes related to the establishment of a security context for the duration of a request (such as
|
||||
an HTTP or RMI invocation) and for the maintenance of the context between requests (by storing it in an HTTP sessio, for
|
||||
example).
|
||||
an HTTP or RMI invocation).
|
||||
<p>
|
||||
A security context is associated with the current execution thread for the duration of the request, making the
|
||||
authentication information it contains available throughout all the layers of an application.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.security.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* Generic session creation event which indicates that a session (potentially
|
||||
* represented by a security context) has begun.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public abstract class SessionCreationEvent extends ApplicationEvent {
|
||||
|
||||
public SessionCreationEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.springframework.security.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.security.context.SecurityContext;
|
||||
|
||||
/**
|
||||
* Generic "session termination" event which indicates that a session (potentially
|
||||
* represented by a security context) has ended.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public abstract class SessionDestroyedEvent extends ApplicationEvent {
|
||||
|
||||
public SessionDestroyedEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the <tt>SecurityContext</tt> under which the session was running.
|
||||
*
|
||||
* @return the <tt>SecurityContext</tt> associated with the session, or null if there is no context.
|
||||
*/
|
||||
public abstract SecurityContext getSecurityContext();
|
||||
}
|
||||
+3
-9
@@ -5,17 +5,16 @@ import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
|
||||
/**
|
||||
* Facade which isolates Spring Security's requirements from the implementation of the underlying
|
||||
* expression objects.
|
||||
* Facade which isolates Spring Security's requirements for evaluation method-security expressions
|
||||
* from the implementation of the underlying expression objects.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public interface SecurityExpressionHandler {
|
||||
public interface MethodSecurityExpressionHandler {
|
||||
/**
|
||||
* @return an expression parser for the expressions used by the implementation.
|
||||
*/
|
||||
@@ -26,11 +25,6 @@ public interface SecurityExpressionHandler {
|
||||
*/
|
||||
EvaluationContext createEvaluationContext(Authentication authentication, MethodInvocation mi);
|
||||
|
||||
/**
|
||||
* Provides an evaluation context in which to evaluate security expressions for a web invocation.
|
||||
*/
|
||||
EvaluationContext createEvaluationContext(Authentication authentication, FilterInvocation fi);
|
||||
|
||||
/**
|
||||
* Filters a target collection or array.
|
||||
* Only applies to method invocations.
|
||||
+3
-3
@@ -12,7 +12,7 @@ import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.expression.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.expression.annotation.PostAuthorize;
|
||||
import org.springframework.security.expression.annotation.PostFilter;
|
||||
import org.springframework.security.expression.annotation.PreAuthorize;
|
||||
@@ -45,10 +45,10 @@ public class ExpressionAnnotationMethodSecurityMetadataSource extends AbstractMe
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which obtains the expression parser from the {@link SecurityExpressionHandler#getExpressionParser() }
|
||||
* Constructor which obtains the expression parser from the {@link MethodSecurityExpressionHandler#getExpressionParser() }
|
||||
* method on the supplied <tt>SecurityExpressionHandler</tt>.
|
||||
*/
|
||||
public ExpressionAnnotationMethodSecurityMetadataSource(SecurityExpressionHandler handler) {
|
||||
public ExpressionAnnotationMethodSecurityMetadataSource(MethodSecurityExpressionHandler handler) {
|
||||
parser = handler.getExpressionParser();
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -12,8 +12,8 @@ import org.springframework.security.Authentication;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.afterinvocation.AfterInvocationProvider;
|
||||
import org.springframework.security.expression.ExpressionUtils;
|
||||
import org.springframework.security.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.expression.support.DefaultSecurityExpressionHandler;
|
||||
import org.springframework.security.expression.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.expression.support.DefaultMethodSecurityExpressionHandler;
|
||||
|
||||
/**
|
||||
* AfterInvocationProvider which handles the @PostAuthorize and @PostFilter annotation expressions.
|
||||
@@ -26,7 +26,7 @@ public class MethodExpressionAfterInvocationProvider implements AfterInvocationP
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private SecurityExpressionHandler expressionHandler = new DefaultSecurityExpressionHandler();
|
||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
public Object decide(Authentication authentication, Object object, List<ConfigAttribute> config, Object returnedObject)
|
||||
throws AccessDeniedException {
|
||||
@@ -90,7 +90,7 @@ public class MethodExpressionAfterInvocationProvider implements AfterInvocationP
|
||||
return clazz.isAssignableFrom(MethodInvocation.class);
|
||||
}
|
||||
|
||||
public void setExpressionHandler(SecurityExpressionHandler expressionHandler) {
|
||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
|
||||
this.expressionHandler = expressionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -11,8 +11,8 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.expression.ExpressionUtils;
|
||||
import org.springframework.security.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.expression.support.DefaultSecurityExpressionHandler;
|
||||
import org.springframework.security.expression.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.expression.support.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.vote.AccessDecisionVoter;
|
||||
|
||||
/**
|
||||
@@ -29,7 +29,7 @@ import org.springframework.security.vote.AccessDecisionVoter;
|
||||
public class MethodExpressionVoter implements AccessDecisionVoter {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private SecurityExpressionHandler expressionHandler = new DefaultSecurityExpressionHandler();
|
||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute instanceof AbstractExpressionBasedMethodConfigAttribute;
|
||||
@@ -105,7 +105,7 @@ public class MethodExpressionVoter implements AccessDecisionVoter {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setExpressionHandler(SecurityExpressionHandler expressionHandler) {
|
||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
|
||||
this.expressionHandler = expressionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-14
@@ -14,14 +14,12 @@ import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationTrustResolver;
|
||||
import org.springframework.security.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.expression.ExpressionUtils;
|
||||
import org.springframework.security.expression.PermissionEvaluator;
|
||||
import org.springframework.security.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
import org.springframework.security.expression.MethodSecurityExpressionHandler;
|
||||
|
||||
/**
|
||||
* The standard implementation of <tt>SecurityExpressionHandler</tt>.
|
||||
@@ -32,7 +30,7 @@ import org.springframework.security.intercept.web.FilterInvocation;
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class DefaultSecurityExpressionHandler implements SecurityExpressionHandler {
|
||||
public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@@ -41,7 +39,7 @@ public class DefaultSecurityExpressionHandler implements SecurityExpressionHandl
|
||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||
private ExpressionParser expressionParser = new SpelAntlrExpressionParser();
|
||||
|
||||
public DefaultSecurityExpressionHandler() {
|
||||
public DefaultMethodSecurityExpressionHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,15 +56,6 @@ public class DefaultSecurityExpressionHandler implements SecurityExpressionHandl
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public EvaluationContext createEvaluationContext(Authentication authentication, FilterInvocation fi) {
|
||||
StandardEvaluationContext ctx = new StandardEvaluationContext();
|
||||
SecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, fi);
|
||||
root.setTrustResolver(trustResolver);
|
||||
ctx.setRootObject(root);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
|
||||
MethodSecurityExpressionRoot rootObject = (MethodSecurityExpressionRoot) ctx.getRootObject();
|
||||
+2
-2
@@ -15,7 +15,7 @@ import org.springframework.security.util.AuthorityUtils;
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
abstract class SecurityExpressionRoot {
|
||||
public abstract class SecurityExpressionRoot {
|
||||
protected final Authentication authentication;
|
||||
private AuthenticationTrustResolver trustResolver;
|
||||
/** Allows "permitAll" expression */
|
||||
@@ -24,7 +24,7 @@ abstract class SecurityExpressionRoot {
|
||||
/** Allows "denyAll" expression */
|
||||
public final boolean denyAll = false;
|
||||
|
||||
SecurityExpressionRoot(Authentication a) {
|
||||
public SecurityExpressionRoot(Authentication a) {
|
||||
if (a == null) {
|
||||
throw new IllegalArgumentException("Authentication object cannot be null");
|
||||
}
|
||||
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
package org.springframework.security.expression.support;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
class WebSecurityExpressionRoot extends SecurityExpressionRoot {
|
||||
private FilterInvocation filterInvocation;
|
||||
|
||||
WebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
|
||||
super(a);
|
||||
this.filterInvocation = fi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a specific IP address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or 202.24.0.0/14).
|
||||
*
|
||||
* @param ipAddress the address or range of addresses from which the request must come.
|
||||
* @return true if the IP address of the current request is in the required range.
|
||||
*/
|
||||
public boolean hasIpAddress(String ipAddress) {
|
||||
int nMaskBits = 0;
|
||||
|
||||
if (ipAddress.indexOf('/') > 0) {
|
||||
String[] addressAndMask = StringUtils.split(ipAddress, "/");
|
||||
ipAddress = addressAndMask[0];
|
||||
nMaskBits = Integer.parseInt(addressAndMask[1]);
|
||||
}
|
||||
|
||||
InetAddress requiredAddress = parseAddress(ipAddress);
|
||||
InetAddress remoteAddress = parseAddress(filterInvocation.getHttpRequest().getRemoteAddr());
|
||||
|
||||
if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
|
||||
throw new IllegalArgumentException("IP Address in expression must be the same type as " +
|
||||
"version returned by request");
|
||||
}
|
||||
|
||||
if (nMaskBits == 0) {
|
||||
return remoteAddress.equals(requiredAddress);
|
||||
}
|
||||
|
||||
byte[] remAddr = remoteAddress.getAddress();
|
||||
byte[] reqAddr = requiredAddress.getAddress();
|
||||
|
||||
int oddBits = nMaskBits % 8;
|
||||
int nMaskBytes = nMaskBits/8 + (oddBits == 0 ? 0 : 1);
|
||||
byte[] mask = new byte[nMaskBytes];
|
||||
|
||||
Arrays.fill(mask, 0, oddBits == 0 ? mask.length : mask.length - 1, (byte)0xFF);
|
||||
|
||||
if (oddBits != 0) {
|
||||
int finalByte = (1 << oddBits) - 1;
|
||||
finalByte <<= 8-oddBits;
|
||||
mask[mask.length - 1] = (byte) finalByte;
|
||||
}
|
||||
|
||||
// System.out.println("Mask is " + new sun.misc.HexDumpEncoder().encode(mask));
|
||||
|
||||
for (int i=0; i < mask.length; i++) {
|
||||
if ((remAddr[i] & mask[i]) != (reqAddr[i] & mask[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private InetAddress parseAddress(String address) {
|
||||
try {
|
||||
return InetAddress.getByName(address);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new IllegalArgumentException("Failed to parse address" + address, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
package org.springframework.security.expression.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.intercept.web.DefaultFilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.intercept.web.RequestKey;
|
||||
import org.springframework.security.util.UrlMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Expression-based <tt>FilterInvocationSecurityMetadataSource</tt>.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public final class ExpressionBasedFilterInvocationSecurityMetadataSource extends DefaultFilterInvocationSecurityMetadataSource {
|
||||
private final static Log logger = LogFactory.getLog(ExpressionBasedFilterInvocationSecurityMetadataSource.class);
|
||||
|
||||
public ExpressionBasedFilterInvocationSecurityMetadataSource(UrlMatcher urlMatcher,
|
||||
LinkedHashMap<RequestKey, List<ConfigAttribute>> requestMap, SecurityExpressionHandler expressionHandler) {
|
||||
super(urlMatcher, processMap(requestMap, expressionHandler.getExpressionParser()));
|
||||
Assert.notNull(expressionHandler, "A non-null SecurityExpressionHandler is required");
|
||||
}
|
||||
|
||||
private static LinkedHashMap<RequestKey, List<ConfigAttribute>> processMap(
|
||||
LinkedHashMap<RequestKey, List<ConfigAttribute>> requestMap, ExpressionParser parser) {
|
||||
Assert.notNull(parser, "SecurityExpressionHandler returned a null parser object");
|
||||
|
||||
LinkedHashMap<RequestKey, List<ConfigAttribute>> requestToExpressionAttributesMap =
|
||||
new LinkedHashMap<RequestKey, List<ConfigAttribute>>(requestMap);
|
||||
|
||||
for (Map.Entry<RequestKey, List<ConfigAttribute>> entry : requestMap.entrySet()) {
|
||||
RequestKey request = entry.getKey();
|
||||
Assert.isTrue(entry.getValue().size() == 1, "Expected a single expression attribute for " + request);
|
||||
ArrayList<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(1);
|
||||
String expression = entry.getValue().get(0).getAttribute();
|
||||
logger.debug("Adding web access control expression '" + expression + "', for " + request);
|
||||
try {
|
||||
attributes.add(new WebExpressionConfigAttribute(parser.parseExpression(expression)));
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Failed to parse expression '" + expression + "'");
|
||||
}
|
||||
|
||||
requestToExpressionAttributesMap.put(request, attributes);
|
||||
}
|
||||
|
||||
return requestToExpressionAttributesMap;
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package org.springframework.security.expression.web;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
|
||||
/**
|
||||
* Simple expression configuration attribute for use in web request authorizations.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
class WebExpressionConfigAttribute implements ConfigAttribute {
|
||||
private final Expression authorizeExpression;
|
||||
|
||||
public WebExpressionConfigAttribute(Expression authorizeExpression) {
|
||||
this.authorizeExpression = authorizeExpression;
|
||||
}
|
||||
|
||||
Expression getAuthorizeExpression() {
|
||||
return authorizeExpression;
|
||||
}
|
||||
|
||||
public String getAttribute() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return authorizeExpression.getExpressionString();
|
||||
}
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
package org.springframework.security.expression.web;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.expression.ExpressionUtils;
|
||||
import org.springframework.security.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.expression.support.DefaultSecurityExpressionHandler;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
import org.springframework.security.vote.AccessDecisionVoter;
|
||||
|
||||
/**
|
||||
* Voter which handles web authorisation decisions.
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class WebExpressionVoter implements AccessDecisionVoter {
|
||||
private SecurityExpressionHandler expressionHandler = new DefaultSecurityExpressionHandler();
|
||||
|
||||
public int vote(Authentication authentication, Object object, List<ConfigAttribute> attributes) {
|
||||
assert authentication != null;
|
||||
assert object != null;
|
||||
assert attributes != null;
|
||||
|
||||
WebExpressionConfigAttribute weca = findConfigAttribute(attributes);
|
||||
|
||||
if (weca == null) {
|
||||
return ACCESS_ABSTAIN;
|
||||
}
|
||||
|
||||
FilterInvocation fi = (FilterInvocation)object;
|
||||
EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication, fi);
|
||||
|
||||
return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ?
|
||||
ACCESS_GRANTED : ACCESS_DENIED;
|
||||
}
|
||||
|
||||
private WebExpressionConfigAttribute findConfigAttribute(List<ConfigAttribute> attributes) {
|
||||
for (ConfigAttribute attribute : attributes) {
|
||||
if (attribute instanceof WebExpressionConfigAttribute) {
|
||||
return (WebExpressionConfigAttribute)attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute instanceof WebExpressionConfigAttribute;
|
||||
}
|
||||
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return clazz.isAssignableFrom(FilterInvocation.class);
|
||||
}
|
||||
|
||||
public void setExpressionHandler(SecurityExpressionHandler expressionHandler) {
|
||||
this.expressionHandler = expressionHandler;
|
||||
}
|
||||
}
|
||||
-224
@@ -1,224 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.intercept.web;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.util.UrlMatcher;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of <tt>FilterInvocationDefinitionSource</tt>.
|
||||
* <p>
|
||||
* Stores an ordered map of compiled URL paths to <tt>ConfigAttribute</tt> lists and provides URL matching
|
||||
* against the items stored in this map using the configured <tt>UrlMatcher</tt>.
|
||||
* <p>
|
||||
* The order of registering the regular expressions using the
|
||||
* {@link #addSecureUrl(String, List<ConfigAttribute>)} is very important.
|
||||
* The system will identify the <b>first</b> matching regular
|
||||
* expression for a given HTTP URL. It will not proceed to evaluate later regular expressions if a match has already
|
||||
* been found. Accordingly, the most specific regular expressions should be registered first, with the most general
|
||||
* regular expressions registered last.
|
||||
* <p>
|
||||
* If URLs are registered for a particular HTTP method using
|
||||
* {@link #addSecureUrl(String, String, List<ConfigAttribute>)}, then the method-specific matches will take
|
||||
* precedence over any URLs which are registered without an HTTP method.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class DefaultFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
|
||||
|
||||
private static final Set<String> HTTP_METHODS = new HashSet<String>(Arrays.asList("DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT", "TRACE"));
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
//private Map<Object, List<ConfigAttribute>> requestMap = new LinkedHashMap<Object, List<ConfigAttribute>>();
|
||||
/** Stores request maps keyed by specific HTTP methods. A null key matches any method */
|
||||
private Map<String, Map<Object, List<ConfigAttribute>>> httpMethodMap =
|
||||
new HashMap<String, Map<Object, List<ConfigAttribute>>>();
|
||||
|
||||
private UrlMatcher urlMatcher;
|
||||
|
||||
private boolean stripQueryStringFromUrls;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Builds the internal request map from the supplied map. The key elements should be of type {@link RequestKey},
|
||||
* which contains a URL path and an optional HTTP method (may be null). The path stored in the key will depend on
|
||||
* the type of the supplied UrlMatcher.
|
||||
*
|
||||
* @param urlMatcher typically an ant or regular expression matcher.
|
||||
* @param requestMap order-preserving map of request definitions to attribute lists
|
||||
*/
|
||||
public DefaultFilterInvocationSecurityMetadataSource(UrlMatcher urlMatcher,
|
||||
LinkedHashMap<RequestKey, List<ConfigAttribute>> requestMap) {
|
||||
this.urlMatcher = urlMatcher;
|
||||
|
||||
for (Map.Entry<RequestKey, List<ConfigAttribute>> entry : requestMap.entrySet()) {
|
||||
addSecureUrl(entry.getKey().getUrl(), entry.getKey().getMethod(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Adds a URL,attribute-list pair to the request map, first allowing the <tt>UrlMatcher</tt> to
|
||||
* process the pattern if required, using its <tt>compile</tt> method. The returned object will be used as the key
|
||||
* to the request map and will be passed back to the <tt>UrlMatcher</tt> when iterating through the map to find
|
||||
* a match for a particular URL.
|
||||
*/
|
||||
private void addSecureUrl(String pattern, String method, List<ConfigAttribute> attr) {
|
||||
Map<Object, List<ConfigAttribute>> mapToUse = getRequestMapForHttpMethod(method);
|
||||
|
||||
mapToUse.put(urlMatcher.compile(pattern), attr);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Added URL pattern: " + pattern + "; attributes: " + attr +
|
||||
(method == null ? "" : " for HTTP method '" + method + "'"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HTTP method specific request map, creating it if it doesn't already exist.
|
||||
* @param method GET, POST etc
|
||||
* @return map of URL patterns to <tt>ConfigAttribute</tt>s for this method.
|
||||
*/
|
||||
private Map<Object, List<ConfigAttribute>> getRequestMapForHttpMethod(String method) {
|
||||
if (method != null && !HTTP_METHODS.contains(method)) {
|
||||
throw new IllegalArgumentException("Unrecognised HTTP method: '" + method + "'");
|
||||
}
|
||||
|
||||
Map<Object, List<ConfigAttribute>> methodRequestMap = httpMethodMap.get(method);
|
||||
|
||||
if (methodRequestMap == null) {
|
||||
methodRequestMap = new LinkedHashMap<Object, List<ConfigAttribute>>();
|
||||
httpMethodMap.put(method, methodRequestMap);
|
||||
}
|
||||
|
||||
return methodRequestMap;
|
||||
}
|
||||
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
|
||||
|
||||
for (Map.Entry<String, Map<Object, List<ConfigAttribute>>> entry : httpMethodMap.entrySet()) {
|
||||
for (List<ConfigAttribute> attrs : entry.getValue().values()) {
|
||||
allAttributes.addAll(attrs);
|
||||
}
|
||||
}
|
||||
|
||||
return allAttributes;
|
||||
}
|
||||
|
||||
|
||||
public List<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
|
||||
if ((object == null) || !this.supports(object.getClass())) {
|
||||
throw new IllegalArgumentException("Object must be a FilterInvocation");
|
||||
}
|
||||
|
||||
String url = ((FilterInvocation) object).getRequestUrl();
|
||||
String method = ((FilterInvocation) object).getHttpRequest().getMethod();
|
||||
|
||||
return lookupAttributes(url, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual lookup of the relevant <tt>ConfigAttribute</tt>s for the given <code>FilterInvocation</code>.
|
||||
* <p>
|
||||
* By default, iterates through the stored URL map and calls the
|
||||
* {@link UrlMatcher#pathMatchesUrl(Object path, String url)} method until a match is found.
|
||||
* <p>
|
||||
* Subclasses can override if required to perform any modifications to the URL.
|
||||
*
|
||||
* @param url the URI to retrieve configuration attributes for
|
||||
* @param method the HTTP method (GET, POST, DELETE...).
|
||||
*
|
||||
* @return the <code>ConfigAttribute</code>s that apply to the specified <code>FilterInvocation</code>
|
||||
* or null if no match is found
|
||||
*/
|
||||
public final List<ConfigAttribute> lookupAttributes(String url, String method) {
|
||||
if (stripQueryStringFromUrls) {
|
||||
// Strip anything after a question mark symbol, as per SEC-161. See also SEC-321
|
||||
int firstQuestionMarkIndex = url.indexOf("?");
|
||||
|
||||
if (firstQuestionMarkIndex != -1) {
|
||||
url = url.substring(0, firstQuestionMarkIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (urlMatcher.requiresLowerCaseUrl()) {
|
||||
url = url.toLowerCase();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Converted URL to lowercase, from: '" + url + "'; to: '" + url + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain the map of request patterns to attributes for this method and lookup the url.
|
||||
Map<Object, List<ConfigAttribute>> requestMap = httpMethodMap.get(method);
|
||||
|
||||
// If no method-specific map, use the general one stored under the null key
|
||||
if (requestMap == null) {
|
||||
requestMap = httpMethodMap.get(null);
|
||||
}
|
||||
|
||||
if (requestMap != null) {
|
||||
for (Map.Entry<Object, List<ConfigAttribute>> entry : requestMap.entrySet()) {
|
||||
Object p = entry.getKey();
|
||||
boolean matched = urlMatcher.pathMatchesUrl(entry.getKey(), url);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Candidate is: '" + url + "'; pattern is " + p + "; matched=" + matched);
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return FilterInvocation.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
protected UrlMatcher getUrlMatcher() {
|
||||
return urlMatcher;
|
||||
}
|
||||
|
||||
public boolean isConvertUrlToLowercaseBeforeComparison() {
|
||||
return urlMatcher.requiresLowerCaseUrl();
|
||||
}
|
||||
|
||||
public void setStripQueryStringFromUrls(boolean stripQueryStringFromUrls) {
|
||||
this.stripQueryStringFromUrls = stripQueryStringFromUrls;
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.intercept.web;
|
||||
|
||||
import org.springframework.security.util.UrlUtils;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Holds objects associated with a HTTP filter.<P>Guarantees the request and response are instances of
|
||||
* <code>HttpServletRequest</code> and <code>HttpServletResponse</code>, and that there are no <code>null</code>
|
||||
* objects.
|
||||
* <p>
|
||||
* Required so that security system classes can obtain access to the filter environment, as well as the request
|
||||
* and response.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author colin sampaleanu
|
||||
* @version $Id$
|
||||
*/
|
||||
public class FilterInvocation {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private FilterChain chain;
|
||||
private HttpServletRequest request;
|
||||
private HttpServletResponse response;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public FilterInvocation(ServletRequest request, ServletResponse response, FilterChain chain) {
|
||||
if ((request == null) || (response == null) || (chain == null)) {
|
||||
throw new IllegalArgumentException("Cannot pass null values to constructor");
|
||||
}
|
||||
|
||||
this.request = (HttpServletRequest) request;
|
||||
this.response = (HttpServletResponse) response;
|
||||
this.chain = chain;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public FilterChain getChain() {
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the URL that the user agent used for this request.
|
||||
* <p>
|
||||
* The returned URL does <b>not</b> reflect the port number determined from a
|
||||
* {@link org.springframework.security.util.PortResolver}.
|
||||
*
|
||||
* @return the full URL of this request
|
||||
*/
|
||||
public String getFullRequestUrl() {
|
||||
return UrlUtils.getFullRequestUrl(this);
|
||||
}
|
||||
|
||||
public HttpServletRequest getHttpRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public HttpServletResponse getHttpResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the web application-specific fragment of the URL.
|
||||
*
|
||||
* @return the URL, excluding any server name, context path or servlet path
|
||||
*/
|
||||
public String getRequestUrl() {
|
||||
return UrlUtils.getRequestUrl(this);
|
||||
}
|
||||
|
||||
public HttpServletRequest getRequest() {
|
||||
return getHttpRequest();
|
||||
}
|
||||
|
||||
public HttpServletResponse getResponse() {
|
||||
return getHttpResponse();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "FilterInvocation: URL: " + getRequestUrl();
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.intercept.web;
|
||||
|
||||
import org.springframework.security.intercept.SecurityMetadataSource;
|
||||
|
||||
|
||||
/**
|
||||
* Marker interface for <code>SecurityMetadataSource</code> implementations
|
||||
* that are designed to perform lookups keyed on {@link FilterInvocation}s.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface FilterInvocationSecurityMetadataSource extends SecurityMetadataSource {}
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.intercept.web;
|
||||
|
||||
import org.springframework.security.intercept.AbstractSecurityInterceptor;
|
||||
import org.springframework.security.intercept.InterceptorStatusToken;
|
||||
import org.springframework.security.intercept.SecurityMetadataSource;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Performs security handling of HTTP resources via a filter implementation.
|
||||
* <p>
|
||||
* The <code>SecurityMetadataSource</code> required by this security interceptor is of type {@link
|
||||
* FilterInvocationSecurityMetadataSource}.
|
||||
* <p>
|
||||
* Refer to {@link AbstractSecurityInterceptor} for details on the workflow.</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter, Ordered {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private FilterInvocationSecurityMetadataSource securityMetadataSource;
|
||||
private boolean observeOncePerRequest = true;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Not used (we rely on IoC container lifecycle services instead)
|
||||
*
|
||||
* @param arg0 ignored
|
||||
*
|
||||
* @throws ServletException never thrown
|
||||
*/
|
||||
public void init(FilterConfig arg0) throws ServletException {}
|
||||
|
||||
/**
|
||||
* Not used (we rely on IoC container lifecycle services instead)
|
||||
*/
|
||||
public void destroy() {}
|
||||
|
||||
/**
|
||||
* Method that is actually called by the filter chain. Simply delegates to the {@link
|
||||
* #invoke(FilterInvocation)} method.
|
||||
*
|
||||
* @param request the servlet request
|
||||
* @param response the servlet response
|
||||
* @param chain the filter chain
|
||||
*
|
||||
* @throws IOException if the filter chain fails
|
||||
* @throws ServletException if the filter chain fails
|
||||
*/
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
FilterInvocation fi = new FilterInvocation(request, response, chain);
|
||||
invoke(fi);
|
||||
}
|
||||
|
||||
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
|
||||
return this.securityMetadataSource;
|
||||
}
|
||||
|
||||
public Class<? extends Object> getSecureObjectClass() {
|
||||
return FilterInvocation.class;
|
||||
}
|
||||
|
||||
public void invoke(FilterInvocation fi) throws IOException, ServletException {
|
||||
if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
|
||||
&& observeOncePerRequest) {
|
||||
// filter already applied to this request and user wants us to observe
|
||||
// once-per-request handling, so don't re-do security checking
|
||||
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
|
||||
} else {
|
||||
// first time this request being called, so perform security checking
|
||||
if (fi.getRequest() != null) {
|
||||
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
||||
}
|
||||
|
||||
InterceptorStatusToken token = super.beforeInvocation(fi);
|
||||
|
||||
try {
|
||||
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
|
||||
} finally {
|
||||
super.afterInvocation(token, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether once-per-request handling will be observed. By default this is <code>true</code>,
|
||||
* meaning the <code>FilterSecurityInterceptor</code> will only execute once-per-request. Sometimes users may wish
|
||||
* it to execute more than once per request, such as when JSP forwards are being used and filter security is
|
||||
* desired on each included fragment of the HTTP request.
|
||||
*
|
||||
* @return <code>true</code> (the default) if once-per-request is honoured, otherwise <code>false</code> if
|
||||
* <code>FilterSecurityInterceptor</code> will enforce authorizations for each and every fragment of the
|
||||
* HTTP request.
|
||||
*/
|
||||
public boolean isObserveOncePerRequest() {
|
||||
return observeOncePerRequest;
|
||||
}
|
||||
|
||||
public SecurityMetadataSource obtainSecurityMetadataSource() {
|
||||
return this.securityMetadataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use setSecurityMetadataSource instead
|
||||
*/
|
||||
public void setObjectDefinitionSource(FilterInvocationSecurityMetadataSource newSource) {
|
||||
logger.warn("The property 'objectDefinitionSource' is deprecated. Please use 'securityMetadataSource' instead");
|
||||
this.securityMetadataSource = newSource;
|
||||
}
|
||||
|
||||
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
|
||||
this.securityMetadataSource = newSource;
|
||||
}
|
||||
|
||||
public void setObserveOncePerRequest(boolean observeOncePerRequest) {
|
||||
this.observeOncePerRequest = observeOncePerRequest;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.FILTER_SECURITY_INTERCEPTOR;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package org.springframework.security.intercept.web;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public class RequestKey {
|
||||
private String url;
|
||||
private String method;
|
||||
|
||||
public RequestKey(String url) {
|
||||
this(url, null);
|
||||
}
|
||||
|
||||
public RequestKey(String url, String method) {
|
||||
this.url = url;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int code = 31;
|
||||
code ^= url.hashCode();
|
||||
|
||||
if (method != null) {
|
||||
code ^= method.hashCode();
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof RequestKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RequestKey key = (RequestKey) obj;
|
||||
|
||||
if (!url.equals(key.url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method == null) {
|
||||
return key.method == null;
|
||||
}
|
||||
|
||||
return method.equals(key.method);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(url.length() + 7);
|
||||
sb.append("[");
|
||||
if (method != null) {
|
||||
sb.append(method).append(",");
|
||||
}
|
||||
sb.append(url);
|
||||
sb.append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.intercept.web;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.AccessDeniedException;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.intercept.AbstractSecurityInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Allows users to determine whether they have privileges for a given web URI.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class WebInvocationPrivilegeEvaluator implements InitializingBean {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(WebInvocationPrivilegeEvaluator.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AbstractSecurityInterceptor securityInterceptor;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(securityInterceptor, "SecurityInterceptor required");
|
||||
}
|
||||
|
||||
public boolean isAllowed(FilterInvocation fi, Authentication authentication) {
|
||||
Assert.notNull(fi, "FilterInvocation required");
|
||||
|
||||
List<ConfigAttribute> attrs = securityInterceptor.obtainSecurityMetadataSource().getAttributes(fi);
|
||||
|
||||
if (attrs == null) {
|
||||
if (securityInterceptor.isRejectPublicInvocations()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((authentication == null) || (authentication.getAuthorities() == null)
|
||||
|| authentication.getAuthorities().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
securityInterceptor.getAccessDecisionManager().decide(authentication, fi, attrs);
|
||||
} catch (AccessDeniedException unauthorized) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(fi.toString() + " denied for " + authentication.toString(), unauthorized);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setSecurityInterceptor(AbstractSecurityInterceptor securityInterceptor) {
|
||||
Assert.notNull(securityInterceptor, "AbstractSecurityInterceptor cannot be null");
|
||||
Assert.isTrue(FilterInvocation.class.equals(securityInterceptor.getSecureObjectClass()),
|
||||
"AbstractSecurityInterceptor does not support FilterInvocations");
|
||||
Assert.notNull(securityInterceptor.getAccessDecisionManager(),
|
||||
"AbstractSecurityInterceptor must provide a non-null AccessDecisionManager");
|
||||
this.securityInterceptor = securityInterceptor;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Enforces security for HTTP requests, typically by the URL requested.
|
||||
</body>
|
||||
</html>
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.providers.anonymous;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
|
||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
import org.springframework.security.userdetails.memory.UserAttribute;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Detects if there is no <code>Authentication</code> object in the <code>SecurityContextHolder</code>, and
|
||||
* populates it with one if needed.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AnonymousProcessingFilter extends SpringSecurityFilter implements InitializingBean {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
||||
private String key;
|
||||
private UserAttribute userAttribute;
|
||||
private boolean removeAfterRequest = true;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(userAttribute);
|
||||
Assert.hasLength(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables subclasses to determine whether or not an anonymous authentication token should be setup for
|
||||
* this request. This is useful if anonymous authentication should be allowed only for specific IP subnet ranges
|
||||
* etc.
|
||||
*
|
||||
* @param request to assist the method determine request details
|
||||
*
|
||||
* @return <code>true</code> if the anonymous token should be setup for this request (provided that the request
|
||||
* doesn't already have some other <code>Authentication</code> inside it), or <code>false</code> if no
|
||||
* anonymous token should be setup for this request
|
||||
*/
|
||||
protected boolean applyAnonymousForThisRequest(HttpServletRequest request) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Authentication createAuthentication(HttpServletRequest request) {
|
||||
AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key, userAttribute.getPassword(),
|
||||
userAttribute.getAuthorities());
|
||||
auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
protected void doFilterHttp(HttpServletRequest request,HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
boolean addedToken = false;
|
||||
|
||||
if (applyAnonymousForThisRequest(request)) {
|
||||
if (SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
SecurityContextHolder.getContext().setAuthentication(createAuthentication(request));
|
||||
addedToken = true;
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Populated SecurityContextHolder with anonymous token: '"
|
||||
+ SecurityContextHolder.getContext().getAuthentication() + "'");
|
||||
}
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"
|
||||
+ SecurityContextHolder.getContext().getAuthentication() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
chain.doFilter(request, response);
|
||||
} finally {
|
||||
if (addedToken && removeAfterRequest
|
||||
&& createAuthentication(request).equals(SecurityContextHolder.getContext().getAuthentication())) {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.ANONYMOUS_FILTER;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public UserAttribute getUserAttribute() {
|
||||
return userAttribute;
|
||||
}
|
||||
|
||||
public boolean isRemoveAfterRequest() {
|
||||
return removeAfterRequest;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether the filter will remove the Anonymous token after the request is complete. Generally
|
||||
* this is desired to avoid the expense of a session being created by {@link
|
||||
* org.springframework.security.context.HttpSessionContextIntegrationFilter HttpSessionContextIntegrationFilter} simply to
|
||||
* store the Anonymous authentication token.<p>Defaults to <code>true</code>, being the most optimal and
|
||||
* appropriate option (ie <code>AnonymousProcessingFilter</code> will clear the token at the end of each request,
|
||||
* thus avoiding the session creation overhead in a typical configuration.</p>
|
||||
*
|
||||
* @param removeAfterRequest DOCUMENT ME!
|
||||
*/
|
||||
public void setRemoveAfterRequest(boolean removeAfterRequest) {
|
||||
this.removeAfterRequest = removeAfterRequest;
|
||||
}
|
||||
|
||||
public void setUserAttribute(UserAttribute userAttributeDefinition) {
|
||||
this.userAttribute = userAttributeDefinition;
|
||||
}
|
||||
}
|
||||
+5
-7
@@ -41,13 +41,12 @@ import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.SpringSecurityException;
|
||||
import org.springframework.security.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.context.SecurityContext;
|
||||
import org.springframework.security.event.SessionDestroyedEvent;
|
||||
import org.springframework.security.providers.AuthenticationProvider;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.providers.jaas.event.JaasAuthenticationFailedEvent;
|
||||
import org.springframework.security.providers.jaas.event.JaasAuthenticationSuccessEvent;
|
||||
import org.springframework.security.ui.session.HttpSessionDestroyedEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -311,9 +310,8 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Appli
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
protected void handleLogout(HttpSessionDestroyedEvent event) {
|
||||
SecurityContext context = (SecurityContext)
|
||||
event.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||
protected void handleLogout(SessionDestroyedEvent event) {
|
||||
SecurityContext context = event.getSecurityContext();
|
||||
|
||||
if (context == null) {
|
||||
log.debug("The destroyed session has no SecurityContext");
|
||||
@@ -343,8 +341,8 @@ public class JaasAuthenticationProvider implements AuthenticationProvider, Appli
|
||||
}
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent applicationEvent) {
|
||||
if (applicationEvent instanceof HttpSessionDestroyedEvent) {
|
||||
HttpSessionDestroyedEvent event = (HttpSessionDestroyedEvent) applicationEvent;
|
||||
if (applicationEvent instanceof SessionDestroyedEvent) {
|
||||
SessionDestroyedEvent event = (SessionDestroyedEvent) applicationEvent;
|
||||
handleLogout(event);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* <p>
|
||||
* This authentication provider will not perform any checks on authentication
|
||||
* requests, as they should already be pre- authenticated. However, the
|
||||
* requests, as they should already be pre-authenticated. However, the
|
||||
* AuthenticationUserDetailsService implementation may still throw a UsernameNotFoundException, for example.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.context.httpinvoker;
|
||||
package org.springframework.security.remoting.httpinvoker;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationCredentialsNotFoundException;
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.context.rmi;
|
||||
package org.springframework.security.remoting.rmi;
|
||||
|
||||
import org.springframework.security.context.SecurityContext;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.context.rmi;
|
||||
package org.springframework.security.remoting.rmi;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
package org.springframework.security.securechannel;
|
||||
|
||||
import org.springframework.security.util.PortMapper;
|
||||
import org.springframework.security.util.PortResolver;
|
||||
import org.springframework.security.util.PortMapperImpl;
|
||||
import org.springframework.security.util.PortResolverImpl;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class AbstractRetryEntryPoint implements ChannelEntryPoint {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private PortMapper portMapper = new PortMapperImpl();
|
||||
private PortResolver portResolver = new PortResolverImpl();
|
||||
/** The scheme ("http://" or "https://") */
|
||||
private String scheme;
|
||||
/** The standard port for the scheme (80 for http, 443 for https) */
|
||||
private int standardPort;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public AbstractRetryEntryPoint(String scheme, int standardPort) {
|
||||
this.scheme = scheme;
|
||||
this.standardPort = standardPort;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void commence(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
|
||||
String pathInfo = request.getPathInfo();
|
||||
String queryString = request.getQueryString();
|
||||
String contextPath = request.getContextPath();
|
||||
String destination = request.getServletPath() + ((pathInfo == null) ? "" : pathInfo)
|
||||
+ ((queryString == null) ? "" : ("?" + queryString));
|
||||
|
||||
String redirectUrl = contextPath;
|
||||
|
||||
Integer currentPort = new Integer(portResolver.getServerPort(request));
|
||||
Integer redirectPort = getMappedPort(currentPort);
|
||||
|
||||
if (redirectPort != null) {
|
||||
boolean includePort = redirectPort.intValue() != standardPort;
|
||||
|
||||
redirectUrl = scheme + request.getServerName() + ((includePort) ? (":" + redirectPort) : "") + contextPath
|
||||
+ destination;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Redirecting to: " + redirectUrl);
|
||||
}
|
||||
|
||||
((HttpServletResponse) res).sendRedirect(((HttpServletResponse) res).encodeRedirectURL(redirectUrl));
|
||||
}
|
||||
|
||||
protected abstract Integer getMappedPort(Integer mapFromPort);
|
||||
|
||||
protected PortMapper getPortMapper() {
|
||||
return portMapper;
|
||||
}
|
||||
|
||||
protected PortResolver getPortResolver() {
|
||||
return portResolver;
|
||||
}
|
||||
|
||||
public void setPortMapper(PortMapper portMapper) {
|
||||
Assert.notNull(portMapper, "portMapper cannot be null");
|
||||
this.portMapper = portMapper;
|
||||
}
|
||||
|
||||
public void setPortResolver(PortResolver portResolver) {
|
||||
Assert.notNull(portResolver, "portResolver cannot be null");
|
||||
this.portResolver = portResolver;
|
||||
}
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
|
||||
/**
|
||||
* Decides whether a web channel provides sufficient security.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface ChannelDecisionManager {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Decided whether the presented {@link FilterInvocation} provides the appropriate level of channel
|
||||
* security based on the requested list of <tt>ConfigAttribute</tt>s.
|
||||
*
|
||||
*/
|
||||
void decide(FilterInvocation invocation, List<ConfigAttribute> config) throws IOException, ServletException;
|
||||
|
||||
/**
|
||||
* Indicates whether this <code>ChannelDecisionManager</code> is able to process the passed
|
||||
* <code>ConfigAttribute</code>.<p>This allows the <code>ChannelProcessingFilter</code> to check every
|
||||
* configuration attribute can be consumed by the configured <code>ChannelDecisionManager</code>.</p>
|
||||
*
|
||||
* @param attribute a configuration attribute that has been configured against the
|
||||
* <code>ChannelProcessingFilter</code>
|
||||
*
|
||||
* @return true if this <code>ChannelDecisionManager</code> can support the passed configuration attribute
|
||||
*/
|
||||
boolean supports(ConfigAttribute attribute);
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link ChannelDecisionManager}.
|
||||
* <p>
|
||||
* Iterates through each configured {@link ChannelProcessor}. If a <code>ChannelProcessor</code> has any issue with the
|
||||
* security of the request, it should cause a redirect, exception or whatever other action is appropriate for the
|
||||
* <code>ChannelProcessor</code> implementation.
|
||||
* <p>
|
||||
* Once any response is committed (ie a redirect is written to the response object), the
|
||||
* <code>ChannelDecisionManagerImpl</code> will not iterate through any further <code>ChannelProcessor</code>s.
|
||||
* <p>
|
||||
* The attribute "ANY_CHANNEL" if applied to a particular URL, the iteration through the channel processors will be
|
||||
* skipped (see SEC-494, SEC-335).
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ChannelDecisionManagerImpl implements ChannelDecisionManager, InitializingBean {
|
||||
|
||||
public static final String ANY_CHANNEL = "ANY_CHANNEL";
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private List<ChannelProcessor> channelProcessors;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notEmpty(channelProcessors, "A list of ChannelProcessors is required");
|
||||
}
|
||||
|
||||
public void decide(FilterInvocation invocation, List<ConfigAttribute> config) throws IOException, ServletException {
|
||||
|
||||
Iterator<ConfigAttribute> attrs = config.iterator();
|
||||
|
||||
while (attrs.hasNext()) {
|
||||
ConfigAttribute attribute = attrs.next();
|
||||
if (ANY_CHANNEL.equals(attribute.getAttribute())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (ChannelProcessor processor : channelProcessors) {
|
||||
processor.decide(invocation, config);
|
||||
|
||||
if (invocation.getResponse().isCommitted()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List<ChannelProcessor> getChannelProcessors() {
|
||||
return this.channelProcessors;
|
||||
}
|
||||
|
||||
@SuppressWarnings("cast")
|
||||
public void setChannelProcessors(List<?> newList) {
|
||||
Assert.notEmpty(newList, "A list of ChannelProcessors is required");
|
||||
channelProcessors = new ArrayList<ChannelProcessor>(newList.size());
|
||||
|
||||
for (Object currentObject : newList) {
|
||||
Assert.isInstanceOf(ChannelProcessor.class, currentObject, "ChannelProcessor " +
|
||||
currentObject.getClass().getName() + " must implement ChannelProcessor");
|
||||
channelProcessors.add((ChannelProcessor)currentObject);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
if (ANY_CHANNEL.equals(attribute.getAttribute())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (ChannelProcessor processor : channelProcessors) {
|
||||
if (processor.supports(attribute)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* May be used by a {@link ChannelProcessor} to launch a web channel.
|
||||
*
|
||||
* <p>
|
||||
* <code>ChannelProcessor</code>s can elect to launch a new web channel directly, or they can delegate to another class.
|
||||
* The <code>ChannelEntryPoint</code> is a pluggable interface to assist <code>ChannelProcessor</code>s in performing
|
||||
* this delegation.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface ChannelEntryPoint {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Commences a secure channel.
|
||||
* <p>
|
||||
* Implementations should modify the headers on the <code>ServletResponse</code> as necessary to commence the user
|
||||
* agent using the implementation's supported channel type.
|
||||
*
|
||||
* @param request that a <code>ChannelProcessor</code> has rejected
|
||||
* @param response so that the user agent can begin using a new channel
|
||||
*
|
||||
*/
|
||||
void commence(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
|
||||
}
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
import org.springframework.security.intercept.web.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Ensures a web request is delivered over the required channel.
|
||||
* <p>Internally uses a {@link FilterInvocation} to represent the request, so that the
|
||||
* <code>FilterInvocation</code>-related property editors and lookup classes can be used.</p>
|
||||
* <p>Delegates the actual channel security decisions and necessary actions to the configured
|
||||
* {@link ChannelDecisionManager}. If a response is committed by the <code>ChannelDecisionManager</code>,
|
||||
* the filter chain will not proceed.</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ChannelProcessingFilter extends SpringSecurityFilter implements InitializingBean {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private ChannelDecisionManager channelDecisionManager;
|
||||
private FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(filterInvocationSecurityMetadataSource, "filterInvocationSecurityMetadataSource must be specified");
|
||||
Assert.notNull(channelDecisionManager, "channelDecisionManager must be specified");
|
||||
|
||||
Collection<ConfigAttribute> attrDefs = this.filterInvocationSecurityMetadataSource.getAllConfigAttributes();
|
||||
|
||||
if (attrDefs == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not validate configuration attributes as the FilterInvocationSecurityMetadataSource did "
|
||||
+ "not return any attributes");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Set<ConfigAttribute> unsupportedAttributes = new HashSet<ConfigAttribute>();
|
||||
|
||||
for (ConfigAttribute attr : attrDefs) {
|
||||
if (!this.channelDecisionManager.supports(attr)) {
|
||||
unsupportedAttributes.add(attr);
|
||||
}
|
||||
}
|
||||
|
||||
if (unsupportedAttributes.size() == 0) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Validated configuration attributes");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported configuration attributes: " + unsupportedAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
FilterInvocation fi = new FilterInvocation(request, response, chain);
|
||||
List<ConfigAttribute> attr = this.filterInvocationSecurityMetadataSource.getAttributes(fi);
|
||||
|
||||
if (attr != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Request: " + fi.toString() + "; ConfigAttributes: " + attr);
|
||||
}
|
||||
|
||||
channelDecisionManager.decide(fi, attr);
|
||||
|
||||
if (fi.getResponse().isCommitted()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
public ChannelDecisionManager getChannelDecisionManager() {
|
||||
return channelDecisionManager;
|
||||
}
|
||||
|
||||
public FilterInvocationSecurityMetadataSource getFilterInvocationSecurityMetadataSource() {
|
||||
return filterInvocationSecurityMetadataSource;
|
||||
}
|
||||
|
||||
public void setChannelDecisionManager(ChannelDecisionManager channelDecisionManager) {
|
||||
this.channelDecisionManager = channelDecisionManager;
|
||||
}
|
||||
|
||||
public void setFilterInvocationSecurityMetadataSource(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) {
|
||||
this.filterInvocationSecurityMetadataSource = filterInvocationSecurityMetadataSource;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.CHANNEL_FILTER;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
|
||||
/**
|
||||
* Decides whether a web channel meets a specific security condition.
|
||||
* <p>
|
||||
* <code>ChannelProcessor</code> implementations are iterated by the {@link ChannelDecisionManagerImpl}.
|
||||
* <p>
|
||||
* If an implementation has an issue with the channel security, they should
|
||||
* take action themselves. The callers of the implementation do not take any
|
||||
* action.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface ChannelProcessor {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Decided whether the presented {@link FilterInvocation} provides the appropriate level of channel
|
||||
* security based on the requested list of <tt>ConfigAttribute</tt>s.
|
||||
*
|
||||
*/
|
||||
void decide(FilterInvocation invocation, List<ConfigAttribute> config) throws IOException, ServletException;
|
||||
|
||||
/**
|
||||
* Indicates whether this <code>ChannelProcessor</code> is able to process the passed
|
||||
* <code>ConfigAttribute</code>.
|
||||
* <p>
|
||||
* This allows the <code>ChannelProcessingFilter</code> to check every configuration attribute can be consumed
|
||||
* by the configured <code>ChannelDecisionManager</code>.
|
||||
*
|
||||
* @param attribute a configuration attribute that has been configured against the <tt>ChannelProcessingFilter</tt>.
|
||||
*
|
||||
* @return true if this <code>ChannelProcessor</code> can support the passed configuration attribute
|
||||
*/
|
||||
boolean supports(ConfigAttribute attribute);
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Ensures channel security is inactive by review of <code>HttpServletRequest.isSecure()</code> responses.
|
||||
* <p>
|
||||
* The class responds to one case-sensitive keyword, {@link #getInsecureKeyword}. If this keyword is detected,
|
||||
* <code>HttpServletRequest.isSecure()</code> is used to determine the channel security offered. If channel security
|
||||
* is present, the configured <code>ChannelEntryPoint</code> is called. By default the entry point is {@link
|
||||
* RetryWithHttpEntryPoint}.
|
||||
* <p>
|
||||
* The default <code>insecureKeyword</code> is <code>REQUIRES_INSECURE_CHANNEL</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InsecureChannelProcessor implements InitializingBean, ChannelProcessor {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private ChannelEntryPoint entryPoint = new RetryWithHttpEntryPoint();
|
||||
private String insecureKeyword = "REQUIRES_INSECURE_CHANNEL";
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(insecureKeyword, "insecureKeyword required");
|
||||
Assert.notNull(entryPoint, "entryPoint required");
|
||||
}
|
||||
|
||||
public void decide(FilterInvocation invocation, List<ConfigAttribute> config) throws IOException, ServletException {
|
||||
if ((invocation == null) || (config == null)) {
|
||||
throw new IllegalArgumentException("Nulls cannot be provided");
|
||||
}
|
||||
|
||||
for (ConfigAttribute attribute : config) {
|
||||
if (supports(attribute)) {
|
||||
if (invocation.getHttpRequest().isSecure()) {
|
||||
entryPoint.commence(invocation.getRequest(), invocation.getResponse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ChannelEntryPoint getEntryPoint() {
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
public String getInsecureKeyword() {
|
||||
return insecureKeyword;
|
||||
}
|
||||
|
||||
public void setEntryPoint(ChannelEntryPoint entryPoint) {
|
||||
this.entryPoint = entryPoint;
|
||||
}
|
||||
|
||||
public void setInsecureKeyword(String secureKeyword) {
|
||||
this.insecureKeyword = secureKeyword;
|
||||
}
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
if ((attribute != null) && (attribute.getAttribute() != null)
|
||||
&& attribute.getAttribute().equals(getInsecureKeyword())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
|
||||
/**
|
||||
* Commences an insecure channel by retrying the original request using HTTP.
|
||||
* <p>
|
||||
* This entry point should suffice in most circumstances. However, it is not intended to properly handle HTTP POSTs or
|
||||
* other usage where a standard redirect would cause an issue.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class RetryWithHttpEntryPoint extends AbstractRetryEntryPoint {
|
||||
|
||||
public RetryWithHttpEntryPoint() {
|
||||
super("http://", 80);
|
||||
}
|
||||
|
||||
protected Integer getMappedPort(Integer mapFromPort) {
|
||||
return getPortMapper().lookupHttpPort(mapFromPort);
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
/**
|
||||
* Commences a secure channel by retrying the original request using HTTPS.
|
||||
* <p>
|
||||
* This entry point should suffice in most circumstances. However, it is not intended to properly handle HTTP POSTs
|
||||
* or other usage where a standard redirect would cause an issue.</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class RetryWithHttpsEntryPoint extends AbstractRetryEntryPoint {
|
||||
|
||||
public RetryWithHttpsEntryPoint() {
|
||||
super("https://", 443);
|
||||
}
|
||||
|
||||
protected Integer getMappedPort(Integer mapFromPort) {
|
||||
return getPortMapper().lookupHttpsPort(mapFromPort);
|
||||
}
|
||||
}
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.securechannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.ConfigAttribute;
|
||||
import org.springframework.security.intercept.web.FilterInvocation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Ensures channel security is active by review of <code>HttpServletRequest.isSecure()</code> responses.
|
||||
* <p>
|
||||
* The class responds to one case-sensitive keyword, {@link #getSecureKeyword}. If this keyword is detected,
|
||||
* <code>HttpServletRequest.isSecure()</code> is used to determine the channel security offered. If channel security
|
||||
* is not present, the configured <code>ChannelEntryPoint</code> is called. By default the entry point is {@link
|
||||
* RetryWithHttpsEntryPoint}.
|
||||
* <p>
|
||||
* The default <code>secureKeyword</code> is <code>REQUIRES_SECURE_CHANNEL</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SecureChannelProcessor implements InitializingBean, ChannelProcessor {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private ChannelEntryPoint entryPoint = new RetryWithHttpsEntryPoint();
|
||||
private String secureKeyword = "REQUIRES_SECURE_CHANNEL";
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(secureKeyword, "secureKeyword required");
|
||||
Assert.notNull(entryPoint, "entryPoint required");
|
||||
}
|
||||
|
||||
public void decide(FilterInvocation invocation, List<ConfigAttribute> config) throws IOException, ServletException {
|
||||
Assert.isTrue((invocation != null) && (config != null), "Nulls cannot be provided");
|
||||
|
||||
for (ConfigAttribute attribute : config) {
|
||||
if (supports(attribute)) {
|
||||
if (!invocation.getHttpRequest().isSecure()) {
|
||||
entryPoint.commence(invocation.getRequest(), invocation.getResponse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ChannelEntryPoint getEntryPoint() {
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
public String getSecureKeyword() {
|
||||
return secureKeyword;
|
||||
}
|
||||
|
||||
public void setEntryPoint(ChannelEntryPoint entryPoint) {
|
||||
this.entryPoint = entryPoint;
|
||||
}
|
||||
|
||||
public void setSecureKeyword(String secureKeyword) {
|
||||
this.secureKeyword = secureKeyword;
|
||||
}
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
if ((attribute != null) && (attribute.getAttribute() != null)
|
||||
&& attribute.getAttribute().equals(getSecureKeyword())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Classes that ensure web requests are received over required
|
||||
transport channels.
|
||||
</body>
|
||||
</html>
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.ui.logout.LogoutHandler;
|
||||
import org.springframework.security.util.RedirectUtils;
|
||||
import org.springframework.security.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class containing the logic used by strategies which handle redirection to a URL and
|
||||
* are passed an <tt>Authentication</tt> object as part of the contract.
|
||||
* See {@link AuthenticationSuccessHandler} and {@link LogoutHandler}, for example.
|
||||
* <p>
|
||||
* Uses the following logic sequence to determine how it should handle the forward/redirect
|
||||
* <ul>
|
||||
* <li>
|
||||
* If the <tt>alwaysUseDefaultTargetUrl</tt> property is set to true, the <tt>defaultTargetUrl</tt> property
|
||||
* will be used for the destination.
|
||||
* </li>
|
||||
* <li>
|
||||
* If a parameter matching the <tt>targetUrlParameter</tt> has been set on the request, the value will be used as
|
||||
* the destination.
|
||||
* </li>
|
||||
* <li>
|
||||
* If the <tt>useReferer</tt> property is set, the "Referer" HTTP header value will be used, if present.
|
||||
* </li>
|
||||
* <li>
|
||||
* As a fallback option, the <tt>defaultTargetUrl</tt> value will be used.
|
||||
* </li>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public abstract class AbstractAuthenticationTargetUrlRequestHandler {
|
||||
|
||||
public static String DEFAULT_TARGET_PARAMETER = "spring-security-redirect";
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
private String targetUrlParameter = DEFAULT_TARGET_PARAMETER;
|
||||
private String defaultTargetUrl = "/";
|
||||
private boolean alwaysUseDefaultTargetUrl = false;
|
||||
private boolean useRelativeContext = false;
|
||||
private boolean useReferer = false;
|
||||
|
||||
protected AbstractAuthenticationTargetUrlRequestHandler() {
|
||||
}
|
||||
|
||||
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
String targetUrl = determineTargetUrl(request, response);
|
||||
|
||||
RedirectUtils.sendRedirect(request, response, targetUrl, useRelativeContext);
|
||||
}
|
||||
|
||||
private String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
|
||||
if (isAlwaysUseDefaultTargetUrl()) {
|
||||
return defaultTargetUrl;
|
||||
}
|
||||
|
||||
// Check for the parameter and use that if available
|
||||
String targetUrl = request.getParameter(targetUrlParameter);
|
||||
|
||||
if (StringUtils.hasText(targetUrl)) {
|
||||
try {
|
||||
targetUrl = URLDecoder.decode(targetUrl, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("UTF-8 not supported. Shouldn't be possible");
|
||||
}
|
||||
|
||||
logger.debug("Found targetUrlParameter in request: " + targetUrl);
|
||||
|
||||
return targetUrl;
|
||||
}
|
||||
|
||||
if (useReferer && !StringUtils.hasLength(targetUrl)) {
|
||||
targetUrl = request.getHeader("Referer");
|
||||
logger.debug("Using Referer header: " + targetUrl);
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(targetUrl)) {
|
||||
targetUrl = defaultTargetUrl;
|
||||
logger.debug("Using default Url: " + targetUrl);
|
||||
}
|
||||
|
||||
return targetUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the default target Url that will be used if no saved request is found or the
|
||||
* <tt>alwaysUseDefaultTargetUrl</tt> property is set to true. If not set, defaults to <tt>/</tt>.
|
||||
*
|
||||
* @return the defaultTargetUrl property
|
||||
*/
|
||||
protected String getDefaultTargetUrl() {
|
||||
return defaultTargetUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the default target Url that will be used if no saved request is found in the session, or the
|
||||
* <tt>alwaysUseDefaultTargetUrl</tt> property is set to true. If not set, defaults to <tt>/</tt>. It
|
||||
* will be treated as relative to the web-app's context path, and should include the leading <code>/</code>.
|
||||
* Alternatively, inclusion of a scheme name (such as "http://" or "https://") as the prefix will denote a
|
||||
* fully-qualified URL and this is also supported.
|
||||
*
|
||||
* @param defaultTargetUrl
|
||||
*/
|
||||
public void setDefaultTargetUrl(String defaultTargetUrl) {
|
||||
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl),
|
||||
"defaultTarget must start with '/' or with 'http(s)'");
|
||||
this.defaultTargetUrl = defaultTargetUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <code>true</code>, will always redirect to the value of <tt>defaultTargetUrl</tt>
|
||||
* (defaults to <code>false</code>).
|
||||
*/
|
||||
public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
|
||||
this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
|
||||
}
|
||||
|
||||
protected boolean isAlwaysUseDefaultTargetUrl() {
|
||||
return alwaysUseDefaultTargetUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current request will be checked for this parameter before and the value used as the target URL if resent.
|
||||
*
|
||||
* @param targetUrlParameter the name of the parameter containing the encoded target URL. Defaults
|
||||
* to "redirect".
|
||||
*/
|
||||
public void setTargetUrlParameter(String targetUrlParameter) {
|
||||
Assert.hasText("targetUrlParameter canot be null or empty");
|
||||
this.targetUrlParameter = targetUrlParameter;
|
||||
}
|
||||
|
||||
protected String getTargetUrlParameter() {
|
||||
return targetUrlParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <tt>true</tt>, causes any redirection URLs to be calculated minus the protocol
|
||||
* and context path (defaults to <tt>false</tt>).
|
||||
*/
|
||||
public void setUseRelativeContext(boolean useRelativeContext) {
|
||||
this.useRelativeContext = useRelativeContext;
|
||||
}
|
||||
|
||||
protected boolean isUseRelativeContext() {
|
||||
return useRelativeContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <tt>true</tt> the <tt>Referer</tt> header will be used (if available). Defaults to <tt>false</tt>.
|
||||
*/
|
||||
public void setUseReferer(boolean useReferer) {
|
||||
this.useReferer = useReferer;
|
||||
}
|
||||
}
|
||||
@@ -1,439 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.AuthenticationManager;
|
||||
import org.springframework.security.SpringSecurityMessageSource;
|
||||
import org.springframework.security.concurrent.SessionRegistry;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
|
||||
import org.springframework.security.ui.rememberme.NullRememberMeServices;
|
||||
import org.springframework.security.ui.rememberme.RememberMeServices;
|
||||
import org.springframework.security.util.SessionUtils;
|
||||
import org.springframework.security.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract processor of browser-based HTTP-based authentication requests.
|
||||
*
|
||||
* <h3>Authentication Process</h3>
|
||||
*
|
||||
* The filter requires that you set the <tt>authenticationManager</tt> property. An <tt>AuthenticationManager</tt> is
|
||||
* required to process the authentication request tokens created by implementing classes.
|
||||
* <p>
|
||||
* This filter will intercept a request and attempt to perform authentication from that request if
|
||||
* the request URL matches the value of the <tt>filterProcessesUrl</tt> property. This behaviour can modified by
|
||||
* overriding the method {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse) requiresAuthentication}.
|
||||
* <p>
|
||||
* Authentication is performed by the {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse)
|
||||
* attemptAuthentication} method, which must be implemented by subclasses.
|
||||
*
|
||||
* <h4>Authentication Success</h4>
|
||||
*
|
||||
* If authentication is successful, the resulting {@link Authentication} object will be placed into the
|
||||
* <code>SecurityContext</code> for the current thread, which is guaranteed to have already been created by an earlier
|
||||
* filter. The configured {@link #setAuthenticationSuccessHandler(AuthenticationSuccessHandler) AuthenticationSuccessHandler} will
|
||||
* then be called to take the redirect to the appropriate destination after a successful login. The default behaviour
|
||||
* is implemented in a {@link SavedRequestAwareAuthenticationSuccessHandler} which will make use of any
|
||||
* <tt>SavedRequest</tt> set by the <tt>ExceptionTranslationFilter</tt> and redirect the user to the URL contained
|
||||
* therein. Otherwise it will redirect to the webapp root "/". You can customize this behaviour by injecting a
|
||||
* differently configured instance of this class, or by using a different implementation.
|
||||
* <p>
|
||||
* See the {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
|
||||
* successfulAuthentication} method for more information.
|
||||
*
|
||||
* <h4>Authentication Failure</h4>
|
||||
*
|
||||
* If authentication fails, the resulting <tt>AuthenticationException</tt> will be placed into the <tt>HttpSession</tt>
|
||||
* with the attribute defined by {@link #SPRING_SECURITY_LAST_EXCEPTION_KEY}. It will then delegate to the configured
|
||||
* {@link AuthenticationFailureHandler} to allow the failure information to be conveyed to the client.
|
||||
* The default implementation is {@link SimpleUrlAuthenticationFailureHandler}, which sends a 401 error code to the
|
||||
* client. It may also be configured with a failure URL as an alternative. Again you can inject whatever
|
||||
* behaviour you require here.
|
||||
*
|
||||
* <h4>Event Publication</h4>
|
||||
*
|
||||
* If authentication is successful, an
|
||||
* {@link org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent
|
||||
* InteractiveAuthenticationSuccessEvent} will be published via the application context. No events will be published if
|
||||
* authentication was unsuccessful, because this would generally be recorded via an
|
||||
* <tt>AuthenticationManager</tt>-specific application event.
|
||||
* <p>
|
||||
* The filter has an optional attribute <tt>invalidateSessionOnSuccessfulAuthentication</tt> that will invalidate
|
||||
* the current session on successful authentication. This is to protect against session fixation attacks (see
|
||||
* <a href="http://en.wikipedia.org/wiki/Session_fixation">this Wikipedia article</a> for more information).
|
||||
* The behaviour is turned off by default. Additionally there is a property <tt>migrateInvalidatedSessionAttributes</tt>
|
||||
* which tells if on session invalidation we are to migrate all session attributes from the old session to a newly
|
||||
* created one. This is turned on by default, but not used unless <tt>invalidateSessionOnSuccessfulAuthentication</tt>
|
||||
* is true. If you are using this feature in combination with concurrent session control, you should set the
|
||||
* <tt>sessionRegistry</tt> property to make sure that the session information is updated consistently.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class AbstractProcessingFilter extends SpringSecurityFilter implements InitializingBean,
|
||||
ApplicationEventPublisherAware, MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
public static final String SPRING_SECURITY_LAST_EXCEPTION_KEY = "SPRING_SECURITY_LAST_EXCEPTION";
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected ApplicationEventPublisher eventPublisher;
|
||||
protected AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
||||
private AuthenticationManager authenticationManager;
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
/*
|
||||
* Delay use of NullRememberMeServices until initialization so that namespace has a chance to inject
|
||||
* the RememberMeServices implementation into custom implementations.
|
||||
*/
|
||||
private RememberMeServices rememberMeServices = null;
|
||||
|
||||
/**
|
||||
* The URL destination that this filter intercepts and processes (usually
|
||||
* something like <code>/j_spring_security_check</code>)
|
||||
*/
|
||||
private String filterProcessesUrl;
|
||||
|
||||
private boolean continueChainBeforeSuccessfulAuthentication = false;
|
||||
|
||||
/**
|
||||
* Tells if we on successful authentication should invalidate the
|
||||
* current session. This is a common guard against session fixation attacks.
|
||||
* Defaults to <code>false</code>.
|
||||
*/
|
||||
private boolean invalidateSessionOnSuccessfulAuthentication = false;
|
||||
|
||||
/**
|
||||
* If {@link #invalidateSessionOnSuccessfulAuthentication} is true, this
|
||||
* flag indicates that the session attributes of the session to be invalidated
|
||||
* are to be migrated to the new session. Defaults to <code>true</code> since
|
||||
* nothing will happen unless {@link #invalidateSessionOnSuccessfulAuthentication}
|
||||
* is true.
|
||||
*/
|
||||
private boolean migrateInvalidatedSessionAttributes = true;
|
||||
|
||||
private boolean allowSessionCreation = true;
|
||||
|
||||
private SessionRegistry sessionRegistry;
|
||||
|
||||
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
|
||||
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.
|
||||
*/
|
||||
protected AbstractProcessingFilter(String defaultFilterProcessesUrl) {
|
||||
this.filterProcessesUrl = defaultFilterProcessesUrl;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
|
||||
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
|
||||
Assert.notNull(authenticationManager, "authenticationManager must be specified");
|
||||
|
||||
if (rememberMeServices == null) {
|
||||
rememberMeServices = new NullRememberMeServices();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse) requiresAuthentication}
|
||||
* method to determine whether the request is for authentication and should be handled by this filter.
|
||||
* If it is an authentication request, the
|
||||
* {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse) attemptAuthentication} will be invoked
|
||||
* to perform the authentication. There are then three possible outcomes:
|
||||
* <ol>
|
||||
* <li>An <tt>Authentication</tt> object is returned.
|
||||
* The {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
|
||||
* successfulAuthentication} method will be invoked</li>
|
||||
* <li>An <tt>AuthenticationException</tt> occurs during authentication.
|
||||
* The {@link #unSuccessfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
|
||||
* unSuccessfulAuthentication} method will be invoked</li>
|
||||
* <li>Null is returned, indicating that the authentication process is incomplete.
|
||||
* The method will then return immediately, assuming that the subclass has done any necessary work (such as
|
||||
* redirects) to continue the authentication process. The assumption is that a later request will be received
|
||||
* by this method where the returned <tt>Authentication</tt> object is not null.
|
||||
* </ol>
|
||||
*/
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
if (!requiresAuthentication(request, response)) {
|
||||
chain.doFilter(request, response);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Request is to process authentication");
|
||||
}
|
||||
|
||||
Authentication authResult;
|
||||
|
||||
try {
|
||||
authResult = attemptAuthentication(request, response);
|
||||
if (authResult == null) {
|
||||
// return immediately as subclass has indicated that it hasn't completed authentication
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (AuthenticationException failed) {
|
||||
// Authentication failed
|
||||
unsuccessfulAuthentication(request, response, failed);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Authentication success
|
||||
if (continueChainBeforeSuccessfulAuthentication) {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
successfulAuthentication(request, response, authResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this filter should attempt to process a login request for the current invocation.
|
||||
* <p>
|
||||
* It strips any parameters from the "path" section of the request URL (such
|
||||
* as the jsessionid parameter in
|
||||
* <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching
|
||||
* against the <code>filterProcessesUrl</code> property.
|
||||
* <p>
|
||||
* Subclasses may override for special requirements, such as Tapestry integration.
|
||||
*
|
||||
* @return <code>true</code> if the filter should attempt authentication, <code>false</code> otherwise.
|
||||
*/
|
||||
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
|
||||
String uri = request.getRequestURI();
|
||||
int pathParamIndex = uri.indexOf(';');
|
||||
|
||||
if (pathParamIndex > 0) {
|
||||
// strip everything after the first semi-colon
|
||||
uri = uri.substring(0, pathParamIndex);
|
||||
}
|
||||
|
||||
if ("".equals(request.getContextPath())) {
|
||||
return uri.endsWith(filterProcessesUrl);
|
||||
}
|
||||
|
||||
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs actual authentication.
|
||||
* <p>
|
||||
* The implementation should do one of the following:
|
||||
* <ol>
|
||||
* <li>Return a populated authentication token for the authenticated user, indicating successful authentication</li>
|
||||
* <li>Return null, indicating that the authentication process is still in progress. Before returning, the
|
||||
* implementation should perform any additional work required to complete the process.</li>
|
||||
* <li>Throw an <tt>AuthenticationException</tt> if the authentication process fails</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param request from which to extract parameters and perform the authentication
|
||||
* @param response the response, which may be needed if the implementation has to do a redirect as part of a
|
||||
* multi-stage authentication process (such as OpenID).
|
||||
*
|
||||
* @return the authenticated user token, or null if authentication is incomplete.
|
||||
*
|
||||
* @throws AuthenticationException if authentication fails.
|
||||
*/
|
||||
public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||
throws AuthenticationException, IOException, ServletException;
|
||||
|
||||
/**
|
||||
* Default behaviour for successful authentication.
|
||||
* <ol>
|
||||
* <li>Sets the successful <tt>Authentication</tt> object on the {@link SecurityContextHolder}</li>
|
||||
* <li>Performs any configured session migration behaviour</li>
|
||||
* <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li>
|
||||
* <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured
|
||||
* <tt>ApplicationEventPublisher</tt></li>
|
||||
* <li>Delegates additional behaviour to the {@link AuthenticationSuccessHandler}.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param authResult the object returned from the <tt>attemptAuthentication</tt> method.
|
||||
*/
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authResult) throws IOException, ServletException {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
|
||||
if (invalidateSessionOnSuccessfulAuthentication) {
|
||||
SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, sessionRegistry);
|
||||
}
|
||||
|
||||
rememberMeServices.loginSuccess(request, response, authResult);
|
||||
|
||||
// Fire event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
|
||||
}
|
||||
|
||||
successHandler.onAuthenticationSuccess(request, response, authResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default behaviour for unsuccessful authentication.
|
||||
* <ol>
|
||||
* <li>Clears the {@link SecurityContextHolder}</li>
|
||||
* <li>Stores the exception in the session (if it exists or <tt>allowSesssionCreation</tt> is set to <tt>true</tt>)</li>
|
||||
* <li>Informs the configured <tt>RememberMeServices</tt> of the failed login</li>
|
||||
* <li>Delegates additional behaviour to the {@link AuthenticationFailureHandler}.</li>
|
||||
* </ol>
|
||||
*/
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException failed) throws IOException, ServletException {
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request failed: " + failed.toString());
|
||||
logger.debug("Updated SecurityContextHolder to contain null Authentication");
|
||||
logger.debug("Delegating to authentication failure handler" + failureHandler);
|
||||
}
|
||||
|
||||
try {
|
||||
HttpSession session = request.getSession(false);
|
||||
|
||||
if (session != null || allowSessionCreation) {
|
||||
request.getSession().setAttribute(SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
|
||||
}
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
}
|
||||
|
||||
rememberMeServices.loginFail(request, response);
|
||||
|
||||
failureHandler.onAuthenticationFailure(request, response, failed);
|
||||
}
|
||||
|
||||
protected AuthenticationManager getAuthenticationManager() {
|
||||
return authenticationManager;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
public String getFilterProcessesUrl() {
|
||||
return filterProcessesUrl;
|
||||
}
|
||||
|
||||
public void setFilterProcessesUrl(String filterProcessesUrl) {
|
||||
this.filterProcessesUrl = filterProcessesUrl;
|
||||
}
|
||||
|
||||
public RememberMeServices getRememberMeServices() {
|
||||
return rememberMeServices;
|
||||
}
|
||||
|
||||
public void setRememberMeServices(RememberMeServices rememberMeServices) {
|
||||
this.rememberMeServices = rememberMeServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the filter chain should be continued prior to delegation to
|
||||
* {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse,
|
||||
* Authentication)}, which may be useful in certain environment (such as
|
||||
* Tapestry applications). Defaults to <code>false</code>.
|
||||
*/
|
||||
public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
|
||||
this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
|
||||
}
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
public void setInvalidateSessionOnSuccessfulAuthentication(boolean invalidateSessionOnSuccessfulAuthentication) {
|
||||
this.invalidateSessionOnSuccessfulAuthentication = invalidateSessionOnSuccessfulAuthentication;
|
||||
}
|
||||
|
||||
public void setMigrateInvalidatedSessionAttributes(boolean migrateInvalidatedSessionAttributes) {
|
||||
this.migrateInvalidatedSessionAttributes = migrateInvalidatedSessionAttributes;
|
||||
}
|
||||
|
||||
public AuthenticationDetailsSource getAuthenticationDetailsSource() {
|
||||
// Required due to SEC-310
|
||||
return authenticationDetailsSource;
|
||||
}
|
||||
|
||||
protected boolean getAllowSessionCreation() {
|
||||
return allowSessionCreation;
|
||||
}
|
||||
|
||||
public void setAllowSessionCreation(boolean allowSessionCreation) {
|
||||
this.allowSessionCreation = allowSessionCreation;
|
||||
}
|
||||
|
||||
/**
|
||||
* The session registry needs to be set if session fixation attack protection is in use (and concurrent
|
||||
* session control is enabled).
|
||||
*/
|
||||
public void setSessionRegistry(SessionRegistry sessionRegistry) {
|
||||
this.sessionRegistry = sessionRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the strategy used to handle a successful authentication.
|
||||
* By default a {@link SavedRequestAwareAuthenticationSuccessHandler} is used.
|
||||
*/
|
||||
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
|
||||
Assert.notNull(successHandler, "successHandler cannot be null");
|
||||
this.successHandler = successHandler;
|
||||
}
|
||||
|
||||
public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
|
||||
Assert.notNull(failureHandler, "failureHandler cannot be null");
|
||||
this.failureHandler = failureHandler;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui;
|
||||
|
||||
import org.springframework.security.AccessDeniedException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Used by {@link ExceptionTranslationFilter} to handle an
|
||||
* <code>AccessDeniedException</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AccessDeniedHandler {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Handles an access denied failure.
|
||||
*
|
||||
* @param request that resulted in an <code>AccessDeniedException</code>
|
||||
* @param response so that the user agent can be advised of the failure
|
||||
* @param accessDeniedException that caused the invocation
|
||||
*
|
||||
* @throws IOException in the event of an IOException
|
||||
* @throws ServletException in the event of a ServletException
|
||||
*/
|
||||
void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
|
||||
throws IOException, ServletException;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.AccessDeniedException;
|
||||
|
||||
|
||||
/**
|
||||
* Base implementation of {@link AccessDeniedHandler}.
|
||||
* <p>
|
||||
* This implementation sends a 403 (SC_FORBIDDEN) HTTP error code. In addition, if an {@link #errorPage} is defined,
|
||||
* the implementation will perform a request dispatcher "forward" to the specified error page view.
|
||||
* Being a "forward", the <code>SecurityContextHolder</code> will remain
|
||||
* populated. This is of benefit if the view (or a tag library or macro) wishes to access the
|
||||
* <code>SecurityContextHolder</code>. The request scope will also be populated with the exception itself, available
|
||||
* from the key {@link #SPRING_SECURITY_ACCESS_DENIED_EXCEPTION_KEY}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
public static final String SPRING_SECURITY_ACCESS_DENIED_EXCEPTION_KEY = "SPRING_SECURITY_403_EXCEPTION";
|
||||
protected static final Log logger = LogFactory.getLog(AccessDeniedHandlerImpl.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private String errorPage;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
|
||||
throws IOException, ServletException {
|
||||
if (!response.isCommitted()) {
|
||||
if (errorPage != null) {
|
||||
// Put exception into request scope (perhaps of use to a view)
|
||||
request.setAttribute(SPRING_SECURITY_ACCESS_DENIED_EXCEPTION_KEY, accessDeniedException);
|
||||
|
||||
// Set the 403 status code.
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
|
||||
// forward to error page.
|
||||
RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
|
||||
dispatcher.forward(request, response);
|
||||
} else {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error page to use. Must begin with a "/" and is interpreted relative to the current context root.
|
||||
*
|
||||
* @param errorPage the dispatcher path to display
|
||||
*
|
||||
* @throws IllegalArgumentException if the argument doesn't comply with the above limitations
|
||||
*/
|
||||
public void setErrorPage(String errorPage) {
|
||||
if ((errorPage != null) && !errorPage.startsWith("/")) {
|
||||
throw new IllegalArgumentException("errorPage must begin with '/'");
|
||||
}
|
||||
|
||||
this.errorPage = errorPage;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Used by {@link ExceptionTranslationFilter} to commence an authentication scheme.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AuthenticationEntryPoint {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Commences an authentication scheme.
|
||||
* <p>
|
||||
* <code>ExceptionTranslationFilter</code> will populate the <code>HttpSession</code> attribute named
|
||||
* <code>AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY</code> with the requested target URL before
|
||||
* calling this method.
|
||||
* <p>
|
||||
* Implementations should modify the headers on the <code>ServletResponse</code> as necessary to
|
||||
* commence the authentication process.
|
||||
*
|
||||
* @param request that resulted in an <code>AuthenticationException</code>
|
||||
* @param response so that the user agent can begin authentication
|
||||
* @param authException that caused the invocation
|
||||
*
|
||||
*/
|
||||
void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
|
||||
throws IOException, ServletException;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.CredentialsExpiredException;
|
||||
|
||||
/**
|
||||
* Strategy used to handle a failed authentication attempt.
|
||||
* <p>
|
||||
* Typical behaviour might be to redirect the user to the authentication page (in the case of a form login) to
|
||||
* allow them to try again. More sophisticated logic might be implemented depending on the type of the exception.
|
||||
* For example, a {@link CredentialsExpiredException} might cause a redirect to a web controller which allowed the
|
||||
* user to change their password.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public interface AuthenticationFailureHandler {
|
||||
|
||||
/**
|
||||
* Called when an authentication attempt fails.
|
||||
* @param request the request during which the authentication attempt occurred.
|
||||
* @param response the response.
|
||||
* @param exception the exception which was thrown to reject the authentication request.
|
||||
*/
|
||||
void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException, ServletException;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
|
||||
/**
|
||||
* Strategy used to handle a successful user authentication.
|
||||
* <p>
|
||||
* Implementations can do whatever they want but typical behaviour would be to control the navigation to the
|
||||
* subsequent destination (using a redirect or a forward). For example, after a user has logged in by submitting a
|
||||
* login form, the application needs to decide where they should be redirected to afterwards
|
||||
* (see {@link AbstractProcessingFilter} and subclasses). Other logic may also be included if required.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
* @see
|
||||
*/
|
||||
public interface AuthenticationSuccessHandler {
|
||||
|
||||
/**
|
||||
* Called when a user has been successfully authenticated.
|
||||
*
|
||||
* @param request the request which caused the successful authentication
|
||||
* @param response the response
|
||||
* @param authentication the <tt>Authentication</tt> object which was created during the authentication process.
|
||||
*/
|
||||
void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication) throws IOException, ServletException;
|
||||
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.util.RedirectUtils;
|
||||
import org.springframework.security.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Uses the internal map of exceptions types to URLs to determine the destination on authentication failure. The keys
|
||||
* are the full exception class names.
|
||||
* <p>
|
||||
* If a match isn't found, falls back to the behaviour of the parent class,
|
||||
* {@link SimpleUrlAuthenticationFailureHandler}.
|
||||
* <p>
|
||||
* The map of exception names to URLs should be injected by setting the <tt>exceptionMappings</tt> property.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class ExceptionMappingAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||
private Map<String, String> failureUrlMap = new HashMap<String, String>();
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException, ServletException {
|
||||
String url = failureUrlMap.get(exception.getClass().getName());
|
||||
|
||||
if (url != null) {
|
||||
RedirectUtils.sendRedirect(request, response, url, isUseRelativeContext());
|
||||
} else {
|
||||
super.onAuthenticationFailure(request, response, exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the map of exception types (by name) to URLs.
|
||||
*
|
||||
* @param failureUrlMap the map keyed by the fully-qualified name of the exception class, with the corresponding
|
||||
* failure URL as the value.
|
||||
*
|
||||
* @throws IllegalArgumentException if the entries are not Strings or the URL is not valid.
|
||||
*/
|
||||
public void setExceptionMappings(Map<?,?> failureUrlMap) {
|
||||
this.failureUrlMap.clear();
|
||||
for (Map.Entry<?,?> entry : failureUrlMap.entrySet()) {
|
||||
Object exception = entry.getKey();
|
||||
Object url = entry.getValue();
|
||||
Assert.isInstanceOf(String.class, exception, "Exception key must be a String (the exception classname).");
|
||||
Assert.isInstanceOf(String.class, url, "URL must be a String");
|
||||
Assert.isTrue(UrlUtils.isValidRedirectUrl((String)url), "Not a valid redirect URL: " + url);
|
||||
this.failureUrlMap.put((String)exception, (String)url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui;
|
||||
|
||||
import org.springframework.security.AccessDeniedException;
|
||||
import org.springframework.security.SpringSecurityException;
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.AuthenticationTrustResolver;
|
||||
import org.springframework.security.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.InsufficientAuthenticationException;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.ui.savedrequest.SavedRequest;
|
||||
import org.springframework.security.util.PortResolver;
|
||||
import org.springframework.security.util.PortResolverImpl;
|
||||
import org.springframework.security.util.ThrowableAnalyzer;
|
||||
import org.springframework.security.util.ThrowableCauseExtractor;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Handles any <code>AccessDeniedException</code> and <code>AuthenticationException</code> thrown within the
|
||||
* filter chain.
|
||||
* <p>
|
||||
* This filter is necessary because it provides the bridge between Java exceptions and HTTP responses.
|
||||
* It is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.
|
||||
* <p>
|
||||
* If an {@link AuthenticationException} is detected, the filter will launch the <code>authenticationEntryPoint</code>.
|
||||
* This allows common handling of authentication failures originating from any subclass of
|
||||
* {@link org.springframework.security.intercept.AbstractSecurityInterceptor}.
|
||||
* <p>
|
||||
* If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an anonymous
|
||||
* user. If they are an anonymous user, the <code>authenticationEntryPoint</code> will be launched. If they are not
|
||||
* an anonymous user, the filter will delegate to the {@link org.springframework.security.ui.AccessDeniedHandler}.
|
||||
* By default the filter will use {@link org.springframework.security.ui.AccessDeniedHandlerImpl}.
|
||||
* <p>
|
||||
* To use this filter, it is necessary to specify the following properties:
|
||||
* <ul>
|
||||
* <li><code>authenticationEntryPoint</code> indicates the handler that
|
||||
* should commence the authentication process if an
|
||||
* <code>AuthenticationException</code> is detected. Note that this may also
|
||||
* switch the current protocol from http to https for an SSL login.</li>
|
||||
* <li><code>portResolver</code> is used to determine the "real" port that a
|
||||
* request was received on.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author colin sampaleanu
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ExceptionTranslationFilter extends SpringSecurityFilter implements InitializingBean {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
private PortResolver portResolver = new PortResolverImpl();
|
||||
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
|
||||
private boolean createSessionAllowed = true;
|
||||
private boolean justUseSavedRequestOnGet;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint must be specified");
|
||||
Assert.notNull(portResolver, "portResolver must be specified");
|
||||
Assert.notNull(authenticationTrustResolver, "authenticationTrustResolver must be specified");
|
||||
Assert.notNull(throwableAnalyzer, "throwableAnalyzer must be specified");
|
||||
}
|
||||
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
|
||||
try {
|
||||
chain.doFilter(request, response);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Chain processed normally");
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Try to extract a SpringSecurityException from the stacktrace
|
||||
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
|
||||
SpringSecurityException ase = (SpringSecurityException)
|
||||
this.throwableAnalyzer.getFirstThrowableOfType(SpringSecurityException.class, causeChain);
|
||||
|
||||
if (ase != null) {
|
||||
handleException(request, response, chain, ase);
|
||||
}
|
||||
else {
|
||||
// Rethrow ServletExceptions and RuntimeExceptions as-is
|
||||
if (ex instanceof ServletException) {
|
||||
throw (ServletException) ex;
|
||||
}
|
||||
else if (ex instanceof RuntimeException) {
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
|
||||
// Wrap other Exceptions. These are not expected to happen
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationEntryPoint getAuthenticationEntryPoint() {
|
||||
return authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public AuthenticationTrustResolver getAuthenticationTrustResolver() {
|
||||
return authenticationTrustResolver;
|
||||
}
|
||||
|
||||
public PortResolver getPortResolver() {
|
||||
return portResolver;
|
||||
}
|
||||
|
||||
private void handleException(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
|
||||
SpringSecurityException exception) throws IOException, ServletException {
|
||||
if (exception instanceof AuthenticationException) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
|
||||
}
|
||||
|
||||
sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
|
||||
}
|
||||
else if (exception instanceof AccessDeniedException) {
|
||||
if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point",
|
||||
exception);
|
||||
}
|
||||
|
||||
sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException(
|
||||
"Full authentication is required to access this resource"));
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
|
||||
exception);
|
||||
}
|
||||
|
||||
accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If <code>true</code>, indicates that <code>ExceptionTranslationFilter</code> is permitted to store the target
|
||||
* URL and exception information in a new <code>HttpSession</code> (the default).
|
||||
* In situations where you do not wish to unnecessarily create <code>HttpSession</code>s - because the user agent
|
||||
* will know the failed URL, such as with BASIC or Digest authentication - you may wish to set this property to
|
||||
* <code>false</code>.
|
||||
* <p>
|
||||
* Remember to also set
|
||||
* {@link org.springframework.security.context.HttpSessionSecurityContextRepository#setAllowSessionCreation(boolean)}
|
||||
* to <code>false</code> if you set this property to <code>false</code>.
|
||||
*
|
||||
* @return <code>true</code> if the <code>HttpSession</code> will be
|
||||
* used to store information about the failed request, <code>false</code>
|
||||
* if the <code>HttpSession</code> will not be used
|
||||
*/
|
||||
public boolean isCreateSessionAllowed() {
|
||||
return createSessionAllowed;
|
||||
}
|
||||
|
||||
protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
|
||||
AuthenticationException reason) throws ServletException, IOException {
|
||||
// SEC-112: Clear the SecurityContextHolder's Authentication, as the
|
||||
// existing Authentication is no longer considered valid
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
saveRequestIfAllowed(request);
|
||||
logger.debug("Calling Authentication entry point.");
|
||||
authenticationEntryPoint.commence(request, response, reason);
|
||||
}
|
||||
|
||||
private void saveRequestIfAllowed(HttpServletRequest request) {
|
||||
if (!justUseSavedRequestOnGet || "GET".equals(request.getMethod())) {
|
||||
SavedRequest savedRequest = new SavedRequest(request, portResolver);
|
||||
|
||||
if (createSessionAllowed || request.getSession(false) != null) {
|
||||
// Store the HTTP request itself. Used by AbstractProcessingFilter
|
||||
// for redirection after successful authentication (SEC-29)
|
||||
request.getSession().setAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
|
||||
logger.debug("SavedRequest added to Session: " + savedRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
|
||||
Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
|
||||
this.accessDeniedHandler = accessDeniedHandler;
|
||||
}
|
||||
|
||||
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
|
||||
this.authenticationTrustResolver = authenticationTrustResolver;
|
||||
}
|
||||
|
||||
public void setCreateSessionAllowed(boolean createSessionAllowed) {
|
||||
this.createSessionAllowed = createSessionAllowed;
|
||||
}
|
||||
|
||||
public void setPortResolver(PortResolver portResolver) {
|
||||
this.portResolver = portResolver;
|
||||
}
|
||||
|
||||
public void setThrowableAnalyzer(ThrowableAnalyzer throwableAnalyzer) {
|
||||
this.throwableAnalyzer = throwableAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <code>true</code>, will only use <code>SavedRequest</code> to determine the target URL on successful
|
||||
* authentication if the request that caused the authentication request was a GET. Defaults to false.
|
||||
*/
|
||||
public void setJustUseSavedRequestOnGet(boolean justUseSavedRequestOnGet) {
|
||||
this.justUseSavedRequestOnGet = justUseSavedRequestOnGet;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.EXCEPTION_TRANSLATION_FILTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of <code>ThrowableAnalyzer</code> which is capable of also unwrapping
|
||||
* <code>ServletException</code>s.
|
||||
*/
|
||||
private static final class DefaultThrowableAnalyzer extends ThrowableAnalyzer {
|
||||
/**
|
||||
* @see org.springframework.security.util.ThrowableAnalyzer#initExtractorMap()
|
||||
*/
|
||||
protected void initExtractorMap() {
|
||||
super.initExtractorMap();
|
||||
|
||||
registerExtractor(ServletException.class, new ThrowableCauseExtractor() {
|
||||
public Throwable extractCause(Throwable throwable) {
|
||||
ThrowableAnalyzer.verifyThrowableHierarchy(throwable, ServletException.class);
|
||||
return ((ServletException) throwable).getRootCause();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Stores the default order numbers of all Spring Security filters for use in configuration.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class FilterChainOrder {
|
||||
/**
|
||||
* The first position at which a Spring Security filter will be found. Any filter with an order less than this will
|
||||
* be guaranteed to be placed before the Spring Security filters in the stack.
|
||||
*/
|
||||
public static final int FILTER_CHAIN_FIRST = 0;
|
||||
private static final int INTERVAL = 100;
|
||||
private static int i = 1;
|
||||
|
||||
public static final int CHANNEL_FILTER = FILTER_CHAIN_FIRST;
|
||||
public static final int CONCURRENT_SESSION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int SECURITY_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int HTTP_SESSION_CONTEXT_FILTER = SECURITY_CONTEXT_FILTER;
|
||||
public static final int LOGOUT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int X509_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int PRE_AUTH_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int CAS_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int AUTHENTICATION_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int OPENID_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int LOGIN_PAGE_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int DIGEST_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int BASIC_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int SERVLET_API_SUPPORT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int REMEMBER_ME_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int ANONYMOUS_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int EXCEPTION_TRANSLATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int NTLM_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int SESSION_FIXATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int FILTER_SECURITY_INTERCEPTOR = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
public static final int SWITCH_USER_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
|
||||
|
||||
private static final Map<String, Integer> filterNameToOrder = new LinkedHashMap<String, Integer>();
|
||||
|
||||
static {
|
||||
filterNameToOrder.put("FIRST", new Integer(Integer.MIN_VALUE));
|
||||
filterNameToOrder.put("CHANNEL_FILTER", new Integer(CHANNEL_FILTER));
|
||||
filterNameToOrder.put("CONCURRENT_SESSION_FILTER", new Integer(CONCURRENT_SESSION_FILTER));
|
||||
filterNameToOrder.put("LOGOUT_FILTER", new Integer(LOGOUT_FILTER));
|
||||
filterNameToOrder.put("X509_FILTER", new Integer(X509_FILTER));
|
||||
filterNameToOrder.put("PRE_AUTH_FILTER", new Integer(PRE_AUTH_FILTER));
|
||||
filterNameToOrder.put("CAS_PROCESSING_FILTER", new Integer(CAS_PROCESSING_FILTER));
|
||||
filterNameToOrder.put("AUTHENTICATION_PROCESSING_FILTER", new Integer(AUTHENTICATION_PROCESSING_FILTER));
|
||||
filterNameToOrder.put("OPENID_PROCESSING_FILTER", new Integer(OPENID_PROCESSING_FILTER));
|
||||
filterNameToOrder.put("BASIC_PROCESSING_FILTER", new Integer(BASIC_PROCESSING_FILTER));
|
||||
filterNameToOrder.put("SERVLET_API_SUPPORT_FILTER", new Integer(SERVLET_API_SUPPORT_FILTER));
|
||||
filterNameToOrder.put("REMEMBER_ME_FILTER", new Integer(REMEMBER_ME_FILTER));
|
||||
filterNameToOrder.put("ANONYMOUS_FILTER", new Integer(ANONYMOUS_FILTER));
|
||||
filterNameToOrder.put("EXCEPTION_TRANSLATION_FILTER", new Integer(EXCEPTION_TRANSLATION_FILTER));
|
||||
filterNameToOrder.put("NTLM_FILTER", new Integer(NTLM_FILTER));
|
||||
filterNameToOrder.put("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER));
|
||||
filterNameToOrder.put("FILTER_SECURITY_INTERCEPTOR", new Integer(FILTER_SECURITY_INTERCEPTOR));
|
||||
filterNameToOrder.put("SWITCH_USER_FILTER", new Integer(SWITCH_USER_FILTER));
|
||||
filterNameToOrder.put("LAST", new Integer(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
/** Allows filters to be used by name in the XSD file without explicit reference to Java constants */
|
||||
public static int getOrder(String filterName) {
|
||||
Integer order = filterNameToOrder.get(filterName);
|
||||
|
||||
Assert.notNull(order, "Unable to match filter name " + filterName);
|
||||
|
||||
return order.intValue();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.ui.logout.LogoutFilter;
|
||||
|
||||
/**
|
||||
* Strategy that is called after a successful logout by the {@link LogoutFilter}, to handle redirection or
|
||||
* forwarding to the appropriate destination.
|
||||
* <p>
|
||||
* Note that the interface is almost the same as {@link LogoutHandler} but may raise an exception.
|
||||
* <tt>LogoutHandler</tt> implementations expect to be invoked to perform necessary cleanup, so should not throw
|
||||
* exceptions.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public interface LogoutSuccessHandler {
|
||||
|
||||
void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException;
|
||||
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.ui.savedrequest.SavedRequest;
|
||||
import org.springframework.security.util.RedirectUtils;
|
||||
import org.springframework.security.wrapper.SavedRequestAwareWrapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An authentication success strategy which can make use of the {@link SavedRequest} which may have been stored in
|
||||
* the session by the {@link ExceptionTranslationFilter}. When such a request is intercepted and requires authentication,
|
||||
* the request data is stored to record the original destination before the authentication process commenced, and to
|
||||
* allow the request to be reconstructed when a redirect to the same URL occurs. This class is responsible for
|
||||
* performing the redirect to the original URL if appropriate.
|
||||
* <p>
|
||||
* Following a successful authentication, it decides on the redirect destination, based on the following scenarios:
|
||||
* <ul>
|
||||
* <li>
|
||||
* If the <tt>alwaysUseDefaultTargetUrl</tt> property is set to true, the <tt>defaultTargetUrl</tt>
|
||||
* will be used for the destination. Any <tt>SavedRequest</tt> stored in the session will be
|
||||
* removed.
|
||||
* </li>
|
||||
* <li>
|
||||
* If the <tt>targetUrlParameter</tt> has been set on the request, the value will be used as the destination.
|
||||
* Any <tt>SavedRequest</tt> will again be removed.
|
||||
* </li>
|
||||
* <li>
|
||||
* If a {@link SavedRequest} is found in the session (as set by the {@link ExceptionTranslationFilter} to record
|
||||
* the original destination before the authentication process commenced), a redirect will be performed to the
|
||||
* Url of that original destination. The <tt>SavedRequest</tt> object will remain in the session and be picked up
|
||||
* when the redirected request is received (See {@link SavedRequestAwareWrapper}).
|
||||
* </li>
|
||||
* <li>
|
||||
* If no <tt>SavedRequest</tt> is found in the session, it will delegate to the base class.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication) throws ServletException, IOException {
|
||||
SavedRequest savedRequest = getSavedRequest(request);
|
||||
|
||||
if (savedRequest == null) {
|
||||
super.onAuthenticationSuccess(request, response, authentication);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAlwaysUseDefaultTargetUrl() || StringUtils.hasText(request.getParameter(getTargetUrlParameter()))) {
|
||||
removeSavedRequest(request);
|
||||
super.onAuthenticationSuccess(request, response, authentication);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the SavedRequest URL
|
||||
String targetUrl = savedRequest.getFullRequestUrl();
|
||||
logger.debug("Redirecting to SavedRequest Url: " + targetUrl);
|
||||
RedirectUtils.sendRedirect(request, response, targetUrl, isUseRelativeContext());
|
||||
}
|
||||
|
||||
private SavedRequest getSavedRequest(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
|
||||
if (session != null) {
|
||||
return (SavedRequest) session.getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void removeSavedRequest(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
|
||||
if (session != null) {
|
||||
logger.debug("Removing SavedRequest from session if present");
|
||||
session.removeAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationTrustResolver;
|
||||
import org.springframework.security.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.concurrent.SessionRegistry;
|
||||
import org.springframework.security.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.context.SecurityContext;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.util.SessionUtils;
|
||||
|
||||
/**
|
||||
* Detects that a user has been authenticated since the start of the request and starts a new session.
|
||||
* <p>
|
||||
* This is essentially a generalization of the functionality that was implemented for SEC-399.
|
||||
* Additionally, it will update the configured SessionRegistry if one is in use, thus preventing problems when used
|
||||
* with Spring Security's concurrent session control.
|
||||
*
|
||||
* @author Martin Algesten
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SessionFixationProtectionFilter extends SpringSecurityFilter {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
static final String FILTER_APPLIED = "__spring_security_session_fixation_filter_applied";
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private SessionRegistry sessionRegistry;
|
||||
|
||||
/**
|
||||
* Indicates that the session attributes of the session to be invalidated
|
||||
* should be migrated to the new session. Defaults to <code>true</code>.
|
||||
*/
|
||||
private boolean migrateSessionAttributes = true;
|
||||
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
protected void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
// Session fixation isn't a problem if there's no session
|
||||
if(request.getSession(false) == null || request.getAttribute(FILTER_APPLIED) != null) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
||||
|
||||
HttpSession session = request.getSession();
|
||||
SecurityContext sessionSecurityContext =
|
||||
(SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||
|
||||
if (sessionSecurityContext == null && isAuthenticated()) {
|
||||
// The user has been authenticated during the current request, so do the session migration
|
||||
startNewSessionIfRequired(request, response);
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private boolean isAuthenticated() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
return authentication != null && !authenticationTrustResolver.isAnonymous(authentication);
|
||||
}
|
||||
|
||||
public void setMigrateSessionAttributes(boolean migrateSessionAttributes) {
|
||||
this.migrateSessionAttributes = migrateSessionAttributes;
|
||||
}
|
||||
|
||||
public void setSessionRegistry(SessionRegistry sessionRegistry) {
|
||||
this.sessionRegistry = sessionRegistry;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.SESSION_FIXATION_FILTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the a user wasn't authenticated at the start of the request but has been during it
|
||||
* <p>
|
||||
* A new session will be created, the session attributes copied to it (if
|
||||
* <tt>migrateSessionAttributes</tt> is set) and the sessionRegistry updated with the new session information.
|
||||
*/
|
||||
protected void startNewSessionIfRequired(HttpServletRequest request, HttpServletResponse response) {
|
||||
SessionUtils.startNewSessionIfRequired(request, migrateSessionAttributes, sessionRegistry);
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.util.RedirectUtils;
|
||||
import org.springframework.security.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <tt>AuthenticationFailureHandler</tt> which performs a redirect to the value of the {@link #setDefaultFailureUrl
|
||||
* defaultFailureUrl} property when the <tt>onAuthenticationFailure</tt> method is called.
|
||||
* If the property has not been set it will send a 401 response to the client, with the error message from the
|
||||
* <tt>AuthenticationException</tt> which caused the failure.
|
||||
* <p>
|
||||
* If the <tt>forwardToDestination</tt> parameter is set, a <tt>RequestDispatcher.forward</tt> call will be made to
|
||||
* the destination instead of a redirect.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {
|
||||
private String defaultFailureUrl;
|
||||
private boolean forwardToDestination = false;
|
||||
private boolean useRelativeContext = false;
|
||||
|
||||
public SimpleUrlAuthenticationFailureHandler() {
|
||||
}
|
||||
|
||||
public SimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) {
|
||||
setDefaultFailureUrl(defaultFailureUrl);
|
||||
}
|
||||
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException, ServletException {
|
||||
if (defaultFailureUrl == null) {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed: " + exception.getMessage());
|
||||
} else {
|
||||
if (forwardToDestination) {
|
||||
request.getRequestDispatcher(defaultFailureUrl).forward(request, response);
|
||||
} else {
|
||||
RedirectUtils.sendRedirect(request, response, defaultFailureUrl, useRelativeContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL which will be used as the failure destination.
|
||||
*
|
||||
* @param defaultFailureUrl the failure URL, for example "/loginFailed.jsp".
|
||||
*/
|
||||
public void setDefaultFailureUrl(String defaultFailureUrl) {
|
||||
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl));
|
||||
this.defaultFailureUrl = defaultFailureUrl;
|
||||
}
|
||||
|
||||
protected boolean isUseForward() {
|
||||
return forwardToDestination;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <tt>true</tt>, performs a forward to the failure destination URL instead of a redirect. Defaults to
|
||||
* <tt>false</tt>.
|
||||
*/
|
||||
public void setUseForward(boolean forwardToDestination) {
|
||||
this.forwardToDestination = forwardToDestination;
|
||||
}
|
||||
|
||||
protected boolean isUseRelativeContext() {
|
||||
return useRelativeContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, causes any redirection URLs to be calculated minus the protocol
|
||||
* and context path (defaults to false).
|
||||
*/
|
||||
public void setUseRelativeContext(boolean useRelativeContext) {
|
||||
this.useRelativeContext = useRelativeContext;
|
||||
}
|
||||
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
|
||||
public class SimpleUrlAuthenticationSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
public SimpleUrlAuthenticationSuccessHandler() {
|
||||
}
|
||||
|
||||
public SimpleUrlAuthenticationSuccessHandler(String defaultTargetUrl) {
|
||||
setDefaultTargetUrl(defaultTargetUrl);
|
||||
}
|
||||
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication) throws IOException, ServletException {
|
||||
|
||||
handle(request, response, authentication);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.springframework.security.ui;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.Filter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Implements Ordered interface as required by security namespace configuration and implements unused filter
|
||||
* lifecycle methods and performs casting of request and response to http versions in doFilter method.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class SpringSecurityFilter implements Filter, Ordered {
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*
|
||||
* @param filterConfig ignored
|
||||
* @throws ServletException ignored
|
||||
*/
|
||||
public final void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. We use IoC container lifecycle services instead.
|
||||
*/
|
||||
public final void destroy() {
|
||||
}
|
||||
|
||||
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
doFilterHttp((HttpServletRequest)request, (HttpServletResponse)response, chain);
|
||||
}
|
||||
|
||||
protected abstract void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException;
|
||||
|
||||
public String toString() {
|
||||
return getClass().getName() + "[ order=" + getOrder() + "; ]";
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui;
|
||||
|
||||
import org.springframework.security.concurrent.SessionIdentifierAware;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
|
||||
/**
|
||||
* A holder of selected HTTP details related to a web authentication request.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class WebAuthenticationDetails implements SessionIdentifierAware, Serializable {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private String remoteAddress;
|
||||
private String sessionId;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Records the remote address and will also set the session Id if a session
|
||||
* already exists (it won't create one).
|
||||
*
|
||||
* @param request that the authentication request was received from
|
||||
*/
|
||||
public WebAuthenticationDetails(HttpServletRequest request) {
|
||||
this.remoteAddress = request.getRemoteAddr();
|
||||
|
||||
HttpSession session = request.getSession(false);
|
||||
this.sessionId = (session != null) ? session.getId() : null;
|
||||
|
||||
doPopulateAdditionalInformation(request);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Provided so that subclasses can populate additional information.
|
||||
*
|
||||
* @param request that the authentication request was received from
|
||||
*/
|
||||
protected void doPopulateAdditionalInformation(HttpServletRequest request) {}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof WebAuthenticationDetails) {
|
||||
WebAuthenticationDetails rhs = (WebAuthenticationDetails) obj;
|
||||
|
||||
if ((remoteAddress == null) && (rhs.getRemoteAddress() != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((remoteAddress != null) && (rhs.getRemoteAddress() == null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remoteAddress != null) {
|
||||
if (!remoteAddress.equals(rhs.getRemoteAddress())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((sessionId == null) && (rhs.getSessionId() != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((sessionId != null) && (rhs.getSessionId() == null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sessionId != null) {
|
||||
if (!sessionId.equals(rhs.getSessionId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the TCP/IP address the authentication request was received from.
|
||||
*
|
||||
* @return the address
|
||||
*/
|
||||
public String getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the <code>HttpSession</code> id the authentication request was received from.
|
||||
*
|
||||
* @return the session ID
|
||||
*/
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int code = 7654;
|
||||
|
||||
if (this.remoteAddress != null) {
|
||||
code = code * (this.remoteAddress.hashCode() % 7);
|
||||
}
|
||||
|
||||
if (this.sessionId != null) {
|
||||
code = code * (this.sessionId.hashCode() % 7);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(super.toString() + ": ");
|
||||
sb.append("RemoteIpAddress: " + this.getRemoteAddress() + "; ");
|
||||
sb.append("SessionId: " + this.getSessionId());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link AuthenticationDetailsSource} which builds the details object from
|
||||
* an <tt>HttpServletRequest</tt> object.
|
||||
* <p>
|
||||
* By default will create an instance of <code>WebAuthenticationDetails</code>. Any object that accepts a
|
||||
* <code>HttpServletRequest</code> as its sole constructor can be used instead of this default.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class WebAuthenticationDetailsSource implements AuthenticationDetailsSource {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private Class<?> clazz = WebAuthenticationDetails.class;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* @param context the <tt>HttpServletRequest</tt> object.
|
||||
*/
|
||||
public Object buildDetails(Object context) {
|
||||
Assert.isInstanceOf(HttpServletRequest.class, context);
|
||||
try {
|
||||
Constructor<?> constructor = clazz.getConstructor(HttpServletRequest.class);
|
||||
|
||||
return constructor.newInstance(context);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
} catch (InvocationTargetException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
} catch (InstantiationException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setClazz(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class required");
|
||||
this.clazz = clazz;
|
||||
}
|
||||
}
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui.basicauth;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.AuthenticationManager;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.AuthenticationEntryPoint;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
import org.springframework.security.ui.rememberme.NullRememberMeServices;
|
||||
import org.springframework.security.ui.rememberme.RememberMeServices;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Processes a HTTP request's BASIC authorization headers, putting the result into the
|
||||
* <code>SecurityContextHolder</code>.
|
||||
*
|
||||
* <p>
|
||||
* For a detailed background on what this filter is designed to process, refer to
|
||||
* <a href="http://www.faqs.org/rfcs/rfc1945.html">RFC 1945, Section 11.1</a>. Any realm name presented in
|
||||
* the HTTP request is ignored.
|
||||
*
|
||||
* <p>
|
||||
* In summary, this filter is responsible for processing any request that has a HTTP request header of
|
||||
* <code>Authorization</code> with an authentication scheme of <code>Basic</code> and a Base64-encoded
|
||||
* <code>username:password</code> token. For example, to authenticate user "Aladdin" with password "open sesame" the
|
||||
* following header would be presented:
|
||||
* <pre>
|
||||
*
|
||||
* Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This filter can be used to provide BASIC authentication services to both remoting protocol clients (such as
|
||||
* Hessian and SOAP) as well as standard user agents (such as Internet Explorer and Netscape).
|
||||
* <p>
|
||||
* If authentication is successful, the resulting {@link Authentication} object will be placed into the
|
||||
* <code>SecurityContextHolder</code>.
|
||||
*
|
||||
* <p>
|
||||
* If authentication fails and <code>ignoreFailure</code> is <code>false</code> (the default), an {@link
|
||||
* AuthenticationEntryPoint} implementation is called (unless the <tt>ignoreFailure</tt> property is set to
|
||||
* <tt>true</tt>). Usually this should be {@link BasicProcessingFilterEntryPoint}, which will prompt the user to
|
||||
* authenticate again via BASIC authentication.
|
||||
*
|
||||
* <p>
|
||||
* Basic authentication is an attractive protocol because it is simple and widely deployed. However, it still
|
||||
* transmits a password in clear text and as such is undesirable in many situations. Digest authentication is also
|
||||
* provided by Spring Security and should be used instead of Basic authentication wherever possible. See {@link
|
||||
* org.springframework.security.ui.digestauth.DigestProcessingFilter}.
|
||||
* <p>
|
||||
* Note that if a {@link RememberMeServices} is set, this filter will automatically send back remember-me
|
||||
* details to the client. Therefore, subsequent requests will not need to present a BASIC authentication header as
|
||||
* they will be authenticated using the remember-me mechanism.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BasicProcessingFilter extends SpringSecurityFilter implements InitializingBean {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
private AuthenticationManager authenticationManager;
|
||||
private RememberMeServices rememberMeServices = new NullRememberMeServices();
|
||||
private boolean ignoreFailure = false;
|
||||
private String credentialsCharset = "UTF-8";
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.authenticationManager, "An AuthenticationManager is required");
|
||||
|
||||
if(!isIgnoreFailure()) {
|
||||
Assert.notNull(this.authenticationEntryPoint, "An AuthenticationEntryPoint is required");
|
||||
}
|
||||
}
|
||||
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
String header = request.getHeader("Authorization");
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authorization header: " + header);
|
||||
}
|
||||
|
||||
if ((header != null) && header.startsWith("Basic ")) {
|
||||
byte[] base64Token = header.substring(6).getBytes("UTF-8");
|
||||
String token = new String(Base64.decodeBase64(base64Token), getCredentialsCharset(request));
|
||||
|
||||
String username = "";
|
||||
String password = "";
|
||||
int delim = token.indexOf(":");
|
||||
|
||||
if (delim != -1) {
|
||||
username = token.substring(0, delim);
|
||||
password = token.substring(delim + 1);
|
||||
}
|
||||
|
||||
if (authenticationIsRequired(username)) {
|
||||
UsernamePasswordAuthenticationToken authRequest =
|
||||
new UsernamePasswordAuthenticationToken(username, password);
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
|
||||
|
||||
Authentication authResult;
|
||||
|
||||
try {
|
||||
authResult = authenticationManager.authenticate(authRequest);
|
||||
} catch (AuthenticationException failed) {
|
||||
// Authentication failed
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for user: " + username + " failed: " + failed.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
rememberMeServices.loginFail(request, response);
|
||||
|
||||
onUnsuccessfulAuthentication(request, response, failed);
|
||||
|
||||
if (ignoreFailure) {
|
||||
chain.doFilter(request, response);
|
||||
} else {
|
||||
authenticationEntryPoint.commence(request, response, failed);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Authentication success
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success: " + authResult.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
|
||||
rememberMeServices.loginSuccess(request, response, authResult);
|
||||
|
||||
onSuccessfulAuthentication(request, response, authResult);
|
||||
}
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private boolean authenticationIsRequired(String username) {
|
||||
// Only reauthenticate if username doesn't match SecurityContextHolder and user isn't authenticated
|
||||
// (see SEC-53)
|
||||
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if(existingAuth == null || !existingAuth.isAuthenticated()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Limit username comparison to providers which use usernames (ie UsernamePasswordAuthenticationToken)
|
||||
// (see SEC-348)
|
||||
|
||||
if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle unusual condition where an AnonymousAuthenticationToken is already present
|
||||
// This shouldn't happen very often, as BasicProcessingFitler is meant to be earlier in the filter
|
||||
// chain than AnonymousProcessingFilter. Nevertheless, presence of both an AnonymousAuthenticationToken
|
||||
// together with a BASIC authentication request header should indicate reauthentication using the
|
||||
// BASIC protocol is desirable. This behaviour is also consistent with that provided by form and digest,
|
||||
// both of which force re-authentication if the respective header is detected (and in doing so replace
|
||||
// any existing AnonymousAuthenticationToken). See SEC-610.
|
||||
if (existingAuth instanceof AnonymousAuthenticationToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authResult) throws IOException {
|
||||
}
|
||||
|
||||
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException failed) throws IOException {
|
||||
}
|
||||
|
||||
protected AuthenticationEntryPoint getAuthenticationEntryPoint() {
|
||||
return authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
}
|
||||
|
||||
protected AuthenticationManager getAuthenticationManager() {
|
||||
return authenticationManager;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
protected boolean isIgnoreFailure() {
|
||||
return ignoreFailure;
|
||||
}
|
||||
|
||||
public void setIgnoreFailure(boolean ignoreFailure) {
|
||||
this.ignoreFailure = ignoreFailure;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setRememberMeServices(RememberMeServices rememberMeServices) {
|
||||
Assert.notNull(rememberMeServices, "rememberMeServices cannot be null");
|
||||
this.rememberMeServices = rememberMeServices;
|
||||
}
|
||||
|
||||
public void setCredentialsCharset(String credentialsCharset) {
|
||||
Assert.hasText(credentialsCharset, "credentialsCharset cannot be null or empty");
|
||||
this.credentialsCharset = credentialsCharset;
|
||||
}
|
||||
|
||||
protected String getCredentialsCharset(HttpServletRequest httpRequest) {
|
||||
return credentialsCharset;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.BASIC_PROCESSING_FILTER;
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui.basicauth;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.ui.AuthenticationEntryPoint;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the {@link
|
||||
* BasicProcessingFilter}.<P>Once a user agent is authenticated using BASIC authentication, logout requires that
|
||||
* the browser be closed or an unauthorized (401) header be sent. The simplest way of achieving the latter is to call
|
||||
* the {@link #commence(HttpServletRequest, HttpServletResponse, AuthenticationException)} method below. This will indicate to
|
||||
* the browser its credentials are no longer authorized, causing it to prompt the user to login again.</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BasicProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private String realmName;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasText(realmName, "realmName must be specified");
|
||||
}
|
||||
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
|
||||
throws IOException, ServletException {
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
httpResponse.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
|
||||
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
|
||||
}
|
||||
|
||||
public String getRealmName() {
|
||||
return realmName;
|
||||
}
|
||||
|
||||
public void setRealmName(String realmName) {
|
||||
this.realmName = realmName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Authenticates HTTP BASIC authentication requests.
|
||||
</body>
|
||||
</html>
|
||||
-419
@@ -1,419 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui.digestauth;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.AuthenticationServiceException;
|
||||
import org.springframework.security.BadCredentialsException;
|
||||
import org.springframework.security.SpringSecurityMessageSource;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.providers.dao.UserCache;
|
||||
import org.springframework.security.providers.dao.cache.NullUserCache;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
import org.springframework.security.ui.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.userdetails.UserDetailsService;
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.util.StringSplitUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Processes a HTTP request's Digest authorization headers, putting the result into the
|
||||
* <code>SecurityContextHolder</code>.
|
||||
* <p>
|
||||
* For a detailed background on what this filter is designed to process, refer to
|
||||
* <a href="http://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a> (which superseded RFC 2069, although this
|
||||
* filter support clients that implement either RFC 2617 or RFC 2069).
|
||||
* <p>
|
||||
* This filter can be used to provide Digest authentication services to both remoting protocol clients (such as
|
||||
* Hessian and SOAP) as well as standard user agents (such as Internet Explorer and FireFox).
|
||||
* <p>
|
||||
* This Digest implementation has been designed to avoid needing to store session state between invocations.
|
||||
* All session management information is stored in the "nonce" that is sent to the client by the {@link
|
||||
* DigestProcessingFilterEntryPoint}.
|
||||
* <p>
|
||||
* If authentication is successful, the resulting {@link org.springframework.security.Authentication Authentication}
|
||||
* object will be placed into the <code>SecurityContextHolder</code>.
|
||||
* <p>
|
||||
* If authentication fails, an {@link org.springframework.security.ui.AuthenticationEntryPoint AuthenticationEntryPoint}
|
||||
* implementation is called. This must always be {@link DigestProcessingFilterEntryPoint}, which will prompt the user
|
||||
* to authenticate again via Digest authentication.
|
||||
* <p>
|
||||
* Note there are limitations to Digest authentication, although it is a more comprehensive and secure solution
|
||||
* than Basic authentication. Please see RFC 2617 section 4 for a full discussion on the advantages of Digest
|
||||
* authentication over Basic authentication, including commentary on the limitations that it still imposes.
|
||||
*/
|
||||
public class DigestProcessingFilter extends SpringSecurityFilter implements Filter, InitializingBean, MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DigestProcessingFilter.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
||||
private DigestProcessingFilterEntryPoint authenticationEntryPoint;
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
private UserCache userCache = new NullUserCache();
|
||||
private UserDetailsService userDetailsService;
|
||||
private boolean passwordAlreadyEncoded = false;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(userDetailsService, "A UserDetailsService is required");
|
||||
Assert.notNull(authenticationEntryPoint, "A DigestProcessingFilterEntryPoint is required");
|
||||
}
|
||||
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
String header = request.getHeader("Authorization");
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authorization header received from user agent: " + header);
|
||||
}
|
||||
|
||||
if ((header != null) && header.startsWith("Digest ")) {
|
||||
String section212response = header.substring(7);
|
||||
|
||||
String[] headerEntries = StringSplitUtils.splitIgnoringQuotes(section212response, ',');
|
||||
Map<String,String> headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
|
||||
|
||||
String username = (String) headerMap.get("username");
|
||||
String realm = (String) headerMap.get("realm");
|
||||
String nonce = (String) headerMap.get("nonce");
|
||||
String uri = (String) headerMap.get("uri");
|
||||
String responseDigest = (String) headerMap.get("response");
|
||||
String qop = (String) headerMap.get("qop"); // RFC 2617 extension
|
||||
String nc = (String) headerMap.get("nc"); // RFC 2617 extension
|
||||
String cnonce = (String) headerMap.get("cnonce"); // RFC 2617 extension
|
||||
|
||||
// Check all required parameters were supplied (ie RFC 2069)
|
||||
if ((username == null) || (realm == null) || (nonce == null) || (uri == null) || (response == null)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("extracted username: '" + username + "'; realm: '" + username + "'; nonce: '"
|
||||
+ username + "'; uri: '" + username + "'; response: '" + username + "'");
|
||||
}
|
||||
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingMandatory",
|
||||
new Object[]{section212response}, "Missing mandatory digest value; received header {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check all required parameters for an "auth" qop were supplied (ie RFC 2617)
|
||||
if ("auth".equals(qop)) {
|
||||
if ((nc == null) || (cnonce == null)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("extracted nc: '" + nc + "'; cnonce: '" + cnonce + "'");
|
||||
}
|
||||
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingAuth",
|
||||
new Object[]{section212response}, "Missing mandatory digest value; received header {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check realm name equals what we expected
|
||||
if (!this.getAuthenticationEntryPoint().getRealmName().equals(realm)) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectRealm",
|
||||
new Object[]{realm, this.getAuthenticationEntryPoint().getRealmName()},
|
||||
"Response realm name '{0}' does not match system realm name of '{1}'")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check nonce was a Base64 encoded (as sent by DigestProcessingFilterEntryPoint)
|
||||
if (!Base64.isArrayByteBase64(nonce.getBytes())) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceEncoding",
|
||||
new Object[]{nonce}, "Nonce is not encoded in Base64; received nonce {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode nonce from Base64
|
||||
// format of nonce is:
|
||||
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
|
||||
String nonceAsPlainText = new String(Base64.decodeBase64(nonce.getBytes()));
|
||||
String[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, ":");
|
||||
|
||||
if (nonceTokens.length != 2) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotTwoTokens",
|
||||
new Object[]{nonceAsPlainText}, "Nonce should have yielded two tokens but was {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract expiry time from nonce
|
||||
long nonceExpiryTime;
|
||||
|
||||
try {
|
||||
nonceExpiryTime = new Long(nonceTokens[0]).longValue();
|
||||
} catch (NumberFormatException nfe) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotNumeric",
|
||||
new Object[]{nonceAsPlainText},
|
||||
"Nonce token should have yielded a numeric first token, but was {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check signature of nonce matches this expiry time
|
||||
String expectedNonceSignature = DigestUtils.md5Hex(nonceExpiryTime + ":"
|
||||
+ this.getAuthenticationEntryPoint().getKey());
|
||||
|
||||
if (!expectedNonceSignature.equals(nonceTokens[1])) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceCompromised",
|
||||
new Object[]{nonceAsPlainText}, "Nonce token compromised {0}")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Lookup password for presented username
|
||||
// NB: DAO-provided password MUST be clear text - not encoded/salted
|
||||
// (unless this instance's passwordAlreadyEncoded property is 'false')
|
||||
boolean loadedFromDao = false;
|
||||
UserDetails user = userCache.getUserFromCache(username);
|
||||
|
||||
if (user == null) {
|
||||
loadedFromDao = true;
|
||||
|
||||
try {
|
||||
user = userDetailsService.loadUserByUsername(username);
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
|
||||
new Object[]{username}, "Username {0} not found")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new AuthenticationServiceException(
|
||||
"AuthenticationDao returned null, which is an interface contract violation");
|
||||
}
|
||||
|
||||
userCache.putUserInCache(user);
|
||||
}
|
||||
|
||||
// Compute the expected response-digest (will be in hex form)
|
||||
String serverDigestMd5;
|
||||
|
||||
// Don't catch IllegalArgumentException (already checked validity)
|
||||
serverDigestMd5 = generateDigest(passwordAlreadyEncoded, username, realm, user.getPassword(),
|
||||
((HttpServletRequest) request).getMethod(), uri, qop, nonce, nc, cnonce);
|
||||
|
||||
// If digest is incorrect, try refreshing from backend and recomputing
|
||||
if (!serverDigestMd5.equals(responseDigest) && !loadedFromDao) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Digest comparison failure; trying to refresh user from DAO in case password had changed");
|
||||
}
|
||||
|
||||
try {
|
||||
user = userDetailsService.loadUserByUsername(username);
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
// Would very rarely happen, as user existed earlier
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.usernameNotFound",
|
||||
new Object[]{username}, "Username {0} not found")));
|
||||
}
|
||||
|
||||
userCache.putUserInCache(user);
|
||||
|
||||
// Don't catch IllegalArgumentException (already checked validity)
|
||||
serverDigestMd5 = generateDigest(passwordAlreadyEncoded, username, realm, user.getPassword(),
|
||||
((HttpServletRequest) request).getMethod(), uri, qop, nonce, nc, cnonce);
|
||||
}
|
||||
|
||||
// If digest is still incorrect, definitely reject authentication attempt
|
||||
if (!serverDigestMd5.equals(responseDigest)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Expected response: '" + serverDigestMd5 + "' but received: '" + responseDigest
|
||||
+ "'; is AuthenticationDao returning clear text passwords?");
|
||||
}
|
||||
|
||||
fail(request, response,
|
||||
new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectResponse",
|
||||
"Incorrect response")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// To get this far, the digest must have been valid
|
||||
// Check the nonce has not expired
|
||||
// We do this last so we can direct the user agent its nonce is stale
|
||||
// but the request was otherwise appearing to be valid
|
||||
if (nonceExpiryTime < System.currentTimeMillis()) {
|
||||
fail(request, response,
|
||||
new NonceExpiredException(messages.getMessage("DigestProcessingFilter.nonceExpired",
|
||||
"Nonce has expired/timed out")));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success for user: '" + username + "' with response: '" + responseDigest
|
||||
+ "'");
|
||||
}
|
||||
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user,
|
||||
user.getPassword());
|
||||
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authRequest);
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
public static String encodePasswordInA1Format(String username, String realm, String password) {
|
||||
String a1 = username + ":" + realm + ":" + password;
|
||||
String a1Md5 = new String(DigestUtils.md5Hex(a1));
|
||||
|
||||
return a1Md5;
|
||||
}
|
||||
|
||||
private void fail(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
|
||||
throws IOException, ServletException {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(failed);
|
||||
}
|
||||
|
||||
authenticationEntryPoint.commence(request, response, failed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the <code>response</code> portion of a Digest authentication header. Both the server and user
|
||||
* agent should compute the <code>response</code> independently. Provided as a static method to simplify the
|
||||
* coding of user agents.
|
||||
*
|
||||
* @param passwordAlreadyEncoded true if the password argument is already encoded in the correct format. False if
|
||||
* it is plain text.
|
||||
* @param username the user's login name.
|
||||
* @param realm the name of the realm.
|
||||
* @param password the user's password in plaintext or ready-encoded.
|
||||
* @param httpMethod the HTTP request method (GET, POST etc.)
|
||||
* @param uri the request URI.
|
||||
* @param qop the qop directive, or null if not set.
|
||||
* @param nonce the nonce supplied by the server
|
||||
* @param nc the "nonce-count" as defined in RFC 2617.
|
||||
* @param cnonce opaque string supplied by the client when qop is set.
|
||||
* @return the MD5 of the digest authentication response, encoded in hex
|
||||
* @throws IllegalArgumentException if the supplied qop value is unsupported.
|
||||
*/
|
||||
public static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
|
||||
String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
|
||||
throws IllegalArgumentException {
|
||||
String a1Md5 = null;
|
||||
String a2 = httpMethod + ":" + uri;
|
||||
String a2Md5 = new String(DigestUtils.md5Hex(a2));
|
||||
|
||||
if (passwordAlreadyEncoded) {
|
||||
a1Md5 = password;
|
||||
} else {
|
||||
a1Md5 = encodePasswordInA1Format(username, realm, password);
|
||||
}
|
||||
|
||||
String digest;
|
||||
|
||||
if (qop == null) {
|
||||
// as per RFC 2069 compliant clients (also reaffirmed by RFC 2617)
|
||||
digest = a1Md5 + ":" + nonce + ":" + a2Md5;
|
||||
} else if ("auth".equals(qop)) {
|
||||
// As per RFC 2617 compliant clients
|
||||
digest = a1Md5 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + a2Md5;
|
||||
} else {
|
||||
throw new IllegalArgumentException("This method does not support a qop: '" + qop + "'");
|
||||
}
|
||||
|
||||
String digestMd5 = new String(DigestUtils.md5Hex(digest));
|
||||
|
||||
return digestMd5;
|
||||
}
|
||||
|
||||
public DigestProcessingFilterEntryPoint getAuthenticationEntryPoint() {
|
||||
return authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public UserCache getUserCache() {
|
||||
return userCache;
|
||||
}
|
||||
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setAuthenticationEntryPoint(DigestProcessingFilterEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
public void setPasswordAlreadyEncoded(boolean passwordAlreadyEncoded) {
|
||||
this.passwordAlreadyEncoded = passwordAlreadyEncoded;
|
||||
}
|
||||
|
||||
public void setUserCache(UserCache userCache) {
|
||||
this.userCache = userCache;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.DIGEST_PROCESSING_FILTER;
|
||||
}
|
||||
}
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui.digestauth;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.ui.AuthenticationEntryPoint;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
|
||||
/**
|
||||
* Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the {@link
|
||||
* DigestProcessingFilter}.<p>The nonce sent back to the user agent will be valid for the period indicated by
|
||||
* {@link #setNonceValiditySeconds(int)}. By default this is 300 seconds. Shorter times should be used if replay
|
||||
* attacks are a major concern. Larger values can be used if performance is a greater concern. This class correctly
|
||||
* presents the <code>stale=true</code> header when the nonce has expierd, so properly implemented user agents will
|
||||
* automatically renegotiate with a new nonce value (ie without presenting a new password dialog box to the user).</p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class DigestProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DigestProcessingFilterEntryPoint.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private String key;
|
||||
private String realmName;
|
||||
private int nonceValiditySeconds = 300;
|
||||
private int order = Integer.MAX_VALUE; // ~ default
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if ((realmName == null) || "".equals(realmName)) {
|
||||
throw new IllegalArgumentException("realmName must be specified");
|
||||
}
|
||||
|
||||
if ((key == null) || "".equals(key)) {
|
||||
throw new IllegalArgumentException("key must be specified");
|
||||
}
|
||||
}
|
||||
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
|
||||
throws IOException, ServletException {
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
// compute a nonce (do not use remote IP address due to proxy farms)
|
||||
// format of nonce is:
|
||||
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
|
||||
long expiryTime = System.currentTimeMillis() + (nonceValiditySeconds * 1000);
|
||||
String signatureValue = new String(DigestUtils.md5Hex(expiryTime + ":" + key));
|
||||
String nonceValue = expiryTime + ":" + signatureValue;
|
||||
String nonceValueBase64 = new String(Base64.encodeBase64(nonceValue.getBytes()));
|
||||
|
||||
// qop is quality of protection, as defined by RFC 2617.
|
||||
// we do not use opaque due to IE violation of RFC 2617 in not
|
||||
// representing opaque on subsequent requests in same session.
|
||||
String authenticateHeader = "Digest realm=\"" + realmName + "\", " + "qop=\"auth\", nonce=\""
|
||||
+ nonceValueBase64 + "\"";
|
||||
|
||||
if (authException instanceof NonceExpiredException) {
|
||||
authenticateHeader = authenticateHeader + ", stale=\"true\"";
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("WWW-Authenticate header sent to user agent: " + authenticateHeader);
|
||||
}
|
||||
|
||||
httpResponse.addHeader("WWW-Authenticate", authenticateHeader);
|
||||
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public int getNonceValiditySeconds() {
|
||||
return nonceValiditySeconds;
|
||||
}
|
||||
|
||||
public String getRealmName() {
|
||||
return realmName;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setNonceValiditySeconds(int nonceValiditySeconds) {
|
||||
this.nonceValiditySeconds = nonceValiditySeconds;
|
||||
}
|
||||
|
||||
public void setRealmName(String realmName) {
|
||||
this.realmName = realmName;
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui.digestauth;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown if an authentication request is rejected because the digest nonce has expired.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class NonceExpiredException extends AuthenticationException {
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Constructs a <code>NonceExpiredException</code> with the specified
|
||||
* message.
|
||||
*
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public NonceExpiredException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>NonceExpiredException</code> with the specified
|
||||
* message and root cause.
|
||||
*
|
||||
* @param msg the detail message
|
||||
* @param t root cause
|
||||
*/
|
||||
public NonceExpiredException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Authenticates HTTP Digest authentication requests.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,150 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui.logout;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.LogoutSuccessHandler;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
import org.springframework.security.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Logs a principal out.
|
||||
* <p>
|
||||
* Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order they are required.
|
||||
* Generally you will want to call logout handlers <code>TokenBasedRememberMeServices</code> and
|
||||
* <code>SecurityContextLogoutHandler</code> (in that order).
|
||||
* <p>
|
||||
* After logout, a redirect will be performed to the URL determined by either the configured
|
||||
* <tt>LogoutSuccessHandler</tt> or the <tt>logoutSuccessUrl</tt>, depending on which constructor was used.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LogoutFilter extends SpringSecurityFilter {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private String filterProcessesUrl = "/j_spring_security_logout";
|
||||
private List<LogoutHandler> handlers; private LogoutSuccessHandler logoutSuccessHandler;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Constructor which takes a <tt>LogoutSuccessHandler</tt> instance to determine the target destination
|
||||
* after logging out. The list of <tt>LogoutHandler</tt>s are intended to perform the actual logout functionality
|
||||
* (such as clearing the security context, invalidating the session, etc.).
|
||||
*/
|
||||
public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) {
|
||||
Assert.notEmpty(handlers, "LogoutHandlers are required");
|
||||
this.handlers = Arrays.asList(handlers);
|
||||
Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null");
|
||||
this.logoutSuccessHandler = logoutSuccessHandler;
|
||||
}
|
||||
|
||||
public LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {
|
||||
Assert.notEmpty(handlers, "LogoutHandlers are required");
|
||||
this.handlers = Arrays.asList(handlers);
|
||||
Assert.isTrue(!StringUtils.hasLength(logoutSuccessUrl) ||
|
||||
UrlUtils.isValidRedirectUrl(logoutSuccessUrl), logoutSuccessUrl + " isn't a valid redirect URL");
|
||||
SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
|
||||
if (StringUtils.hasText(logoutSuccessUrl)) {
|
||||
urlLogoutSuccessHandler.setDefaultTargetUrl(logoutSuccessUrl);
|
||||
}
|
||||
logoutSuccessHandler = urlLogoutSuccessHandler;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
|
||||
if (requiresLogout(request, response)) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Logging out user '" + auth + "' and transferring to logout destination");
|
||||
}
|
||||
|
||||
for (LogoutHandler handler : handlers) {
|
||||
handler.logout(request, response, auth);
|
||||
}
|
||||
|
||||
logoutSuccessHandler.onLogoutSuccess(request, response, auth);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow subclasses to modify when a logout should take place.
|
||||
*
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
*
|
||||
* @return <code>true</code> if logout should occur, <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) {
|
||||
String uri = request.getRequestURI();
|
||||
int pathParamIndex = uri.indexOf(';');
|
||||
|
||||
if (pathParamIndex > 0) {
|
||||
// strip everything from the first semi-colon
|
||||
uri = uri.substring(0, pathParamIndex);
|
||||
}
|
||||
|
||||
int queryParamIndex = uri.indexOf('?');
|
||||
|
||||
if (queryParamIndex > 0) {
|
||||
// strip everything from the first question mark
|
||||
uri = uri.substring(0, queryParamIndex);
|
||||
}
|
||||
|
||||
if ("".equals(request.getContextPath())) {
|
||||
return uri.endsWith(filterProcessesUrl);
|
||||
}
|
||||
|
||||
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
|
||||
}
|
||||
|
||||
public void setFilterProcessesUrl(String filterProcessesUrl) {
|
||||
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid value for" +
|
||||
" 'filterProcessesUrl'");
|
||||
this.filterProcessesUrl = filterProcessesUrl;
|
||||
}
|
||||
|
||||
protected String getFilterProcessesUrl() {
|
||||
return filterProcessesUrl;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.LOGOUT_FILTER;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui.logout;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a class that is able to participate in logout handling.
|
||||
*
|
||||
* <p>
|
||||
* Called by {@link LogoutFilter}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface LogoutHandler {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Causes a logout to be completed. The method must complete successfully.
|
||||
*
|
||||
* @param request the HTTP request
|
||||
* @param response the HTTP response
|
||||
* @param authentication the current principal details
|
||||
*/
|
||||
void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication);
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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 org.springframework.security.ui.logout;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
/**
|
||||
* Performs a logout by modifying the {@link org.springframework.security.context.SecurityContextHolder}.
|
||||
* <p>
|
||||
* Will also invalidate the {@link HttpSession} if {@link #isInvalidateHttpSession()} is <code>true</code> and the
|
||||
* session is not <code>null</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SecurityContextLogoutHandler implements LogoutHandler {
|
||||
private boolean invalidateHttpSession = true;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Requires the request to be passed in.
|
||||
*
|
||||
* @param request from which to obtain a HTTP session (cannot be null)
|
||||
* @param response not used (can be <code>null</code>)
|
||||
* @param authentication not used (can be <code>null</code>)
|
||||
*/
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
Assert.notNull(request, "HttpServletRequest required");
|
||||
if (invalidateHttpSession) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
session.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
public boolean isInvalidateHttpSession() {
|
||||
return invalidateHttpSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the {@link HttpSession} to be invalidated when this {@link LogoutHandler} is invoked. Defaults to true.
|
||||
*
|
||||
* @param invalidateHttpSession true if you wish the session to be invalidated (default) or false if it should
|
||||
* not be.
|
||||
*/
|
||||
public void setInvalidateHttpSession(boolean invalidateHttpSession) {
|
||||
this.invalidateHttpSession = invalidateHttpSession;
|
||||
}
|
||||
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package org.springframework.security.ui.logout;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.ui.AbstractAuthenticationTargetUrlRequestHandler;
|
||||
import org.springframework.security.ui.LogoutSuccessHandler;
|
||||
|
||||
/**
|
||||
* Handles the navigation on logout by delegating to the {@link AbstractAuthenticationTargetUrlRequestHandler}
|
||||
* base class logic.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.5
|
||||
*/
|
||||
public class SimpleUrlLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler
|
||||
implements LogoutSuccessHandler {
|
||||
|
||||
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
super.handle(request, response, authentication);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Authentication processing mechanisms, which respond to the submission of authentication
|
||||
credentials using various protocols (eg BASIC, CAS, form login etc).
|
||||
</body>
|
||||
</html>
|
||||
-174
@@ -1,174 +0,0 @@
|
||||
package org.springframework.security.ui.preauth;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.security.AuthenticationManager;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.AbstractProcessingFilter;
|
||||
import org.springframework.security.ui.SpringSecurityFilter;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for processing filters that handle pre-authenticated authentication requests. Subclasses must implement
|
||||
* the getPreAuthenticatedPrincipal() and getPreAuthenticatedCredentials() methods.
|
||||
* <p>
|
||||
* By default, the filter chain will proceed when an authentication attempt fails in order to allow other
|
||||
* authentication mechanisms to process the request. To reject the credentials immediately, set the
|
||||
* <tt>continueFilterChainOnUnsuccessfulAuthentication</tt> flag to false. The exception raised by the
|
||||
* <tt>AuthenticationManager</tt> will the be re-thrown. Note that this will not affect cases where the principal
|
||||
* returned by {@link #getPreAuthenticatedPrincipal} is null, when the chain will still proceed as normal.
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Ruud Senden
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractPreAuthenticatedProcessingFilter extends SpringSecurityFilter implements
|
||||
InitializingBean, ApplicationEventPublisherAware {
|
||||
|
||||
private ApplicationEventPublisher eventPublisher = null;
|
||||
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
||||
|
||||
private AuthenticationManager authenticationManager = null;
|
||||
|
||||
private boolean continueFilterChainOnUnsuccessfulAuthentication = true;
|
||||
|
||||
/**
|
||||
* Check whether all required properties have been set.
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(authenticationManager, "An AuthenticationManager must be set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to authenticate a pre-authenticated user with Spring Security if the user has not yet been authenticated.
|
||||
*/
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
|
||||
}
|
||||
|
||||
if (SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
doAuthenticate(request, response);
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the actual authentication for a pre-authenticated user.
|
||||
*/
|
||||
private void doAuthenticate(HttpServletRequest request, HttpServletResponse response) {
|
||||
Authentication authResult = null;
|
||||
|
||||
Object principal = getPreAuthenticatedPrincipal(request);
|
||||
Object credentials = getPreAuthenticatedCredentials(request);
|
||||
|
||||
if (principal == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No pre-authenticated principal found in request");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("preAuthenticatedPrincipal = " + principal + ", trying to authenticate");
|
||||
}
|
||||
|
||||
try {
|
||||
PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(principal, credentials);
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
|
||||
authResult = authenticationManager.authenticate(authRequest);
|
||||
successfulAuthentication(request, response, authResult);
|
||||
} catch (AuthenticationException failed) {
|
||||
unsuccessfulAuthentication(request, response, failed);
|
||||
|
||||
if (!continueFilterChainOnUnsuccessfulAuthentication) {
|
||||
throw failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the <code>Authentication</code> instance returned by the
|
||||
* authentication manager into the secure context.
|
||||
*/
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success: " + authResult);
|
||||
}
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
// Fire event
|
||||
if (this.eventPublisher != null) {
|
||||
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the authentication object in the secure context is set to null
|
||||
* when authentication fails.
|
||||
*/
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cleared security context due to exception", failed);
|
||||
}
|
||||
request.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anApplicationEventPublisher
|
||||
* The ApplicationEventPublisher to use
|
||||
*/
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher anApplicationEventPublisher) {
|
||||
this.eventPublisher = anApplicationEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authenticationDetailsSource
|
||||
* The AuthenticationDetailsSource to use
|
||||
*/
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authenticationManager
|
||||
* The AuthenticationManager to use
|
||||
*/
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
public void setContinueFilterChainOnUnsuccessfulAuthentication(boolean shouldContinue) {
|
||||
continueFilterChainOnUnsuccessfulAuthentication = shouldContinue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to extract the principal information from the current request
|
||||
*/
|
||||
protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Override to extract the credentials (if applicable) from the current request. Some implementations
|
||||
* may return a dummy value.
|
||||
*/
|
||||
protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package org.springframework.security.ui.preauth;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
|
||||
public class PreAuthenticatedCredentialsNotFoundException extends AuthenticationException {
|
||||
|
||||
public PreAuthenticatedCredentialsNotFoundException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
package org.springframework.security.ui.preauth;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.MutableGrantedAuthoritiesContainer;
|
||||
import org.springframework.security.ui.AuthenticationDetails;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This AuthenticationDetails implementation allows for storing a list of
|
||||
* pre-authenticated Granted Authorities.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
* @since 2.0
|
||||
*/
|
||||
public class PreAuthenticatedGrantedAuthoritiesAuthenticationDetails extends AuthenticationDetails implements
|
||||
MutableGrantedAuthoritiesContainer {
|
||||
public static final long serialVersionUID = 1L;
|
||||
|
||||
private List<GrantedAuthority> preAuthenticatedGrantedAuthorities = null;
|
||||
|
||||
public PreAuthenticatedGrantedAuthoritiesAuthenticationDetails(Object context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The String representation of this object.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(super.toString() + "; ");
|
||||
sb.append("preAuthenticatedGrantedAuthorities: " + preAuthenticatedGrantedAuthorities);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see org.springframework.security.GrantedAuthoritiesContainer#getGrantedAuthorities()
|
||||
*/
|
||||
public List<GrantedAuthority> getGrantedAuthorities() {
|
||||
Assert.notNull(preAuthenticatedGrantedAuthorities, "Pre-authenticated granted authorities have not been set");
|
||||
|
||||
return preAuthenticatedGrantedAuthorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.security.MutableGrantedAuthoritiesContainer#setGrantedAuthorities()
|
||||
*/
|
||||
public void setGrantedAuthorities(List<GrantedAuthority> aJ2eeBasedGrantedAuthorities) {
|
||||
this.preAuthenticatedGrantedAuthorities = Collections.unmodifiableList(aJ2eeBasedGrantedAuthorities);
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package org.springframework.security.ui.preauth;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.ui.WebAuthenticationDetails;
|
||||
import org.springframework.security.GrantedAuthoritiesContainerImpl;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.MutableGrantedAuthoritiesContainer;
|
||||
|
||||
/**
|
||||
* This WebAuthenticationDetails implementation allows for storing a list of
|
||||
* pre-authenticated Granted Authorities.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
*/
|
||||
public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends WebAuthenticationDetails implements
|
||||
MutableGrantedAuthoritiesContainer {
|
||||
public static final long serialVersionUID = 1L;
|
||||
|
||||
private MutableGrantedAuthoritiesContainer authoritiesContainer = new GrantedAuthoritiesContainerImpl();
|
||||
|
||||
public PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
public List<GrantedAuthority> getGrantedAuthorities() {
|
||||
return authoritiesContainer.getGrantedAuthorities();
|
||||
}
|
||||
|
||||
public void setGrantedAuthorities(List<GrantedAuthority> authorities) {
|
||||
this.authoritiesContainer.setGrantedAuthorities(authorities);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(super.toString() + "; ");
|
||||
sb.append(authoritiesContainer);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package org.springframework.security.ui.preauth;
|
||||
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.ui.AuthenticationEntryPoint;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* In the pre-authenticated authentication case (unlike CAS, for example) the
|
||||
* user will already have been identified through some external mechanism and a
|
||||
* secure context established by the time the security-enforcement filter is
|
||||
* invoked.
|
||||
* <p>
|
||||
* Therefore this class isn't actually responsible for the commencement of
|
||||
* authentication, as it is in the case of other providers. It will be called if
|
||||
* the user is rejected by the AbstractPreAuthenticatedProcessingFilter,
|
||||
* resulting in a null authentication.
|
||||
* <p>
|
||||
* The <code>commence</code> method will always return an
|
||||
* <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).
|
||||
* <p>
|
||||
* This code is based on
|
||||
* {@link org.springframework.security.ui.x509.X509ProcessingFilterEntryPoint}.
|
||||
*
|
||||
* @see org.springframework.security.ui.ExceptionTranslationFilter
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Ruud Senden
|
||||
* @since 2.0
|
||||
*/
|
||||
public class PreAuthenticatedProcessingFilterEntryPoint implements AuthenticationEntryPoint, Ordered {
|
||||
private static final Log logger = LogFactory.getLog(PreAuthenticatedProcessingFilterEntryPoint.class);
|
||||
|
||||
private int order = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Always returns a 403 error code to the client.
|
||||
*/
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException,
|
||||
ServletException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Pre-authenticated entry point called. Rejecting access");
|
||||
}
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int i) {
|
||||
order = i;
|
||||
}
|
||||
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.header;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.ui.preauth.PreAuthenticatedCredentialsNotFoundException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple pre-authenticated filter which obtains the username from a request header, for use with systems such as
|
||||
* CA Siteminder.
|
||||
* <p>
|
||||
* As with most pre-authenticated scenarios, it is essential that the external authentication system is set up
|
||||
* correctly as this filter does no authentication whatsoever. All the protection is assumed to be provided externally
|
||||
* and if this filter is included inappropriately in a configuration, it would be possible to assume the
|
||||
* identity of a user merely by setting the correct header name. This also means it should not be used in combination
|
||||
* with other Spring Security authentication mechanisms such as form login, as this would imply there was a means of
|
||||
* bypassing the external system which would be risky.
|
||||
* <p>
|
||||
* The property <tt>principalRequestHeader</tt> is the name of the request header that contains the username. It
|
||||
* defaults to "SM_USER" for compatibility with Siteminder.
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public class RequestHeaderPreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
|
||||
private String principalRequestHeader = "SM_USER";
|
||||
private String credentialsRequestHeader;
|
||||
|
||||
/**
|
||||
* Read and returns the header named by <tt>principalRequestHeader</tt> from the request.
|
||||
*
|
||||
* @throws PreAuthenticatedCredentialsNotFoundException if the header is missing
|
||||
*/
|
||||
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
|
||||
String principal = request.getHeader(principalRequestHeader);
|
||||
|
||||
if (principal == null) {
|
||||
throw new PreAuthenticatedCredentialsNotFoundException(principalRequestHeader
|
||||
+ " header not found in request.");
|
||||
}
|
||||
|
||||
return principal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Credentials aren't usually applicable, but if a <tt>credentialsRequestHeader</tt> is set, this
|
||||
* will be read and used as the credentials value. Otherwise a dummy value will be used.
|
||||
*/
|
||||
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
|
||||
if (credentialsRequestHeader != null) {
|
||||
String credentials = request.getHeader(credentialsRequestHeader);
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
public void setPrincipalRequestHeader(String principalRequestHeader) {
|
||||
Assert.hasText(principalRequestHeader, "principalRequestHeader must not be empty or null");
|
||||
this.principalRequestHeader = principalRequestHeader;
|
||||
}
|
||||
|
||||
public void setCredentialsRequestHeader(String credentialsRequestHeader) {
|
||||
Assert.hasText(credentialsRequestHeader, "credentialsRequestHeader must not be empty or null");
|
||||
this.credentialsRequestHeader = credentialsRequestHeader;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.PRE_AUTH_FILTER;
|
||||
}
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.j2ee;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.MutableGrantedAuthoritiesContainer;
|
||||
import org.springframework.security.authoritymapping.Attributes2GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.authoritymapping.MappableAttributesRetriever;
|
||||
import org.springframework.security.authoritymapping.SimpleAttributes2GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base implementation for classes scenarios where the authentication details object is used
|
||||
* to store a list of authorities obtained from the context object (such as an HttpServletRequest)
|
||||
* passed to {@link #buildDetails(Object)}.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractPreAuthenticatedAuthenticationDetailsSource extends AuthenticationDetailsSourceImpl {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
protected Set<String> j2eeMappableRoles;
|
||||
protected Attributes2GrantedAuthoritiesMapper j2eeUserRoles2GrantedAuthoritiesMapper =
|
||||
new SimpleAttributes2GrantedAuthoritiesMapper();
|
||||
|
||||
public AbstractPreAuthenticatedAuthenticationDetailsSource() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that all required properties have been set.
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(j2eeMappableRoles, "No mappable roles available");
|
||||
Assert.notNull(j2eeUserRoles2GrantedAuthoritiesMapper, "Roles to granted authorities mapper not set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the authentication details object. If the specified authentication
|
||||
* details class implements {@link MutableGrantedAuthoritiesContainer}, a
|
||||
* list of pre-authenticated Granted Authorities will be set based on the
|
||||
* roles for the current user.
|
||||
*
|
||||
* @see org.springframework.security.ui.AuthenticationDetailsSource#buildDetails(Object)
|
||||
*/
|
||||
public Object buildDetails(Object context) {
|
||||
Object result = super.buildDetails(context);
|
||||
|
||||
if (result instanceof MutableGrantedAuthoritiesContainer) {
|
||||
Collection<String> j2eeUserRoles = getUserRoles(context, j2eeMappableRoles);
|
||||
List<GrantedAuthority> userGas = j2eeUserRoles2GrantedAuthoritiesMapper.getGrantedAuthorities(j2eeUserRoles);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("J2EE roles [" + j2eeUserRoles + "] mapped to Granted Authorities: [" + userGas + "]");
|
||||
}
|
||||
|
||||
((MutableGrantedAuthoritiesContainer) result).setGrantedAuthorities(userGas);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the roles of the current user to be determined from the context object
|
||||
*
|
||||
* @param context the context object (an HttpRequest, PortletRequest etc)
|
||||
* @param mappableRoles the possible roles as determined by the MappableAttributesRetriever
|
||||
* @return the subset of mappable roles which the current user has.
|
||||
*/
|
||||
protected abstract Collection<String> getUserRoles(Object context, Set<String> mappableRoles);
|
||||
|
||||
/**
|
||||
* @param aJ2eeMappableRolesRetriever
|
||||
* The MappableAttributesRetriever to use
|
||||
*/
|
||||
public void setMappableRolesRetriever(MappableAttributesRetriever aJ2eeMappableRolesRetriever) {
|
||||
this.j2eeMappableRoles = aJ2eeMappableRolesRetriever.getMappableAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mapper
|
||||
* The Attributes2GrantedAuthoritiesMapper to use
|
||||
*/
|
||||
public void setUserRoles2GrantedAuthoritiesMapper(Attributes2GrantedAuthoritiesMapper mapper) {
|
||||
j2eeUserRoles2GrantedAuthoritiesMapper = mapper;
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.j2ee;
|
||||
|
||||
import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
|
||||
import org.springframework.security.authoritymapping.SimpleAttributes2GrantedAuthoritiesMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Implementation of AuthenticationDetailsSource which converts the user's J2EE roles (as obtained by calling
|
||||
* {@link HttpServletRequest#isUserInRole(String)}) into GrantedAuthoritys and stores these in the authentication
|
||||
* details object (.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
* @since 2.0
|
||||
*/
|
||||
public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource extends AbstractPreAuthenticatedAuthenticationDetailsSource {
|
||||
/**
|
||||
* Public constructor which overrides the default AuthenticationDetails
|
||||
* class to be used.
|
||||
*/
|
||||
public J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource() {
|
||||
super.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
|
||||
|
||||
j2eeUserRoles2GrantedAuthoritiesMapper = new SimpleAttributes2GrantedAuthoritiesMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the list of user roles based on the current user's J2EE roles.
|
||||
*
|
||||
* @param request The request against which <tt>isUserInRole</tt> will be called for each role name
|
||||
* returned by the MappableAttributesRetriever.
|
||||
* @return GrantedAuthority[] mapped from the user's J2EE roles.
|
||||
*/
|
||||
protected Collection<String> getUserRoles(Object context, Set<String> mappableRoles) {
|
||||
ArrayList<String> j2eeUserRolesList = new ArrayList<String>();
|
||||
|
||||
for (String role : mappableRoles) {
|
||||
if (((HttpServletRequest)context).isUserInRole(role)) {
|
||||
j2eeUserRolesList.add(role);
|
||||
}
|
||||
}
|
||||
|
||||
return j2eeUserRolesList;
|
||||
}
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.j2ee;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
|
||||
/**
|
||||
* This AbstractPreAuthenticatedProcessingFilter implementation is based on the
|
||||
* J2EE container-based authentication mechanism. It will use the J2EE user
|
||||
* principal name as the pre-authenticated principal.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
* @since 2.0
|
||||
*/
|
||||
public class J2eePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
|
||||
/**
|
||||
* Return the J2EE user name.
|
||||
*/
|
||||
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
|
||||
Object principal = httpRequest.getUserPrincipal() == null ? null : httpRequest.getUserPrincipal().getName();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("PreAuthenticated J2EE principal: " + principal);
|
||||
}
|
||||
return principal;
|
||||
}
|
||||
|
||||
/**
|
||||
* For J2EE container-based authentication there is no generic way to
|
||||
* retrieve the credentials, as such this method returns a fixed dummy
|
||||
* value.
|
||||
*/
|
||||
protected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.j2ee;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.security.authoritymapping.XmlMappableAttributesRetriever;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This MappableAttributesRetriever implementation reads the list of defined J2EE
|
||||
* roles from a web.xml file. It's functionality is based on the
|
||||
* XmlMappableAttributesRetriever base class.
|
||||
* <p>
|
||||
* Example on how to configure this MappableAttributesRetriever in the Spring
|
||||
* configuration file:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
*
|
||||
* <bean id="j2eeMappableRolesRetriever" class="org.springframework.security.ui.preauth.j2ee.WebXmlMappableAttributesRetriever">
|
||||
* <property name="webXmlInputStream"><bean factory-bean="webXmlResource" factory-method="getInputStream"/></property>
|
||||
* </bean>
|
||||
* <bean id="webXmlResource" class="org.springframework.web.context.support.ServletContextResource">
|
||||
* <constructor-arg><ref local="servletContext"/></constructor-arg>
|
||||
* <constructor-arg><value>/WEB-INF/web.xml</value></constructor-arg>
|
||||
* </bean>
|
||||
* <bean id="servletContext" class="org.springframework.web.context.support.ServletContextFactoryBean"/>
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author Ruud Senden
|
||||
* @since 2.0
|
||||
*/
|
||||
public class WebXmlMappableAttributesRetriever extends XmlMappableAttributesRetriever {
|
||||
private static final String XPATH_EXPR = "/web-app/security-role/role-name/text()";
|
||||
|
||||
/**
|
||||
* Constructor setting the XPath expression to use
|
||||
*/
|
||||
public WebXmlMappableAttributesRetriever() {
|
||||
super.setXpathExpression(XPATH_EXPR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anInputStream
|
||||
* The InputStream to read the XML data from
|
||||
*/
|
||||
public void setWebXmlInputStream(InputStream anInputStream) {
|
||||
super.setXmlInputStream(anInputStream);
|
||||
}
|
||||
}
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.websphere;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.rmi.PortableRemoteObject;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* WebSphere Security helper class to allow retrieval of the current username and groups.
|
||||
* <p>
|
||||
* See Spring Security Jira SEC-477.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
* @author Stephane Manciot
|
||||
* @since 2.0
|
||||
*/
|
||||
final class WASSecurityHelper {
|
||||
private static final Log logger = LogFactory.getLog(WASSecurityHelper.class);
|
||||
|
||||
private static final String USER_REGISTRY = "UserRegistry";
|
||||
|
||||
private static Method getRunAsSubject = null;
|
||||
|
||||
private static Method getGroupsForUser = null;
|
||||
|
||||
private static Method getSecurityName = null;
|
||||
|
||||
// SEC-803
|
||||
private static Class<?> wsCredentialClass = null;
|
||||
|
||||
/**
|
||||
* Get the security name for the given subject.
|
||||
*
|
||||
* @param subject
|
||||
* The subject for which to retrieve the security name
|
||||
* @return String the security name for the given subject
|
||||
*/
|
||||
private static final String getSecurityName(final Subject subject) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Determining Websphere security name for subject " + subject);
|
||||
}
|
||||
String userSecurityName = null;
|
||||
if (subject != null) {
|
||||
// SEC-803
|
||||
Object credential = subject.getPublicCredentials(getWSCredentialClass()).iterator().next();
|
||||
if (credential != null) {
|
||||
userSecurityName = (String)invokeMethod(getSecurityNameMethod(),credential,null);
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Websphere security name is " + userSecurityName + " for subject " + subject);
|
||||
}
|
||||
return userSecurityName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current RunAs subject.
|
||||
*
|
||||
* @return Subject the current RunAs subject
|
||||
*/
|
||||
private static final Subject getRunAsSubject() {
|
||||
logger.debug("Retrieving WebSphere RunAs subject");
|
||||
// get Subject: WSSubject.getCallerSubject ();
|
||||
return (Subject) invokeMethod(getRunAsSubjectMethod(), null, new Object[] {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WebSphere group names for the given subject.
|
||||
*
|
||||
* @param subject
|
||||
* The subject for which to retrieve the WebSphere group names
|
||||
* @return the WebSphere group names for the given subject
|
||||
*/
|
||||
private static final String[] getWebSphereGroups(final Subject subject) {
|
||||
return getWebSphereGroups(getSecurityName(subject));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WebSphere group names for the given security name.
|
||||
*
|
||||
* @param securityName
|
||||
* The securityname for which to retrieve the WebSphere group names
|
||||
* @return the WebSphere group names for the given security name
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final String[] getWebSphereGroups(final String securityName) {
|
||||
Context ic = null;
|
||||
try {
|
||||
// TODO: Cache UserRegistry object
|
||||
ic = new InitialContext();
|
||||
Object objRef = ic.lookup(USER_REGISTRY);
|
||||
Object userReg = PortableRemoteObject.narrow(objRef, Class.forName ("com.ibm.websphere.security.UserRegistry"));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Determining WebSphere groups for user " + securityName + " using WebSphere UserRegistry " + userReg);
|
||||
}
|
||||
final Collection groups = (Collection) invokeMethod(getGroupsForUserMethod(), userReg, new Object[]{ securityName });
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Groups for user " + securityName + ": " + groups.toString());
|
||||
}
|
||||
String[] result = new String[groups.size()];
|
||||
return (String[]) groups.toArray(result);
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception occured while looking up groups for user", e);
|
||||
throw new RuntimeException("Exception occured while looking up groups for user", e);
|
||||
} finally {
|
||||
try {
|
||||
ic.close();
|
||||
} catch (NamingException e) {
|
||||
logger.debug("Exception occured while closing context", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static final String[] getGroupsForCurrentUser() {
|
||||
return getWebSphereGroups(getRunAsSubject());
|
||||
}
|
||||
|
||||
public static final String getCurrentUserName() {
|
||||
return getSecurityName(getRunAsSubject());
|
||||
}
|
||||
|
||||
private static final Object invokeMethod(Method method, Object instance, Object[] args)
|
||||
{
|
||||
try {
|
||||
return method.invoke(instance,args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+ Arrays.asList(args)+")",e);
|
||||
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
|
||||
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
|
||||
} catch (InvocationTargetException e) {
|
||||
logger.error("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
|
||||
throw new RuntimeException("Error while invoking method "+method.getClass().getName()+"."+method.getName()+"("+Arrays.asList(args)+")",e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Method getMethod(String className, String methodName, String[] parameterTypeNames) {
|
||||
try {
|
||||
Class<?> c = Class.forName(className);
|
||||
final int len = parameterTypeNames.length;
|
||||
Class<?>[] parameterTypes = new Class[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
parameterTypes[i] = Class.forName(parameterTypeNames[i]);
|
||||
}
|
||||
return c.getDeclaredMethod(methodName, parameterTypes);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.error("Required class"+className+" not found");
|
||||
throw new RuntimeException("Required class"+className+" not found",e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
logger.error("Required method "+methodName+" with parameter types ("+ Arrays.asList(parameterTypeNames) +") not found on class "+className);
|
||||
throw new RuntimeException("Required class"+className+" not found",e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Method getRunAsSubjectMethod() {
|
||||
if (getRunAsSubject == null) {
|
||||
getRunAsSubject = getMethod("com.ibm.websphere.security.auth.WSSubject", "getRunAsSubject", new String[] {});
|
||||
}
|
||||
return getRunAsSubject;
|
||||
}
|
||||
|
||||
private static final Method getGroupsForUserMethod() {
|
||||
if (getGroupsForUser == null) {
|
||||
getGroupsForUser = getMethod("com.ibm.websphere.security.UserRegistry", "getGroupsForUser", new String[] { "java.lang.String" });
|
||||
}
|
||||
return getGroupsForUser;
|
||||
}
|
||||
|
||||
private static final Method getSecurityNameMethod() {
|
||||
if (getSecurityName == null) {
|
||||
getSecurityName = getMethod("com.ibm.websphere.security.cred.WSCredential", "getSecurityName", new String[] {});
|
||||
}
|
||||
return getSecurityName;
|
||||
}
|
||||
|
||||
// SEC-803
|
||||
private static final Class<?> getWSCredentialClass() {
|
||||
if (wsCredentialClass == null) {
|
||||
wsCredentialClass = getClass("com.ibm.websphere.security.cred.WSCredential");
|
||||
}
|
||||
return wsCredentialClass;
|
||||
}
|
||||
|
||||
private static final Class<?> getClass(String className) {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.error("Required class " + className + " not found");
|
||||
throw new RuntimeException("Required class " + className + " not found",e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.websphere;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationManager;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This method interceptor can be used in front of arbitrary Spring beans to make a Spring SecurityContext
|
||||
* available to the bean, based on the current WebSphere credentials.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
* @since 1.0
|
||||
*/
|
||||
public class WebSphere2SpringSecurityPropagationInterceptor implements MethodInterceptor {
|
||||
private static final Log LOG = LogFactory.getLog(WebSphere2SpringSecurityPropagationInterceptor.class);
|
||||
private AuthenticationManager authenticationManager = null;
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new WebSpherePreAuthenticatedAuthenticationDetailsSource();
|
||||
|
||||
/**
|
||||
* Authenticate with Spring Security based on WebSphere credentials before proceeding with method
|
||||
* invocation, and clean up the Spring Security Context after method invocation finishes.
|
||||
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
|
||||
*/
|
||||
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
|
||||
try {
|
||||
LOG.debug("Performing Spring Security authentication with WebSphere credentials");
|
||||
authenticateSpringSecurityWithWASCredentials(this);
|
||||
LOG.debug("Proceeding with method invocation");
|
||||
return methodInvocation.proceed();
|
||||
} finally {
|
||||
LOG.debug("Clearing Spring Security security context");
|
||||
clearSpringSecurityContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current WebSphere credentials and authenticate them with Spring Security
|
||||
* using the pre-authenticated authentication provider.
|
||||
* @param aContext The context to use for building the authentication details.
|
||||
*/
|
||||
private final void authenticateSpringSecurityWithWASCredentials(Object aContext)
|
||||
{
|
||||
Assert.notNull(authenticationManager);
|
||||
Assert.notNull(authenticationDetailsSource);
|
||||
|
||||
String userName = WASSecurityHelper.getCurrentUserName();
|
||||
if (LOG.isDebugEnabled()) { LOG.debug("Creating authentication request for user "+userName); }
|
||||
PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(userName,null);
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(null));
|
||||
if (LOG.isDebugEnabled()) { LOG.debug("Authentication request for user "+userName+": "+authRequest); }
|
||||
Authentication authResponse = authenticationManager.authenticate(authRequest);
|
||||
if (LOG.isDebugEnabled()) { LOG.debug("Authentication response for user "+userName+": "+authResponse); }
|
||||
SecurityContextHolder.getContext().setAuthentication(authResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the Spring Security Context
|
||||
*/
|
||||
private final void clearSpringSecurityContext()
|
||||
{
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the authenticationManager.
|
||||
*/
|
||||
public AuthenticationManager getAuthenticationManager() {
|
||||
return authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authenticationManager The authenticationManager to set.
|
||||
*/
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
/**
|
||||
* @return Returns the authenticationDetailsSource.
|
||||
*/
|
||||
public AuthenticationDetailsSource getAuthenticationDetailsSource() {
|
||||
return authenticationDetailsSource;
|
||||
}
|
||||
/**
|
||||
* @param authenticationDetailsSource The authenticationDetailsSource to set.
|
||||
*/
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.websphere;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.MutableGrantedAuthoritiesContainer;
|
||||
import org.springframework.security.authoritymapping.Attributes2GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.authoritymapping.SimpleAttributes2GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
|
||||
import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesAuthenticationDetails;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This AuthenticationDetailsSource implementation, when configured with a MutableGrantedAuthoritiesContainer,
|
||||
* will set the pre-authenticated granted authorities based on the WebSphere groups for the current WebSphere
|
||||
* user, mapped using the configured Attributes2GrantedAuthoritiesMapper.
|
||||
*
|
||||
* By default, this class is configured to build instances of the
|
||||
* PreAuthenticatedGrantedAuthoritiesAuthenticationDetails class.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
*/
|
||||
public class WebSpherePreAuthenticatedAuthenticationDetailsSource extends AuthenticationDetailsSourceImpl implements InitializingBean {
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Attributes2GrantedAuthoritiesMapper webSphereGroups2GrantedAuthoritiesMapper = new SimpleAttributes2GrantedAuthoritiesMapper();
|
||||
|
||||
/**
|
||||
* Public constructor which overrides the default AuthenticationDetails
|
||||
* class to be used.
|
||||
*/
|
||||
public WebSpherePreAuthenticatedAuthenticationDetailsSource() {
|
||||
super.setClazz(PreAuthenticatedGrantedAuthoritiesAuthenticationDetails.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that all required properties have been set.
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(webSphereGroups2GrantedAuthoritiesMapper, "WebSphere groups to granted authorities mapper not set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the authentication details object. If the specified authentication
|
||||
* details class implements the PreAuthenticatedGrantedAuthoritiesSetter, a
|
||||
* list of pre-authenticated Granted Authorities will be set based on the
|
||||
* WebSphere groups for the current user.
|
||||
*
|
||||
* @see org.springframework.security.ui.AuthenticationDetailsSource#buildDetails(Object)
|
||||
*/
|
||||
public Object buildDetails(Object context) {
|
||||
Object result = super.buildDetails(context);
|
||||
if (result instanceof MutableGrantedAuthoritiesContainer) {
|
||||
((MutableGrantedAuthoritiesContainer) result)
|
||||
.setGrantedAuthorities(getWebSphereGroupsBasedGrantedAuthorities());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of Granted Authorities based on the current user's WebSphere groups.
|
||||
*
|
||||
* @return GrantedAuthority[] mapped from the user's WebSphere groups.
|
||||
*/
|
||||
private List<GrantedAuthority> getWebSphereGroupsBasedGrantedAuthorities() {
|
||||
List<String> webSphereGroups = Arrays.asList(WASSecurityHelper.getGroupsForCurrentUser());
|
||||
List<GrantedAuthority> userGas = webSphereGroups2GrantedAuthoritiesMapper.getGrantedAuthorities(webSphereGroups);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("WebSphere groups: " + webSphereGroups + " mapped to Granted Authorities: " + userGas);
|
||||
}
|
||||
return userGas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mapper
|
||||
* The Attributes2GrantedAuthoritiesMapper to use
|
||||
*/
|
||||
public void setWebSphereGroups2GrantedAuthoritiesMapper(Attributes2GrantedAuthoritiesMapper mapper) {
|
||||
webSphereGroups2GrantedAuthoritiesMapper = mapper;
|
||||
}
|
||||
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.websphere;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
|
||||
/**
|
||||
* This AbstractPreAuthenticatedProcessingFilter implementation is based on
|
||||
* WebSphere authentication. It will use the WebSphere RunAs user principal name
|
||||
* as the pre-authenticated principal.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
* @since 2.0
|
||||
*/
|
||||
public class WebSpherePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
|
||||
/**
|
||||
* Return the WebSphere user name.
|
||||
*/
|
||||
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
|
||||
Object principal = WASSecurityHelper.getCurrentUserName();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("PreAuthenticated WebSphere principal: " + principal);
|
||||
}
|
||||
return principal;
|
||||
}
|
||||
|
||||
/**
|
||||
* For J2EE container-based authentication there is no generic way to
|
||||
* retrieve the credentials, as such this method returns a fixed dummy
|
||||
* value.
|
||||
*/
|
||||
protected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.websphere;
|
||||
|
||||
import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
|
||||
|
||||
/**
|
||||
* This AuthenticationDetailsSource implementation, when configured with a MutableGrantedAuthoritiesContainer,
|
||||
* will set the pre-authenticated granted authorities based on the WebSphere groups for the current WebSphere
|
||||
* user, mapped using the configured Attributes2GrantedAuthoritiesMapper.
|
||||
*
|
||||
* By default, this class is configured to build instances of the
|
||||
* PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails class.
|
||||
*
|
||||
* @author Ruud Senden
|
||||
*/
|
||||
public class WebSpherePreAuthenticatedWebAuthenticationDetailsSource extends WebSpherePreAuthenticatedAuthenticationDetailsSource {
|
||||
/**
|
||||
* Public constructor which overrides the default AuthenticationDetails
|
||||
* class to be used.
|
||||
*/
|
||||
public WebSpherePreAuthenticatedWebAuthenticationDetailsSource() {
|
||||
super();
|
||||
super.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
|
||||
}
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.x509;
|
||||
|
||||
import org.springframework.security.BadCredentialsException;
|
||||
import org.springframework.security.SpringSecurityMessageSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.context.MessageSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* Obtains the principal from a certificate using a regular expression match against the Subject (as returned by a call
|
||||
* to {@link X509Certificate#getSubjectDN()}).
|
||||
* <p>
|
||||
* The regular expression should contain a single group; for example the default expression "CN=(.?)," matches the
|
||||
* common name field. So "CN=Jimi Hendrix, OU=..." will give a user name of "Jimi Hendrix".
|
||||
* <p>
|
||||
* The matches are case insensitive. So "emailAddress=(.?)," will match "EMAILADDRESS=jimi@hendrix.org, CN=..." giving a
|
||||
* user name "jimi@hendrix.org"
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SubjectDnX509PrincipalExtractor implements X509PrincipalExtractor {
|
||||
//~ Instance fields ================================================================================================
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
private Pattern subjectDnPattern;
|
||||
|
||||
public SubjectDnX509PrincipalExtractor() {
|
||||
setSubjectDnRegex("CN=(.*?),");
|
||||
}
|
||||
|
||||
public Object extractPrincipal(X509Certificate clientCert) {
|
||||
//String subjectDN = clientCert.getSubjectX500Principal().getName();
|
||||
String subjectDN = clientCert.getSubjectDN().getName();
|
||||
|
||||
logger.debug("Subject DN is '" + subjectDN + "'");
|
||||
|
||||
Matcher matcher = subjectDnPattern.matcher(subjectDN);
|
||||
|
||||
if (!matcher.find()) {
|
||||
throw new BadCredentialsException(messages.getMessage("DaoX509AuthoritiesPopulator.noMatching",
|
||||
new Object[] {subjectDN}, "No matching pattern was found in subject DN: {0}"));
|
||||
}
|
||||
|
||||
if (matcher.groupCount() != 1) {
|
||||
throw new IllegalArgumentException("Regular expression must contain a single group ");
|
||||
}
|
||||
|
||||
String username = matcher.group(1);
|
||||
|
||||
logger.debug("Extracted Principal name is '" + username + "'");
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the regular expression which will by used to extract the user name from the certificate's Subject
|
||||
* DN.
|
||||
* <p>
|
||||
* It should contain a single group; for example the default expression "CN=(.?)," matches the common
|
||||
* name field. So "CN=Jimi Hendrix, OU=..." will give a user name of "Jimi Hendrix".
|
||||
* <p>
|
||||
* The matches are case insensitive. So "emailAddress=(.?)," will match "EMAILADDRESS=jimi@hendrix.org,
|
||||
* CN=..." giving a user name "jimi@hendrix.org"
|
||||
*
|
||||
* @param subjectDnRegex the regular expression to find in the subject
|
||||
*/
|
||||
public void setSubjectDnRegex(String subjectDnRegex) {
|
||||
Assert.hasText(subjectDnRegex, "Regular expression may not be null or empty");
|
||||
subjectDnPattern = Pattern.compile(subjectDnRegex, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.x509;
|
||||
|
||||
import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.ui.FilterChainOrder;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class X509PreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
|
||||
private X509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
|
||||
|
||||
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
|
||||
X509Certificate cert = extractClientCertificate(request);
|
||||
|
||||
if (cert == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return principalExtractor.extractPrincipal(cert);
|
||||
}
|
||||
|
||||
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
|
||||
return extractClientCertificate(request);
|
||||
}
|
||||
|
||||
private X509Certificate extractClientCertificate(HttpServletRequest request) {
|
||||
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
|
||||
|
||||
if (certs != null && certs.length > 0) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("X.509 client authentication certificate:" + certs[0]);
|
||||
}
|
||||
|
||||
return certs[0];
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No client certificate found in request.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
|
||||
this.principalExtractor = principalExtractor;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.X509_FILTER;
|
||||
}
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package org.springframework.security.ui.preauth.x509;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* Obtains the principal from an X509Certificate for use within the framework.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface X509PrincipalExtractor {
|
||||
|
||||
/**
|
||||
* Returns the principal (usually a String) for the given certificate.
|
||||
*/
|
||||
Object extractPrincipal(X509Certificate cert);
|
||||
}
|
||||
-384
@@ -1,384 +0,0 @@
|
||||
package org.springframework.security.ui.rememberme;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.SpringSecurityMessageSource;
|
||||
import org.springframework.security.AccountStatusException;
|
||||
import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.logout.LogoutHandler;
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.userdetails.UserDetailsService;
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.userdetails.UserDetailsChecker;
|
||||
import org.springframework.security.userdetails.checker.AccountStatusUserDetailsChecker;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Base class for RememberMeServices implementations.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
public static final String SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY = "SPRING_SECURITY_REMEMBER_ME_COOKIE";
|
||||
public static final String DEFAULT_PARAMETER = "_spring_security_remember_me";
|
||||
public static final int TWO_WEEKS_S = 1209600;
|
||||
|
||||
private static final String DELIMITER = ":";
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
private UserDetailsService userDetailsService;
|
||||
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
||||
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
||||
|
||||
private String cookieName = SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY;
|
||||
private String parameter = DEFAULT_PARAMETER;
|
||||
private boolean alwaysRemember;
|
||||
private String key;
|
||||
private int tokenValiditySeconds = TWO_WEEKS_S;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(key);
|
||||
Assert.hasLength(parameter);
|
||||
Assert.hasLength(cookieName);
|
||||
Assert.notNull(userDetailsService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template implementation which locates the Spring Security cookie, decodes it into
|
||||
* a delimited array of tokens and submits it to subclasses for processing
|
||||
* via the <tt>processAutoLoginCookie</tt> method.
|
||||
* <p>
|
||||
* The returned username is then used to load the UserDetails object for the user, which in turn
|
||||
* is used to create a valid authentication token.
|
||||
*/
|
||||
public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
|
||||
String rememberMeCookie = extractRememberMeCookie(request);
|
||||
|
||||
if (rememberMeCookie == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("Remember-me cookie detected");
|
||||
|
||||
UserDetails user = null;
|
||||
|
||||
try {
|
||||
String[] cookieTokens = decodeCookie(rememberMeCookie);
|
||||
user = processAutoLoginCookie(cookieTokens, request, response);
|
||||
userDetailsChecker.check(user);
|
||||
} catch (CookieTheftException cte) {
|
||||
cancelCookie(request, response);
|
||||
throw cte;
|
||||
} catch (UsernameNotFoundException noUser) {
|
||||
cancelCookie(request, response);
|
||||
logger.debug("Remember-me login was valid but corresponding user not found.", noUser);
|
||||
return null;
|
||||
} catch (InvalidCookieException invalidCookie) {
|
||||
cancelCookie(request, response);
|
||||
logger.debug("Invalid remember-me cookie: " + invalidCookie.getMessage());
|
||||
return null;
|
||||
} catch (AccountStatusException statusInvalid) {
|
||||
cancelCookie(request, response);
|
||||
logger.debug("Invalid UserDetails: " + statusInvalid.getMessage());
|
||||
return null;
|
||||
} catch (RememberMeAuthenticationException e) {
|
||||
cancelCookie(request, response);
|
||||
logger.debug(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("Remember-me cookie accepted");
|
||||
|
||||
return createSuccessfulAuthentication(request, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the Spring Security remember me cookie in the request and returns its value.
|
||||
*
|
||||
* @param request the submitted request which is to be authenticated
|
||||
* @return the cookie value (if present), null otherwise.
|
||||
*/
|
||||
protected String extractRememberMeCookie(HttpServletRequest request) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
|
||||
if ((cookies == null) || (cookies.length == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cookies.length; i++) {
|
||||
if (cookieName.equals(cookies[i].getName())) {
|
||||
return cookies[i].getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the final <tt>Authentication</tt> object returned from the <tt>autoLogin</tt> method.
|
||||
* <p>
|
||||
* By default it will create a <tt>RememberMeAuthenticationToken</tt> instance.
|
||||
*
|
||||
* @param request the original request. The configured <tt>AuthenticationDetailsSource</tt> will
|
||||
* use this to build the details property of the returned object.
|
||||
* @param user the <tt>UserDetails</tt> loaded from the <tt>UserDetailsService</tt>. This will be
|
||||
* stored as the principal.
|
||||
*
|
||||
* @return the <tt>Authentication</tt> for the remember-me authenticated user
|
||||
*/
|
||||
protected Authentication createSuccessfulAuthentication(HttpServletRequest request, UserDetails user) {
|
||||
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(key, user, user.getAuthorities());
|
||||
auth.setDetails(authenticationDetailsSource.buildDetails(request));
|
||||
return auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the cookie and splits it into a set of token strings using the ":" delimiter.
|
||||
*
|
||||
* @param cookieValue the value obtained from the submitted cookie
|
||||
* @return the array of tokens.
|
||||
* @throws InvalidCookieException if the cookie was not base64 encoded.
|
||||
*/
|
||||
protected String[] decodeCookie(String cookieValue) throws InvalidCookieException {
|
||||
for (int j = 0; j < cookieValue.length() % 4; j++) {
|
||||
cookieValue = cookieValue + "=";
|
||||
}
|
||||
|
||||
if (!Base64.isArrayByteBase64(cookieValue.getBytes())) {
|
||||
throw new InvalidCookieException( "Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
|
||||
}
|
||||
|
||||
String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
|
||||
|
||||
return StringUtils.delimitedListToStringArray(cookieAsPlainText, DELIMITER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse operation of decodeCookie.
|
||||
*
|
||||
* @param cookieTokens the tokens to be encoded.
|
||||
* @return base64 encoding of the tokens concatenated with the ":" delimiter.
|
||||
*/
|
||||
protected String encodeCookie(String[] cookieTokens) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for(int i=0; i < cookieTokens.length; i++) {
|
||||
sb.append(cookieTokens[i]);
|
||||
|
||||
if (i < cookieTokens.length - 1) {
|
||||
sb.append(DELIMITER);
|
||||
}
|
||||
}
|
||||
|
||||
String value = sb.toString();
|
||||
|
||||
sb = new StringBuffer(new String(Base64.encodeBase64(value.getBytes())));
|
||||
|
||||
while (sb.charAt(sb.length() - 1) == '=') {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public final void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
||||
logger.debug("Interactive login attempt was unsuccessful.");
|
||||
cancelCookie(request, response);
|
||||
onLoginFail(request, response);
|
||||
}
|
||||
|
||||
protected void onLoginFail(HttpServletRequest request, HttpServletResponse response) {}
|
||||
|
||||
/**
|
||||
* Examines the incoming request and checks for the presence of the configured "remember me" parameter.
|
||||
* If it's present, or if <tt>alwaysRemember</tt> is set to true, calls <tt>onLoginSucces</tt>.
|
||||
*/
|
||||
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication successfulAuthentication) {
|
||||
|
||||
if (!rememberMeRequested(request, parameter)) {
|
||||
logger.debug("Remember-me login not requested.");
|
||||
return;
|
||||
}
|
||||
|
||||
onLoginSuccess(request, response, successfulAuthentication);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from loginSuccess when a remember-me login has been requested.
|
||||
* Typically implemented by subclasses to set a remember-me cookie and potentially store a record
|
||||
* of it if the implementation requires this.
|
||||
*/
|
||||
protected abstract void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication successfulAuthentication);
|
||||
|
||||
/**
|
||||
* Allows customization of whether a remember-me login has been requested.
|
||||
* The default is to return true if <tt>alwaysRemember</tt> is set or the configured parameter name has
|
||||
* been included in the request and is set to the value "true".
|
||||
*
|
||||
* @param request the request submitted from an interactive login, which may include additional information
|
||||
* indicating that a persistent login is desired.
|
||||
* @param parameter the configured remember-me parameter name.
|
||||
*
|
||||
* @return true if the request includes information indicating that a persistent login has been
|
||||
* requested.
|
||||
*/
|
||||
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
|
||||
if (alwaysRemember) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String paramValue = request.getParameter(parameter);
|
||||
|
||||
if (paramValue != null) {
|
||||
if (paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on") ||
|
||||
paramValue.equalsIgnoreCase("yes") || paramValue.equals("1")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Did not send remember-me cookie (principal did not set parameter '" + parameter + "')");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from autoLogin to process the submitted persistent login cookie. Subclasses should
|
||||
* validate the cookie and perform any additional management required.
|
||||
*
|
||||
* @param cookieTokens the decoded and tokenized cookie value
|
||||
* @param request the request
|
||||
* @param response the response, to allow the cookie to be modified if required.
|
||||
* @return the UserDetails for the corresponding user account if the cookie was validated successfully.
|
||||
* @throws RememberMeAuthenticationException if the cookie is invalid or the login is invalid for some
|
||||
* other reason.
|
||||
* @throws UsernameNotFoundException if the user account corresponding to the login cookie couldn't be found
|
||||
* (for example if the user has been removed from the system).
|
||||
*/
|
||||
protected abstract UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
|
||||
HttpServletResponse response) throws RememberMeAuthenticationException, UsernameNotFoundException;
|
||||
|
||||
/**
|
||||
* Sets a "cancel cookie" (with maxAge = 0) on the response to disable persistent logins.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {
|
||||
logger.debug("Cancelling cookie");
|
||||
Cookie cookie = new Cookie(cookieName, null);
|
||||
cookie.setMaxAge(0);
|
||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
||||
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cookie on the response
|
||||
*
|
||||
* @param tokens the tokens which will be encoded to make the cookie value.
|
||||
* @param maxAge the value passed to {@link Cookie#setMaxAge(int)}
|
||||
* @param request the request
|
||||
* @param response the response to add the cookie to.
|
||||
*/
|
||||
protected void setCookie(String[] tokens, int maxAge, HttpServletRequest request, HttpServletResponse response) {
|
||||
String cookieValue = encodeCookie(tokens);
|
||||
Cookie cookie = new Cookie(cookieName, cookieValue);
|
||||
cookie.setMaxAge(maxAge);
|
||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of <tt>LogoutHandler</tt>. Default behaviour is to call <tt>cancelCookie()</tt>.
|
||||
*/
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug( "Logout of user "
|
||||
+ (authentication == null ? "Unknown" : authentication.getName()));
|
||||
}
|
||||
cancelCookie(request, response);
|
||||
}
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
this.cookieName = cookieName;
|
||||
}
|
||||
|
||||
protected String getCookieName() {
|
||||
return cookieName;
|
||||
}
|
||||
|
||||
public void setAlwaysRemember(boolean alwaysRemember) {
|
||||
this.alwaysRemember = alwaysRemember;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the parameter which should be checked for to see if a remember-me has been requested
|
||||
* during a login request. This should be the same name you assign to the checkbox in your login form.
|
||||
*
|
||||
* @param parameter the HTTP request parameter
|
||||
*/
|
||||
public void setParameter(String parameter) {
|
||||
Assert.hasText(parameter, "Parameter name cannot be null");
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
public String getParameter() {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
protected UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
Assert.notNull(userDetailsService, "UserDetailsService canot be null");
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setTokenValiditySeconds(int tokenValiditySeconds) {
|
||||
this.tokenValiditySeconds = tokenValiditySeconds;
|
||||
}
|
||||
|
||||
protected int getTokenValiditySeconds() {
|
||||
return tokenValiditySeconds;
|
||||
}
|
||||
|
||||
protected AuthenticationDetailsSource getAuthenticationDetailsSource() {
|
||||
return authenticationDetailsSource;
|
||||
}
|
||||
|
||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource cannot be null");
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user