|
@@ -46,21 +46,17 @@ import javax.servlet.http.HttpServletResponse;
|
|
|
* <p>
|
|
* <p>
|
|
|
* This filter is necessary because it provides the bridge between Java exceptions and HTTP responses.
|
|
* 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.
|
|
* It is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.
|
|
|
- * </p>
|
|
|
|
|
* <p>
|
|
* <p>
|
|
|
* If an {@link AuthenticationException} is detected, the filter will launch the <code>authenticationEntryPoint</code>.
|
|
* 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
|
|
* This allows common handling of authentication failures originating from any subclass of
|
|
|
* {@link org.springframework.security.intercept.AbstractSecurityInterceptor}.
|
|
* {@link org.springframework.security.intercept.AbstractSecurityInterceptor}.
|
|
|
- * </p>
|
|
|
|
|
* <p>
|
|
* <p>
|
|
|
* If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an anonymous
|
|
* 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
|
|
* 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}.
|
|
* 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}.
|
|
* By default the filter will use {@link org.springframework.security.ui.AccessDeniedHandlerImpl}.
|
|
|
- * </p>
|
|
|
|
|
* <p>
|
|
* <p>
|
|
|
* To use this filter, it is necessary to specify the following properties:
|
|
* To use this filter, it is necessary to specify the following properties:
|
|
|
- * </p>
|
|
|
|
|
* <ul>
|
|
* <ul>
|
|
|
* <li><code>authenticationEntryPoint</code> indicates the handler that
|
|
* <li><code>authenticationEntryPoint</code> indicates the handler that
|
|
|
* should commence the authentication process if an
|
|
* should commence the authentication process if an
|
|
@@ -76,34 +72,34 @@ import javax.servlet.http.HttpServletResponse;
|
|
|
*/
|
|
*/
|
|
|
public class ExceptionTranslationFilter extends SpringSecurityFilter implements InitializingBean {
|
|
public class ExceptionTranslationFilter extends SpringSecurityFilter implements InitializingBean {
|
|
|
|
|
|
|
|
- //~ Instance fields ================================================================================================
|
|
|
|
|
|
|
+ //~ Instance fields ================================================================================================
|
|
|
|
|
|
|
|
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
|
|
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
|
|
|
- private AuthenticationEntryPoint authenticationEntryPoint;
|
|
|
|
|
- private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
|
|
|
|
- private PortResolver portResolver = new PortResolverImpl();
|
|
|
|
|
- private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
|
|
|
|
|
|
|
+ private AuthenticationEntryPoint authenticationEntryPoint;
|
|
|
|
|
+ private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
|
|
|
|
+ private PortResolver portResolver = new PortResolverImpl();
|
|
|
|
|
+ private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
|
|
|
private boolean createSessionAllowed = true;
|
|
private boolean createSessionAllowed = true;
|
|
|
|
|
|
|
|
- //~ Methods ========================================================================================================
|
|
|
|
|
|
|
+ //~ 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 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 {
|
|
|
|
|
|
|
+ public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException,
|
|
|
|
|
+ ServletException {
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- chain.doFilter(request, response);
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ chain.doFilter(request, response);
|
|
|
|
|
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
|
|
- logger.debug("Chain processed normally");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
|
|
+ logger.debug("Chain processed normally");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
catch (IOException ex) {
|
|
catch (IOException ex) {
|
|
|
throw ex;
|
|
throw ex;
|
|
|
}
|
|
}
|
|
@@ -129,114 +125,116 @@ public class ExceptionTranslationFilter extends SpringSecurityFilter implements
|
|
|
throw new RuntimeException(ex);
|
|
throw new RuntimeException(ex);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public AuthenticationEntryPoint getAuthenticationEntryPoint() {
|
|
|
|
|
- return authenticationEntryPoint;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public AuthenticationTrustResolver getAuthenticationTrustResolver() {
|
|
|
|
|
- return authenticationTrustResolver;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public PortResolver getPortResolver() {
|
|
|
|
|
- return portResolver;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void handleException(ServletRequest request, ServletResponse 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>SecurityEnforcementFilter</code> is permitted to store the target
|
|
|
|
|
- * URL and exception information in the <code>HttpSession</code> (the default).
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public AuthenticationEntryPoint getAuthenticationEntryPoint() {
|
|
|
|
|
+ return authenticationEntryPoint;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public AuthenticationTrustResolver getAuthenticationTrustResolver() {
|
|
|
|
|
+ return authenticationTrustResolver;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public PortResolver getPortResolver() {
|
|
|
|
|
+ return portResolver;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void handleException(ServletRequest request, ServletResponse 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 the <code>HttpSession</code> (the default).
|
|
|
* In situations where you do not wish to unnecessarily create <code>HttpSession</code>s - because the user agent
|
|
* 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>. Remember to also set the
|
|
|
|
|
- * {@link org.springframework.security.context.HttpSessionContextIntegrationFilter#allowSessionCreation}
|
|
|
|
|
- * 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(ServletRequest request, ServletResponse response, FilterChain chain,
|
|
|
|
|
- AuthenticationException reason) throws ServletException, IOException {
|
|
|
|
|
- HttpServletRequest httpRequest = (HttpServletRequest) request;
|
|
|
|
|
-
|
|
|
|
|
- SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver);
|
|
|
|
|
-
|
|
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
|
|
- logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (createSessionAllowed) {
|
|
|
|
|
- // Store the HTTP request itself. Used by AbstractProcessingFilter
|
|
|
|
|
- // for redirection after successful authentication (SEC-29)
|
|
|
|
|
- httpRequest.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // SEC-112: Clear the SecurityContextHolder's Authentication, as the
|
|
|
|
|
- // existing Authentication is no longer considered valid
|
|
|
|
|
- SecurityContextHolder.getContext().setAuthentication(null);
|
|
|
|
|
-
|
|
|
|
|
- authenticationEntryPoint.commence(httpRequest, response, reason);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- 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;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ * 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(ServletRequest request, ServletResponse response, FilterChain chain,
|
|
|
|
|
+ AuthenticationException reason) throws ServletException, IOException {
|
|
|
|
|
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
|
|
|
|
|
+
|
|
|
|
|
+ SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver);
|
|
|
|
|
+
|
|
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
|
|
+ logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (createSessionAllowed) {
|
|
|
|
|
+ // Store the HTTP request itself. Used by AbstractProcessingFilter
|
|
|
|
|
+ // for redirection after successful authentication (SEC-29)
|
|
|
|
|
+ httpRequest.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // SEC-112: Clear the SecurityContextHolder's Authentication, as the
|
|
|
|
|
+ // existing Authentication is no longer considered valid
|
|
|
|
|
+ SecurityContextHolder.getContext().setAuthentication(null);
|
|
|
|
|
+
|
|
|
|
|
+ authenticationEntryPoint.commence(httpRequest, response, reason);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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) {
|
|
public void setThrowableAnalyzer(ThrowableAnalyzer throwableAnalyzer) {
|
|
|
this.throwableAnalyzer = throwableAnalyzer;
|
|
this.throwableAnalyzer = throwableAnalyzer;
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
public int getOrder() {
|
|
public int getOrder() {
|
|
|
return FilterChainOrder.EXCEPTION_TRANSLATION_FILTER;
|
|
return FilterChainOrder.EXCEPTION_TRANSLATION_FILTER;
|