Spring security works with access="ROLE_USER" but not with EL - spring

I'm learning to use Spring Security and I've integrated it in a web application. I'm using Spring and Spring Security version 3.1.2.
If I specify access="ROLE_USER" in the security configuration, the authentication, works correctly, that is I first receive a 401 and after logging in, I'm able to access the resources.
<http>
<http-basic />
<logout />
<intercept-url
pattern="/exports/**"
access="ROLE_USER" />
</http>
However, if I switch to EL, my check doesn't work anymore:
<http use-expressions="true">
<http-basic />
<logout />
<intercept-url
pattern="/exports/**"
access="hasRole('USER')" />
</http>
I thought that those two configuration were equivalent, but the second one is not authorising to view the resource (403 error).
Looking at the logs:
DEBUG org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#844f42c6: Principal: org.springframework.security.core.userdetails.User#c052d588: Username: test; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_USER
DEBUG org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#1a15597, returned: -1
DEBUG org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is not anonymous); delegating to AccessDeniedHandler
If I understand right, the WebExpressionVoter is voting against me, despite the authentication worked.
What am I missing?

The solution was simple: just add ROLE_ also in hasRole(), such as:
access="hasRole('ROLE_USER')"
I got mislead by an example in section 16.2 of the manual.

Related

Spring doesn't Reject Protected Url access

I have implemented spring security it was working fine but suddenly it stopped working.
Now when I try to access a protected url it doesn't reject, it allows me to open url.
when I try to login with valid user it does call loginSuccessHandler but doesn't redirect to secure/applications.html page.
Sorry it is bit lengthy detail, I am really stuck :(.
Spring Logging
20:41:04,555 DEBUG [org.springframework.security.web.util.AntPathRequestMatcher] - Checking match of request : '/j_spring_security_check'; against '/resources/**'
20:41:04,556 DEBUG [org.springframework.security.web.FilterChainProxy] - /j_spring_security_check at position 1 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
20:41:04,556 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] - No HttpSession currently exists
20:41:04,556 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] - No SecurityContext was available from the HttpSession: null. A new one will be created.
20:41:04,556 DEBUG [org.springframework.security.web.FilterChainProxy] - /j_spring_security_check at position 2 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
20:41:04,556 DEBUG [org.springframework.security.web.FilterChainProxy] - /j_spring_security_check at position 3 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
20:41:04,556 DEBUG [org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter] - Request is to process authentication
20:41:04,557 DEBUG [org.springframework.security.authentication.ProviderManager] - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
Hibernate: select this_.id as id1_12_1_, this_.date as date2_12_1_, this_.email as email3_12_1_, this_.firstName as firstNam4_12_1_, this_.lastName as lastName5_12_1_, this_.password as password6_12_1_, this_.phoneNumber as phoneNum7_12_1_, this_.role as role8_12_1_, registerst2_.id as id1_11_0_, registerst2_.active as active2_11_0_, registerst2_.sessionExpiry as sessionE3_11_0_, registerst2_.sessionString as sessionS4_11_0_ from User this_ left outer join RegisterStatus registerst2_ on this_.id=registerst2_.id where this_.email=?
20:41:07,736 WARN [org.springframework.security.authentication.event.LoggerListener] - Authentication event AuthenticationSuccessEvent: yahya.arshad#gmail.com; details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffff8868: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: null
20:41:07,737 DEBUG [org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter] - Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#48f0accc: Principal: com.fyp.ptma.security.AuthorisedUser#48f0db60: Username: yahya.arshad#gmail.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffff8868: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: null; Not granted any authorities
20:41:07,737 DEBUG [org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices] - Did not send remember-me cookie (principal did not set parameter '_spring_security_remember_me')
20:41:07,737 DEBUG [org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices] - Remember-me login not requested.
20:41:07,737 WARN [org.springframework.security.authentication.event.LoggerListener] - Authentication event InteractiveAuthenticationSuccessEvent: yahya.arshad#gmail.com; details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffff8868: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: null
20:41:09,038 DEBUG [org.springframework.security.web.session.HttpSessionEventPublisher] - Publishing event: org.springframework.security.web.session.HttpSessionCreatedEvent[source=org.mortbay.jetty.servlet.HashSessionManager$Session:ap7q19yq3oul#1156618876]
20:41:09,039 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] - SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl#48f0accc: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#48f0accc: Principal: com.fyp.ptma.security.AuthorisedUser#48f0db60: Username: yahya.arshad#gmail.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffff8868: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: null; Not granted any authorities'
20:41:09,041 DEBUG [org.springframework.security.web.context.SecurityContextPersistenceFilter] - SecurityContextHolder now cleared, as request processing completed
20:41:09,047 DEBUG [org.springframework.security.web.util.AntPathRequestMatcher] - Checking match of request : '/secure/applications.html'; against '/resources/**'
20:41:09,047 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 1 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
20:41:09,047 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] - Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl#48f0accc: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#48f0accc: Principal: com.fyp.ptma.security.AuthorisedUser#48f0db60: Username: yahya.arshad#gmail.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffff8868: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: null; Not granted any authorities'
20:41:09,047 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 2 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 3 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 4 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 5 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 6 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 7 of 11 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
20:41:09,048 DEBUG [org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter] - SecurityContextHolder not populated with remember-me token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken#48f0accc: Principal: com.fyp.ptma.security.AuthorisedUser#48f0db60: Username: yahya.arshad#gmail.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffff8868: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: null; Not granted any authorities'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
20:41:09,048 DEBUG [org.springframework.security.web.authentication.AnonymousAuthenticationFilter] - SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken#48f0accc: Principal: com.fyp.ptma.security.AuthorisedUser#48f0db60: Username: yahya.arshad#gmail.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffff8868: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: null; Not granted any authorities'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
20:41:09,048 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
20:41:09,049 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] - Secure object: FilterInvocation: URL: /secure/applications.html; Attributes: [permitAll]
20:41:09,049 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#48f0accc: Principal: com.fyp.ptma.security.AuthorisedUser#48f0db60: Username: yahya.arshad#gmail.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffff8868: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: null; Not granted any authorities
20:41:09,049 DEBUG [org.springframework.security.access.vote.AffirmativeBased] - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#3aa8c31b, returned: 1
20:41:09,049 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] - Authorization successful
20:41:09,049 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] - RunAsManager did not change Authentication object
20:41:09,049 DEBUG [org.springframework.security.web.FilterChainProxy] - /secure/applications.html reached end of additional filter chain; proceeding with original chain
20:41:09,053 DEBUG [org.springframework.security.web.access.ExceptionTranslationFilter] - Chain processed normally
20:41:09,053 DEBUG [org.springframework.security.web.context.SecurityContextPersistenceFilter] - SecurityContextHolder now cleared, as request processing completed
LoginSuccessHandler.java
public class LoginSuccessHandler implements AuthenticationSuccessHandler,
AuthenticationFailureHandler {
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication auth)
throws IOException, ServletException {
request.getSession(true).setAttribute("user", auth.getPrincipal());
request.setAttribute("user", auth.getPrincipal());
**response.sendRedirect("secure/applications.html");** // it does't redirect
}
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException auth)
throws IOException, ServletException {
// TODO Auto-generated method stub
request.getSession(true).setAttribute("error", auth.getMessage());
request.setAttribute("error", auth.getMessage());
response.sendRedirect("login.html?error=true");
}
}
security-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="com.fyp.ptma" />
<beans:bean id="loggerListener"
class="org.springframework.security.authentication.event.LoggerListener" />
<global-method-security pre-post-annotations="enabled" />
<http pattern="/resources/**" security="none" />
<http use-expressions="true" auto-config="true">
<intercept-url pattern="/**" access="permitAll" />
<intercept-url pattern="/secure/*" access="isAuthenticated()" />
<intercept-url pattern="/j_spring_security_check"
access="permitAll" />
<intercept-url pattern="/processRegistration.html"
access="permitAll" />
<form-login login-page="/login.html" default-target-url="/secure/fileUpload.html"
authentication-failure-url="/login.html?error=true"
login-processing-url="/j_spring_security_check"
authentication-success-handler-ref="loginSuccessHandler"
/>
<logout />
<remember-me user-service-ref="userDetailsService" />
<access-denied-handler error-page="/login.html?error=true" />
</http>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="plaintext" />
</authentication-provider>
</authentication-manager>
<beans:bean id="userDetailsService"
class="com.fyp.ptma.dao.service.UserDetailsServiceImpl">
</beans:bean>
<beans:bean id="loginSuccessHandler" class="com.fyp.ptma.security.LoginSuccessHandler" />
servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.fyp.ptma.controllers" />
</beans:beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/log4j.properties</param-value>
</context-param>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/spring/appServlet/security-context.xml
/WEB-INF/hibernate/hibernate-config.xml
</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>120</session-timeout>
</session-config>
</web-app>
You should move
<intercept-url pattern="/**" access="permitAll" />
after
<intercept-url pattern="/secure/*" access="isAuthenticated()" />
in your security-context.xml

Spring custom AuthenticationFailureHandler

I already try the whole day, to get my custom authentication failure handler to work with Spring 3.1.3.
I think it is properly configured
<http use-expressions="true" disable-url-rewriting="true">
<intercept-url pattern="/rest/login" access="permitAll" />
<intercept-url pattern="/rest/**" access="isAuthenticated()" />
<intercept-url pattern="/index.html" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />
<intercept-url pattern="/**" access="denyAll" />
<form-login username-parameter="user" password-parameter="pass" login-page="/rest/login"
authentication-failure-handler-ref="authenticationFailureHandler" />
</http>
<beans:bean id="authenticationFailureHandler" class="LoginFailureHandler" />
My implementation is this
public class LoginFailureHandler implements AuthenticationFailureHandler {
private static final Logger log = LoggerFactory.getLogger(LoginFailureHandler.class);
public LoginFailureHandler() {
log.debug("I am");
}
#Autowired
private ObjectMapper customObjectMapper;
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
log.debug("invalid login");
User user = new User();
user.setUsername("invalid");
try (OutputStream out = response.getOutputStream()) {
customObjectMapper.writeValue(out, user);
}
}
}
In the console I see
2013-04-11 14:52:29,478 DEBUG LoginFailureHandler - I am
So it is loaded.
With wrong username or passwort, when a BadCredentialsException is thrown, I don't see invalid login.
The Method onAuthenticationFailure is never invoked.
Instead the service redirects the browser onto /rest/login again and again...
Edit
2013-04-11 15:47:26,411 DEBUG de.pentos.spring.LoginController - Incomming login chuck.norris, norris
2013-04-11 15:47:26,412 DEBUG o.s.s.a.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2013-04-11 15:47:26,415 DEBUG o.s.s.a.d.DaoAuthenticationProvider - Authentication failed: password does not match stored value
2013-04-11 15:47:26,416 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public de.pentos.spring.User de.pentos.spring.LoginController.login(de.pentos.spring.User)]: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2013-04-11 15:47:26,419 DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [public de.pentos.spring.User de.pentos.spring.LoginController.login(de.pentos.spring.User)]: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2013-04-11 15:47:26,419 DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [public de.pentos.spring.User de.pentos.spring.LoginController.login(de.pentos.spring.User)]: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2013-04-11 15:47:26,426 DEBUG o.s.web.servlet.DispatcherServlet - Could not complete request
org.springframework.security.authentication.BadCredentialsException: Bad credentials
This happens in DEBUG Mode
Where is my mistake?
Judged from the logs you attached I think you've made a mistake in implementing the login process. I cannot be absolutely sure, but I guess you call ProviderManager.authenticate() in your LoginController. The method throws a BadCredentialsException that causes Spring MVC's exception handling mechanism to kick in, which of course has no knowledge about the AuthenticationFailureHandler configured for Spring Security.
From the login controller you should normally just serve a simple login form with action="j_spring_security_check" method="post". When the user submits that form, the configured security filter (namely UsernamePasswordAuthenticationFilter) intercepts that request and handles authentication. You don't have to implement that logic yourself in a controller method.
Reply to your comment:
You do use ProviderManager (it's the implementation of the autowired AuthenticationManager interface). The mistake you make is that you try to rewrite the logic already implemented and thoroughly tested in auth filters. This is bad in itself, but even that is done in a wrong way. You select just a few lines from a complex logic, and among other things you forget e.g. invoking the session strategy (to prevent session fixation attacks, and handling concurrent sessions). The original implementation invokes the AuthenticationFailureHandler
as well, which you also forgot in your method, this is the very reason of the problem your original question is about.
So you end up with an untested, brittle solution instead of nicely integrating with the framework to leverage its roboustness and full capacity. As I said, the config you posted in your answer is a definite improvement, because it uses the framework provided filter for authentication. Keep that config and remove LoginController.login(), it won't be called anyway by requests sent to /rest/login.
A more fundamental question is if it's really a good solution to use sessions and form-based login mechanism if you implement RESTful services. (On form-based login I mean that the client sends its credentials once in whatever format, and then gets authenticated by a stateful session on subsequent requests.) With REST services it's more prevalent to keep everything stateless, and re-authenticate each new request by information carried by http headers.
It's a problem with the order in the security-app-context.xml.
If I first define all my beans and then all the rest it works.
I tried a lot, so don't wonder, that it now looks a little different then in the question
<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/rest/login" />
</beans:bean>
<beans:bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="filterProcessesUrl" value="/rest/login" />
<beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
<beans:property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
</beans:bean>
<beans:bean id="authenticationSuccessHandler" class="de.pentos.spring.LoginSuccessHandler" />
<beans:bean id="authenticationFailureHandler" class="de.pentos.spring.LoginFailureHandler" />
<http use-expressions="true" disable-url-rewriting="true" entry-point-ref="authenticationProcessingFilterEntryPoint"
create-session="ifRequired">
<intercept-url pattern="/rest/login" access="permitAll" />
<intercept-url pattern="/rest/**" access="isAuthenticated()" />
<intercept-url pattern="/index.html" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />
<intercept-url pattern="/**" access="denyAll" />
<custom-filter position="FORM_LOGIN_FILTER" ref="authenticationFilter" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="chuck.norris" password="cnorris" authorities="ROLE_ADMIN" />
<user name="user" password="user" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
Does not look bad to me. Did you try to use the debug mode of your IDE ?
Did you see things like this in your logs :
Authentication request failed: ...
Updated SecurityContextHolder to contain null Authentication
Delegating to authentication failure handler ...
The AuthenticationFailureHandler will be called automatically, only if the authentication is done in one of the authentication filter : UsernamePasswordAuthenticationFilter normally in your case.
(Looking at your requirements), You don't need a custom AuthenticationFailureHandler as the with default SimpleUrlAuthenticationFailureHandler of Spring and properly implementing AuthenticationProvider should serve the purpose.
<form-login login-page="/login" login-processing-url="/do/login" authentication- failure-url ="/login?authfailed=true" authentication-success-handler-ref ="customAuthenticationSuccessHandler"/>
If you have handled the Exceptions well in Authentication Provider:
Sample Logic:
String loginUsername = (String) authentication.getPrincipal();
if (loginUsername == null)
throw new UsernameNotFoundException("User not found");
String loginPassword = (String) authentication.getCredentials();
User user = getUserByUsername(loginUsername);
UserPassword password = getPassword(user.getId());
if (!password.matches(loginPassword)) {
throw new BadCredentialsException("Invalid password.");
}
If we want the exceptions thrown to be reflected at the client interface, add the following scriplet on the JSP responding to authentication-failure-url="/login?authfailed=true"
<%
Exception error = (Exception) request.getSession().getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
if (error != null)
out.write(error.getMessage());
%>

How to show custom access denied page in Spring security for method annotations [duplicate]

i have a method secured with spring security as follows:
#PreAuthorize("hasRole('add_user')")
public void addUser(User user) ;
and if a user with no enoguh permissions is trying to invoke it
, an accessDenied exception is thrown:
org.springframework.security.access.AccessDeniedException: Access is denied
this is what's expected, but the question is, why the defined access-denied-handler
in security.xml configuration file is not working:
<access-denied-handler error-page="accessDenied"/>
I mean by not working that when user with not enough permission when trying to press the button addUser which will invoke the service addUser (that's only accessible by user has this permission) an AccessDenied Exception is thrown and that's the desired behavior, but the user isn't redirected to the access denied exception as configured in xml.
shouldn't the user gets redirected automatically to access denied page when this exception is thrown, or i have to define such behavior explicitly in code ?
please advise.
I am using Spring Security 3.0.5 with JSF 2.1 and ICEFaces 2
UPDATE: applicationSecurity.xml:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.4.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<!-- Enable #pre, #post spring security method level annotations -->
<global-method-security pre-post-annotations="enabled" />
<http use-expressions="true" auto-config="true" access-denied-page="/accessDenied">
<session-management session-fixation-protection="none"/>
<remember-me token-validity-seconds="1209600"/>
<intercept-url pattern="/accessDenied" access="permitAll"/>
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/j_spring_security_check" access="permitAll" />
<intercept-url pattern="/faces/javax.faces.resource/**" access="permitAll" />
<intercept-url pattern="/xmlhttp/**" access="permitAll" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/scripts/**" access="permitAll" />
<intercept-url pattern="/images/**" access="permitAll" />
<intercept-url pattern="/css/**" access="permitAll" />
<!-- All pages requires authentication (not anonymous user) -->
<intercept-url pattern="/**" access="isAuthenticated()" />
<intercept-url pattern="/faces/**" access="isAuthenticated()" />
<form-login default-target-url="/"
always-use-default-target="true"
login-processing-url="/j_spring_security_check"
login-page="/login"
authentication-failure-url="/login?login_error=1"
/>
<logout logout-url="/logout" logout-success-url="/login" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsServiceImpl"/>
</authentication-manager>
</beans:beans>
UPDATE 2 : debugs before exception:
DEBUG [http-bio-8080-exec-1] (PrePostAnnotationSecurityMetadataSource.java:93) - #org.springframework.security.access.prepost.PreAuthorize(value=hasRole('add_user')) found on specific method: public void com.myapp.service.impl.UserServiceImpl.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException
DEBUG [http-bio-8080-exec-1] (DelegatingMethodSecurityMetadataSource.java:66) - Adding security method [CacheKey[com.myapp.service.impl.UserServiceImpl; public abstract void com.myapp.service.UserService.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException]] with attributes [[authorize: 'hasRole('add_user')', filter: 'null', filterTarget: 'null']]
DEBUG [http-bio-8080-exec-1] (AbstractSecurityInterceptor.java:191) - Secure object: ReflectiveMethodInvocation: public abstract void com.myapp.service.UserService.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException; target is of class [com.myapp.service.impl.UserServiceImpl]; Attributes: [[authorize: 'hasRole('add_user')', filter: 'null', filterTarget: 'null']]
DEBUG [http-bio-8080-exec-1] (AbstractSecurityInterceptor.java:292) - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#c650d918: Principal: org.springframework.security.core.userdetails.User#db344023: Username: user#mycomp.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: access_viewUsers; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: E6BBAC0CD4499B1455227DC6035CC882; Granted Authorities: access_viewUsers
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter#1d1e082e, returned: -1
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.vote.RoleVoter#1eab12f1, returned: 0
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.vote.AuthenticatedVoter#71689bf1, returned: 0
According to the spring Security documentation the user of the access-denied-page attribute of the element has been deprecated in Spring 3.0 and above.
We do the following in our app:
Create a custom access denied handler by extending the Spring Security framework's AccessDeniedHandlerImpl.
Call the setErrorPage method, passing in the name of the controller that will display your access denied page
In our case we lock the user's account in the custom handler - there's no good reason for any user to ever get an access denied exception unless they're doing something that they should't. We also log what they were trying to access, etc.
Call super.handle(_request, _response, _exception); at the end of the handler. Spring will forward control to the controller listed in #2 above.
public class AccessDeniedHandlerApp extends AccessDeniedHandlerImpl {
private static Logger logger = Logger.getLogger(AccessDeniedHandlerApp.class);
private static final String LOG_TEMPLATE = "AccessDeniedHandlerApp: User attempted to access a resource for which they do not have permission. User %s attempted to access %s";
#Override
public void handle(HttpServletRequest _request, HttpServletResponse _response, AccessDeniedException _exception) throws IOException, ServletException {
setErrorPage("/securityAccessDenied"); // this is a standard Spring MVC Controller
// any time a user tries to access a part of the application that they do not have rights to lock their account
<custom code to lock the account>
super.handle(_request, _response, _exception);
}
}
Here's my XML: AccessDeniedHandlerApp extends 'AccessDeniedHandlerImpl`
<http auto-config='true'>
<intercept-url pattern="/views/**" access="ROLE_USER" />
<form-login login-page="/Login.jsp" authentication-success-handler-ref="loginSuccessFilter"
authentication-failure-handler-ref="loginFailureFilter" />
<logout logout-success-url="/home" />
<access-denied-handler ref="customAccessDeniedHandler"/>
</http>
<beans:bean id="customAccessDeniedHandler" class="org.demo.security.AccessDeniedHandlerApp"/>
Here's my Access Denied Controller - I should have posted this earlier - sorry about that. In order to get the access denied page to come up I had to use a redirect:
#Controller
public class AccessDeniedController {
private static Logger logger = Logger.getLogger(AccessDeniedController.class);
#RequestMapping(value = "/securityAccessDenied")
public String processAccessDeniedException(){
logger.info("Access Denied Handler");
return "redirect:/securityAccessDeniedView";
}
#RequestMapping(value = "/securityAccessDeniedView")
public String displayAccessDeniedView(){
logger.info("Access Denied View");
return "/SecurityAccessDenied";
}
Please let me know if this doesn't resolve it and I'll keep digging - I just tested it again locally here and this should do the trick.
}
I have run into the same problem myself and posted another question relating to the same issue. After several hours of digging around, I finally found the solution for my issue. I know this question is 2+ years old, but thought I would update it with information in case it was of value to someone else.
In a nutshell, I noticed that the SimpleMappingExceptionResolver was handling the exception and resolving it with a default mapping. Consequently, there was no exception left to bubble up the stack to the ExceptionTranslationFilter which would redirect to the access-denied-handler.
Please see Spring Security ignoring access-denied-handler with Method Level Security for further information.
Spring Security redirect to the access denied page just when the user don't have authorization to access the resource. This is, when the user is authenticated but doesn't have the allowed roles.
But when the problem is not authorization, but authentication, Spring Security redirects to the login page (to let the user authenticate himself/herself), not to the access denied page.
As you have a rule checking for "isAuthenticated()" in the rules, you won't be redirected to the access denied page, but to the login page.
Hope it helps.

Spring security for Authorization only

I'm working in a new project where I need to manage user roles, just that, because this application is using its own authentication system through web services. So, the only thing I need to do is validate each user role, if the user id exists in my database he could enter otherwise will be rejected. It means, no password, no login page (that is performed by the web services), just user id validation and get their roles.
I did a proof of concept in order to implement this according to my understanding, this was after reading the documentation involved. The point is that this is not working as expected, let me share this example with you:
Security settings
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:http use-expressions="true" entry-point-ref="http403ForbiddenEntryPoint">
<security:anonymous enabled="false" />
<security:intercept-url pattern="index.jsp" access="permitAll" />
<security:intercept-url pattern="home.html" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="excel.html" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />
<security:intercept-url pattern="denied.jsp" access="permitAll" />
<security:custom-filter position="PRE_AUTH_FILTER" ref="meivFilter" />
</security:http>
<bean id="http403ForbiddenEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />
<bean id="meivFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="Host" />
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.
preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="userDetailsService" />
</bean>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
<bean id="userDetailsService" class="com.gm.gpsc.meiv.security.UserServiceImpl" />
</beans>
Note:In order to test it, as you can see, I use in RequestHeaderAuthenticationFilter as principalRequestHeader attribute, the Host header value which is present as default header value. Just for testing. Because in the future I going to read a value from the header.
This is the userDetailService I did in order to get user roles.
public class UserServiceImpl implements UserDetailsService {
private boolean accountNonExpired = true;
private boolean accountNonLocked = true;
private boolean credentialsNonExpired = true;
private boolean enabled = true;
#Override
public UserDetails loadUserByUsername(String userId)
throws UsernameNotFoundException {
GrantedAuthority[] grantedAuthority = new GrantedAuthority[1];
grantedAuthority[0] = new GrantedAuthorityImpl("ROLE_USER");
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(grantedAuthority[0]);
return new User(userId, "x",enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
}
When I run this example the settings are loaded successfully, then when I try to reach the first pattern, home.html, I can reach that page, which is wrong, because according to my rule I have ROLE_USER and as you can see above I need ROLE_ADMIN.
For the second pattern I could get access too but in that case it makes sense because ROLE_USER is include as rule.
For the first situation I change home.html for /home.html and I got 403 Access denied. But If I use the same pattern for the second option excel, it means /excel.html, I got the same error (403).
This is the error I get from the log file:
DEBUG | 2012-09-30 03:55:09,738 | FilterChainProxy | /index.jsp at position 1 of 7 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
DEBUG | 2012-09-30 03:55:09,739 | HttpSessionSecurityContextRepository | No HttpSession currently exists
DEBUG | 2012-09-30 03:55:09,739 | HttpSessionSecurityContextRepository | No SecurityContext was available from the HttpSession: null. A new one will be created.
DEBUG | 2012-09-30 03:55:09,744 | FilterChainProxy | /index.jsp at position 2 of 7 in additional filter chain; firing Filter: 'RequestHeaderAuthenticationFilter'
DEBUG | 2012-09-30 03:55:09,744 | RequestHeaderAuthenticationFilter | Checking secure context token: null
DEBUG | 2012-09-30 03:55:09,744 | HttpSessionSecurityContextRepository | SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
DEBUG | 2012-09-30 03:55:09,744 | SecurityContextPersistenceFilter | SecurityContextHolder now cleared, as request processing completed
30/09/2012 03:55:09 org.apache.catalina.core.StandardWrapperValve invoke
GRAVE: Servlet.service() para servlet jsp lanzó excepción
org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException: MYID header not found in request.
I don't understand what's going on here, maybe someone can explain me where I'm wrong.
I'd appreciate your help because I am so confused with this and tired of reading the documentation without getting anywhere.

Handling AccessDenied with Method Level Security

i have a method secured with spring security as follows:
#PreAuthorize("hasRole('add_user')")
public void addUser(User user) ;
and if a user with no enoguh permissions is trying to invoke it
, an accessDenied exception is thrown:
org.springframework.security.access.AccessDeniedException: Access is denied
this is what's expected, but the question is, why the defined access-denied-handler
in security.xml configuration file is not working:
<access-denied-handler error-page="accessDenied"/>
I mean by not working that when user with not enough permission when trying to press the button addUser which will invoke the service addUser (that's only accessible by user has this permission) an AccessDenied Exception is thrown and that's the desired behavior, but the user isn't redirected to the access denied exception as configured in xml.
shouldn't the user gets redirected automatically to access denied page when this exception is thrown, or i have to define such behavior explicitly in code ?
please advise.
I am using Spring Security 3.0.5 with JSF 2.1 and ICEFaces 2
UPDATE: applicationSecurity.xml:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.4.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<!-- Enable #pre, #post spring security method level annotations -->
<global-method-security pre-post-annotations="enabled" />
<http use-expressions="true" auto-config="true" access-denied-page="/accessDenied">
<session-management session-fixation-protection="none"/>
<remember-me token-validity-seconds="1209600"/>
<intercept-url pattern="/accessDenied" access="permitAll"/>
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/j_spring_security_check" access="permitAll" />
<intercept-url pattern="/faces/javax.faces.resource/**" access="permitAll" />
<intercept-url pattern="/xmlhttp/**" access="permitAll" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/scripts/**" access="permitAll" />
<intercept-url pattern="/images/**" access="permitAll" />
<intercept-url pattern="/css/**" access="permitAll" />
<!-- All pages requires authentication (not anonymous user) -->
<intercept-url pattern="/**" access="isAuthenticated()" />
<intercept-url pattern="/faces/**" access="isAuthenticated()" />
<form-login default-target-url="/"
always-use-default-target="true"
login-processing-url="/j_spring_security_check"
login-page="/login"
authentication-failure-url="/login?login_error=1"
/>
<logout logout-url="/logout" logout-success-url="/login" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsServiceImpl"/>
</authentication-manager>
</beans:beans>
UPDATE 2 : debugs before exception:
DEBUG [http-bio-8080-exec-1] (PrePostAnnotationSecurityMetadataSource.java:93) - #org.springframework.security.access.prepost.PreAuthorize(value=hasRole('add_user')) found on specific method: public void com.myapp.service.impl.UserServiceImpl.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException
DEBUG [http-bio-8080-exec-1] (DelegatingMethodSecurityMetadataSource.java:66) - Adding security method [CacheKey[com.myapp.service.impl.UserServiceImpl; public abstract void com.myapp.service.UserService.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException]] with attributes [[authorize: 'hasRole('add_user')', filter: 'null', filterTarget: 'null']]
DEBUG [http-bio-8080-exec-1] (AbstractSecurityInterceptor.java:191) - Secure object: ReflectiveMethodInvocation: public abstract void com.myapp.service.UserService.addUser(com.myapp.domain.User) throws java.lang.Exception,org.springframework.security.access.AccessDeniedException; target is of class [com.myapp.service.impl.UserServiceImpl]; Attributes: [[authorize: 'hasRole('add_user')', filter: 'null', filterTarget: 'null']]
DEBUG [http-bio-8080-exec-1] (AbstractSecurityInterceptor.java:292) - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#c650d918: Principal: org.springframework.security.core.userdetails.User#db344023: Username: user#mycomp.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: access_viewUsers; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: E6BBAC0CD4499B1455227DC6035CC882; Granted Authorities: access_viewUsers
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter#1d1e082e, returned: -1
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.vote.RoleVoter#1eab12f1, returned: 0
DEBUG [http-bio-8080-exec-1] (AffirmativeBased.java:53) - Voter: org.springframework.security.access.vote.AuthenticatedVoter#71689bf1, returned: 0
According to the spring Security documentation the user of the access-denied-page attribute of the element has been deprecated in Spring 3.0 and above.
We do the following in our app:
Create a custom access denied handler by extending the Spring Security framework's AccessDeniedHandlerImpl.
Call the setErrorPage method, passing in the name of the controller that will display your access denied page
In our case we lock the user's account in the custom handler - there's no good reason for any user to ever get an access denied exception unless they're doing something that they should't. We also log what they were trying to access, etc.
Call super.handle(_request, _response, _exception); at the end of the handler. Spring will forward control to the controller listed in #2 above.
public class AccessDeniedHandlerApp extends AccessDeniedHandlerImpl {
private static Logger logger = Logger.getLogger(AccessDeniedHandlerApp.class);
private static final String LOG_TEMPLATE = "AccessDeniedHandlerApp: User attempted to access a resource for which they do not have permission. User %s attempted to access %s";
#Override
public void handle(HttpServletRequest _request, HttpServletResponse _response, AccessDeniedException _exception) throws IOException, ServletException {
setErrorPage("/securityAccessDenied"); // this is a standard Spring MVC Controller
// any time a user tries to access a part of the application that they do not have rights to lock their account
<custom code to lock the account>
super.handle(_request, _response, _exception);
}
}
Here's my XML: AccessDeniedHandlerApp extends 'AccessDeniedHandlerImpl`
<http auto-config='true'>
<intercept-url pattern="/views/**" access="ROLE_USER" />
<form-login login-page="/Login.jsp" authentication-success-handler-ref="loginSuccessFilter"
authentication-failure-handler-ref="loginFailureFilter" />
<logout logout-success-url="/home" />
<access-denied-handler ref="customAccessDeniedHandler"/>
</http>
<beans:bean id="customAccessDeniedHandler" class="org.demo.security.AccessDeniedHandlerApp"/>
Here's my Access Denied Controller - I should have posted this earlier - sorry about that. In order to get the access denied page to come up I had to use a redirect:
#Controller
public class AccessDeniedController {
private static Logger logger = Logger.getLogger(AccessDeniedController.class);
#RequestMapping(value = "/securityAccessDenied")
public String processAccessDeniedException(){
logger.info("Access Denied Handler");
return "redirect:/securityAccessDeniedView";
}
#RequestMapping(value = "/securityAccessDeniedView")
public String displayAccessDeniedView(){
logger.info("Access Denied View");
return "/SecurityAccessDenied";
}
Please let me know if this doesn't resolve it and I'll keep digging - I just tested it again locally here and this should do the trick.
}
I have run into the same problem myself and posted another question relating to the same issue. After several hours of digging around, I finally found the solution for my issue. I know this question is 2+ years old, but thought I would update it with information in case it was of value to someone else.
In a nutshell, I noticed that the SimpleMappingExceptionResolver was handling the exception and resolving it with a default mapping. Consequently, there was no exception left to bubble up the stack to the ExceptionTranslationFilter which would redirect to the access-denied-handler.
Please see Spring Security ignoring access-denied-handler with Method Level Security for further information.
Spring Security redirect to the access denied page just when the user don't have authorization to access the resource. This is, when the user is authenticated but doesn't have the allowed roles.
But when the problem is not authorization, but authentication, Spring Security redirects to the login page (to let the user authenticate himself/herself), not to the access denied page.
As you have a rule checking for "isAuthenticated()" in the rules, you won't be redirected to the access denied page, but to the login page.
Hope it helps.

Resources