Spring security for Authorization only - spring

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.

Related

Spring Security session concurrency

I have an app build with Spring 4.0 and Security 3.2 and I want to implement session concurrency but it does not seems to work. All other aspects of security are working just fine. Here are my xml configs:
first of all in my web.xml:
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
then in my security.xml
<security:http auto-config="false"
use-expressions="true"
authentication-manager-ref="authManager"
access-decision-manager-ref="webAccessDecisionManager"
entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/agent/**" access="hasAnyRole('ROLE_AGENT')" />
<security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/public/**" access="permitAll" />
<security:intercept-url pattern="/**" access="permitAll" />
<security:session-management session-authentication-strategy-ref="sas"
invalid-session-url="/public/login.xhtml"/>
<security:logout logout-success-url="/public/login.xhtml"
invalidate-session="true"
delete-cookies="true"/>
<security:expression-handler ref="webExpressionHandler"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
</security:http>
and
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg index="0" value="/public/login.xhtml" />
</bean>
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/public/login.xhtml" />
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
<constructor-arg index="0" ref="sessionRegistry"/>
<constructor-arg index="1" value="/session-expired.htm"/>
</bean>
<bean id="myAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="sessionAuthenticationStrategy" ref="sas" />
<property name="authenticationManager" ref="authManager" />
<property name="authenticationFailureHandler" ref="customAuthenticationFailureHandler"/>
</bean>
<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<property name="maximumSessions" value="1" />
<property name="exceptionIfMaximumExceeded" value="true" />
</bean>
<bean id="authManager" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="myCompLdapAuthProvider"/>
<ref bean="myCompDBAuthProvider"/>
</list>
</property>
</bean>
My UserDetails implements hashCode() an equals() and with all this the concurrent session limitation does not work. After a little debug session I have observed that my session is never found in sessionRegistry and I guess that this is the main reason, but I do not know why!?
Any idea of what I`m doing wrong here ?
P.S. I have such records in my debug logs:
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 2 of 11 in additional filter chain; firing Filter: 'ConcurrentSessionFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 3 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
(FilterChainProxy.java:337) - /resources/images/icons/connection_on.gif at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
(AnonymousAuthenticationFilter.java:107) - SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken#96cf68e: Principal: MyUserDetails [username=adrian.videanu, dn=org.springframework.ldap.core.DirContextAdapter: dn=cn=Adrian Videanu,ou=IT,ou=Organization .....
so the filters are invoked...
Updates
I can see that the session creation event is published because i have this line in logs :
(HttpSessionEventPublisher.java:66) - Publishing event: org.springframework.security.web.session.HttpSessionCreatedEvent[source=org.apache.catalina.session.StandardSessionFacade#3827a0aa]
but i never hit registerNewSession method from SessionRegistryImpl as I guess it should. Also the HttpSessionEventPublisher is invoked when I initially open the login page because I guess that`s when the session is created, but then after I enter the credentials and push submit HttpSessionEventPublisher is not invoked anymore.
Updates 2
As a test I have injected SessionRegistryImpl to one of my beans in order to try to access some of its methods:
#Named
#Scope("view")
public class UserDashboardMB implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private SessionRegistry sessionRegistry;
public void init(){
System.out.println("-- START INIT -- ");
List<Object> principals = sessionRegistry.getAllPrincipals();
System.out.println("Principals = "+principals);
for (Object p:principals){
System.out.println("Principal = "+p);
}
System.out.println("-- STOP INIT -- ");
}
}
and the output is :
INFO: -- START INIT --
INFO: Principals = []
INFO: -- STOP INIT --
so, nothing is populated there.
Update 3
I have replaced "sas" bean with the one provided by Serge but it still doesn`t seems to work. I have neabled the debugger again and I thing that the problem is that on class UsernamePasswordAuthenticationFilter at method doFilter() none of my requests are processed as it should.Here is a part of doFilter():
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
// rest of method here
}
From what I see in the debugger my requests doesn't seems to require auth and chain.doFilter(request, response); is invoked.
Update 4
I guess i found the issue. The filter is not running as it should because the filterUrl param is not the proper one. As i read in the docs:
This filter by default responds to the URL /j_spring_security_check.
but my login part is implemented with JSF managed beans and actions. Now, my login form is at /public/login.xhtml and the url that the login info are posted is the same. If I set this as filterUrl I have problems because is called at the intial form rendering also and I have a infinite loop there as no user/password are set up.
Any idea how to overcome that ?
This is how my LoginManagedBean looks like:
#Named
#Scope("request")
public class LoginMB implements Serializable {
private static final long serialVersionUID = 1L;
#Autowired
#Qualifier("authManager")
private AuthenticationManager authenticationManager;
// setters and getters
public String login(){
FacesContext context = FacesContext.getCurrentInstance();
try {
Authentication request = new UsernamePasswordAuthenticationToken(this.getUsername(), this.getPassword());
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
// perform some extra logic here and return protected page
return "/agent/dashboard.xhtml?faces-redirect=true";
} catch (AuthenticationException e) {
e.printStackTrace();
logger.error("Auth Exception ->"+e.getMessage());
FacesMessage fm = new FacesMessage("Invalid user/password");
fm.setSeverity(FacesMessage.SEVERITY_ERROR);
context.addMessage(null, fm);
}
return null;
}
}
There is a slight difference between spring security 3.1 and spring security 3.2 concerning concurrency session management.
The old ConcurrentSessionControlStrategy is now deprecated. It checked if the number of concurrent sessions was exceeded and registered sessions in a SessionRegistry for future use.
It has been partially replaced in 3.2 by ConcurrentSessionControlAuthenticationStrategy. It effectively controls if the number of concurrent sessions is exceeded but no longer registers new sessions (even if javadoc pretends to : I looked in the source code to understand it!)
The registration of sessions is now delegated to RegisterSessionAuthenticationStrategy! So for session concurrency to work, you have to use both. And the example in 3.2 reference manual, effectively uses for bean sas a CompositeSessionAuthenticationStrategy containing a ConcurrentSessionControlAuthenticationStrategy, a SessionFixationProtectionStrategy and a RegisterSessionAuthenticationStrategy!
For the whole thing to work, you just have to replace your sas bean with:
<bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<constructor-arg>
<list>
<bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg ref="sessionRegistry"/>
<property name="maximumSessions" value="1" />
<property name="exceptionIfMaximumExceeded" value="true" />
</bean>
<bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
</bean>
<bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<constructor-arg ref="sessionRegistry"/>
</bean>
</list>
</constructor-arg>
</bean>
I have finally managed to fix the issue. The problem was due to my mix setup between spring standard filters and custom jsf login form. I have left in my xml conf only the "sas" bean as Serge pointed out and the in my LoginMB I have manually and programmatically invoked the SessionAuthenticationStrategy onAuthentication() method. So now my LoginMB looks like:
#Named
#Scope("request")
public class LoginMB implements Serializable {
#Autowired
#Qualifier("authManager")
private AuthenticationManager authenticationManager;
#Inject
#Qualifier("sas")
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
public String login(){
FacesContext context = FacesContext.getCurrentInstance();
try {
Authentication authRequest = new UsernamePasswordAuthenticationToken(this.getUsername(), this.getPassword());
Authentication result = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(result);
HttpServletRequest httpReq = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
HttpServletResponse httpResp = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
sessionAuthenticationStrategy.onAuthentication(result, httpReq, httpResp);
// custom logic here
return "/agent/dashboard.xhtml?faces-redirect=true";
}catch(SessionAuthenticationException sae){
sae.printStackTrace();
logger.error("Auth Exception ->"+sae.getMessage());
String userMessage = "Session auth exception!";
if (sae.getMessage().compareTo("Maximum sessions of 1 for this principal exceeded") == 0){
userMessage = "Cannot login from more than 1 location.";
}
FacesMessage fm = new FacesMessage(userMessage);
fm.setSeverity(FacesMessage.SEVERITY_FATAL);
context.addMessage(null, fm);
}
catch (AuthenticationException e) {
e.printStackTrace();
logger.error("Auth Exception ->"+e.getMessage());
FacesMessage fm = new FacesMessage("Invalid user/password");
fm.setSeverity(FacesMessage.SEVERITY_FATAL);
context.addMessage(null, fm);
}
return null;
}
Now the sessions are registered and session limitation is working.

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.

Creating New Roles and Permissions Dynamically in Spring Security 3

I am using Spring Security 3 in Struts 2 + Spring IOC project.
I have used Custom Filter, Authentication Provider etc. in my Project.
You can see my security.xml here
<?xml version="1.0" encoding="UTF-8"?>
<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:p="http://www.springframework.org/schema/p"
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">
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>
<beans:bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" >
<beans:property name="permissionEvaluator" ref="customPermissionEvaluator" />
</beans:bean>
<beans:bean class="code.permission.MyCustomPermissionEvaluator" id="customPermissionEvaluator" />
<!-- User Login -->
<http auto-config="true" use-expressions="true" pattern="/user/*" >
<intercept-url pattern="/index.jsp" access="permitAll"/>
<intercept-url pattern="/user/showLoginPage.action" access="permitAll"/>
<intercept-url pattern="/user/showFirstPage" access="hasRole('ROLE_USER') or hasRole('ROLE_VISIT')"/>
<intercept-url pattern="/user/showSecondUserPage" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/user/showThirdUserPage" access="hasRole('ROLE_VISIT')"/>
<intercept-url pattern="/user/showFirstPage" access="hasRole('ROLE_USER') or hasRole('ROLE_VISIT')"/>
<form-login login-page="/user/showLoginPage.action" />
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/user/j_spring_security_logout"/>
<access-denied-handler ref="myAccessDeniedHandler" />
<custom-filter before="FORM_LOGIN_FILTER" ref="myApplicationFilter"/>
</http>
<beans:bean id="myAccessDeniedHandler" class="code.security.MyAccessDeniedHandler" />
<beans:bean id="myApplicationFilter" class="code.security.MyApplicationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationFailureHandler" ref="failureHandler"/>
<beans:property name="authenticationSuccessHandler" ref="successHandler"/>
</beans:bean>
<beans:bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/user/showFirstPage"> </beans:property>
</beans:bean>
<beans:bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/user/showLoginPage.action?login_error=1"/>
</beans:bean>
<beans:bean id= "myUserDetailServiceImpl" class="code.security.MyUserDetailServiceImpl">
</beans:bean>
<beans:bean id="myAuthenticationProvider" class="code.security.MyAuthenticationProvider">
<beans:property name="userDetailsService" ref="myUserDetailServiceImpl"/>
</beans:bean>
<!-- User Login Ends -->
<!-- Admin Login -->
<http auto-config="true" use-expressions="true" pattern="/admin/*" >
<intercept-url pattern="/index.jsp" access="permitAll"/>
<intercept-url pattern="/admin/showSecondLogin" access="permitAll"/>
<intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')"/>
<form-login login-page="/admin/showSecondLogin"/>
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/admin/j_spring_security_logout"/>
<access-denied-handler ref="myAccessDeniedHandlerForAdmin" />
<custom-filter before="FORM_LOGIN_FILTER" ref="myApplicationFilterForAdmin"/>
</http>
<beans:bean id="myAccessDeniedHandlerForAdmin" class="code.security.admin.MyAccessDeniedHandlerForAdmin" />
<beans:bean id="myApplicationFilterForAdmin" class="code.security.admin.MyApplicationFilterForAdmin">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationFailureHandler" ref="failureHandlerForAdmin"/>
<beans:property name="authenticationSuccessHandler" ref="successHandlerForAdmin"/>
</beans:bean>
<beans:bean id="successHandlerForAdmin"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
</beans:bean>
<beans:bean id="failureHandlerForAdmin"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/admin/showSecondLogin?login_error=1"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="myAuthenticationProviderForAdmin" />
<authentication-provider ref="myAuthenticationProvider" />
</authentication-manager>
<beans:bean id="myAuthenticationProviderForAdmin" class="code.security.admin.MyAuthenticationProviderForAdmin">
<beans:property name="userDetailsService" ref="userDetailsServiceForAdmin"/>
</beans:bean>
<beans:bean id= "userDetailsServiceForAdmin" class="code.security.admin.MyUserDetailsServiceForAdminImpl">
</beans:bean>
<!-- Admin Login Ends -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basenames">
<beans:list>
<beans:value>code/security/SecurityMessages</beans:value>
</beans:list>
</beans:property>
</beans:bean>
Uptill now you can see, url-pattern I have mentioned is hard coded. I wanted to know if there is a way to create new ROLES and PERMISSIONS dynamically, not hard coded.
Like creating new roles and permissions and saving them to database and then accessing from database. I have searched on net, but I am not able to find out how to add new entries to code.
So these are at least two questions:
How to make the granted authorities/privileges/Roles dynamic?
How to make the access restriction for the URLs dynamic?
1) How to make the granted authorities/privileges/Roles dynamic?
I will not answer this in great detail, because I believe this theme was discussed often enough.
The easiest way would be to store the complete user information (login, password and roles) in a database (3 Tables: User, Roles, User2Roles) and use the JdbcDetailService. You can configure the two SQL Statements (for authentication and for granting the roles) very nicely in your xml configuration.
But then the user needs to logout and login to get these new Roles. If this is not acceptable, you must also manipulate the Roles of the current logged in user. They are stored in the users session. I guess the easiest way to do that is to add a filter in the spring security filter chain that updates the Roles for every request, if they need to be changed.
2) How to make the access restriction for the URLs dynamic?
Here you have at last two ways:
Hacking into the FilterSecurityInterceptor and updating the securityMetadataSource, the needed Roles should be stored there. At least you must manipulate the output of the method DefaultFilterInvocationSecurityMetadataSource#lookupAttributes(String url, String method)
The other way would be using other expressions for the access attribute instead of access="hasRole('ROLE_USER')". Example: access="isAllowdForUserPages1To3". Of course you must create that method. This is called a "custom SpEL expression handler" (If you have the Spring Security 3 Book it's around page 210. Wish they had chapter numbers!). So what you need to do now is to subclass WebSecurityExpressionRoot and introduce a new method isAllowdForUserPages1To3. Then you need to subclass DefaultWebSecurityExpressionHandler and modify the createEvaluationContext method so that its first request StandartEvaluationContext calls super (you need to cast the result to StandartEvaluationContext). Then, replace the rootObject in the StandartEvaluationContext using your new CustomWebSecurityExpressionRoot implementation. That's the hard part! Then, you need to replace the expressionHandler attribute of the expressionVoter (WebExpressionVoter) in the xml configuration with your new subclassed DefaultWebSecurityExpressionHandler. (This sucks because you first need to write a lot of security configuration explicity as you can't access them directly from the security namespace.)
I would like to supplement Ralph's response about creating custom SpEL expression. His explanations helped very much on my attempt to find the right way to do this, but i think that they need to be extended.
Here is a way on how to create custom SpEL expression:
1) Create custom subclass of WebSecurityExpressionRoot class. In this subclass create a new method which you will use in expression. For example:
public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot {
public CustomWebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
super(a, fi);
}
public boolean yourCustomMethod() {
boolean calculatedValue = ...;
return calculatedValue;
}
}
2) Create custom subclass of DefaultWebSecurityExpressionHandler class and override method createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) (not createEvaluationContext(...)) in it to return your CustomWebSecurityExpressionRoot instance. For example:
#Component(value="customExpressionHandler")
public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
#Override
protected SecurityExpressionRoot createSecurityExpressionRoot(
Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot expressionRoot = new CustomWebSecurityExpressionRoot(authentication, fi);
return expressionRoot;
}}
3) Define in your spring-security.xml the reference to your expression handler bean
<security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
...
<security:expression-handler ref="customExpressionHandler"/>
</security:http>
After this, you can use your own custom expression instead of the standard one:
<security:authorize access="yourCustomMethod()">
This question has a very straightforward answer. I wonder why you haven't got your answer yet.
There are two things that should be cleared at least:
First, you should know that when you are using namespace, automatically some filters will be added to each URL you have written.
Second, you should also know that what each filter does.
Back to your question:
As you want to have intercept-url to be dynamically configured, you have to remove those namespaces, and replace them with these filters:
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/css/**" filters="none" />
<sec:filter-chain pattern="/images/**" filters="none" />
<sec:filter-chain pattern="/login.jsp*" filters="none" />
<sec:filter-chain pattern="/user/showLoginPage.action" filters="none" />
<sec:filter-chain pattern="/**"
filters="
securityContextPersistenceFilter,
logoutFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</sec:filter-chain-map>
</bean>
Then you have to inject your own SecurityMetadaSource into FilterSecurityInterceptor. See the following:
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="securityMetadataSource" ref="myFilterInvocationSecurityMetadataSource" />
</bean>
<bean id="myFilterInvocationSecurityMetadataSource" class="myPackage.MyFilterSecurityMetadataSource">
</bean>
But before that, you have to customize 'MyFilterSecurityMetadataSource' first.
This class has to implement the 'DefaultFilterInvocationSecurityMetadataSource'.
As you want to have all roles and URLs in your DB, you have to customize its getAttributes
Now see the following example of its implementation:
public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
attributes = getAttributesByURL(url); //Here Goes Code
return attributes;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
do you see the "Here goes your code" comment? You have to implement that method yourself.
I myself, have a table named URL_ACCESS which contains both URLs and their corresponding roles. After receiving URL from user, I look up into that table and return its related role.
As I'm working exactly on this subject, you may ask any questions... I will always answer.
You can use Voter to dynamically restrict access. Also see Get Spring Security intercept urls from database or properties
Create your model (user, role, permissions) and a way to retrieve permissions for a given user;
Define your own org.springframework.security.authentication.ProviderManager and configure is (set its providers) to a custom org.springframework.security.authentication.AuthenticationProvider; this last one should return on its authenticate method a Authentication, which should be setted with the GrantedAuthority, in your case, all the permissions for the given user.
The trick in that article is to have roles assigned to users, but, to set the permissions for those roles in the Authentication.authorities object.
For that I advise you to read the API, and see if you can extend some basic ProviderManager and AuthenticationProvider instead of implementing everything. I've done that with LdapAuthenticationProvider setting a custom LdapAuthoritiesPopulator, that would retrieve the correct roles for the user.

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