Reputation: 1675
For example, I have :
<intercept-url pattern="/aaa/**" access="ROLE_AAA" />
<intercept-url pattern="/bbb/**" access="ROLE_BBB" />
And, accordingly, if user wanted page /aaa and at the same time had no role ROLE_AAA - I want make him redirect to page /access-denied-aaa/
And, if he was trying to get /bbb and had no ROLE_BBB -> to page /access-denied-bbb/.
Currently I can describe only one AccessDeniedHandler, with a one common accessDenied page...
How it is normally can be implemented? preferably using the Spring Security
Upvotes: 3
Views: 2093
Reputation: 10580
You can always implement your own AccessDeniedHandler
. Here's an example I made by extending the default AccessDeniedHandlerImpl
(package and imports omitted):
public class PageByResourceAccessDeniedHandler extends AccessDeniedHandlerImpl {
//~ Instance fields ===================================
/**A Map of Path by Error Page*/
private Map<String, String> errorPagesByPaths;
/**The default error page if any of {@link #errorPagesByRole} matches */
private String defaultErrorPage;
//~ Main Methods ======================================
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException,
ServletException {
String errorPage = determineErrorPage(request);
setErrorPage(errorPage);
super.handle(request, response, accessDeniedException);
}
/**
* Searches into the property {@link #errorPagesByRole} for a matching error page
* for the current denied request path.
* @param request current request
* @return a matching error page found in {@link #errorPagesByRole} or {@link #defaultErrorPage}
* if none was found.
*/
private String determineErrorPage(HttpServletRequest request) {
AntPathMatcher apm = new AntPathMatcher();
for (String key : errorPagesByPaths.keySet()) {
if(apm.match(key, request.getServletPath())) {
return errorPagesByPaths.get(key);
}
}
return defaultErrorPage;
}
//~ Getters/Setters/Utilities =========================
public void setErrorPagesByPaths(Map<String, String> errorPagesByPaths) {
for (String key : errorPagesByPaths.keySet()) {
validateErrorPage(errorPagesByPaths.get(key));
}
this.errorPagesByPaths = errorPagesByPaths;
}
public void setDefaultErrorPage(String errorPage) {
validateErrorPage(errorPage);
this.defaultErrorPage = errorPage;
}
/**
* Simple validator based on {@link AccessDeniedHandlerImpl#setErrorPage(String)}
* code. Just verifies if the page doesn't starts with <tt>/</tt>.
*
* @throws IllegalArgumentException if the errorPage doesn't starts with <tt>/</tt>.
*/
private void validateErrorPage(String errorPage) {
if ((errorPage != null) && !errorPage.startsWith("/")) {
throw new IllegalArgumentException("errorPage " + errorPage + " must begin with '/'");
}
}
}
Here's how you configure it in your application context configuration file:
<bean id="pbrADH" class="com.foo.PageByResourceAccessDeniedHandler">
<property name="defaultErrorPage" value="/errorpagedefault.jsp" />
<property name="errorPagesByPaths">
<map>
<entry key="/aaa/**" value="/errorpageaaa.jsp" />
<entry key="/bbb/**" value="/errorpagebbb.jsp" />
</map>
</property>
</bean>
Don't forget to tell Spring Security to use it, in your http
section:
...
<http auto-config="true">
<access-denied-handler ref="pbrADH" />
...
</http>
First of all, remember: this is just an idea. It works, but it can be improved.
Basically it has a Map in which the keys are the protected resources, and the values are their error pages. And it has a default one.
It uses an AntPathMatcher
to determine the error page for the current request so you can normally use ant paths. After it decides what is the error page, the class just call the handle
method of it's superclass (AccessDeniedHandlerImpl
).
A better way to configure it would be something like:
<property name="errorPagesByRoles">
<map>
<entry key="ROLE_AAA" value="/errorpageaaa.jsp" />
<entry key="ROLE_BBB" value="/errorpagebbb.jsp" />
</map>
</property>
But I couldn't find a way to know what was the role the user didn't had that caused the error. If you look at the RoleVoter
code, you can see that this information is lost.
Upvotes: 7