I realized a login servlet with spring webflow 2 and spring ldap for user authentication. And so far all is ok.
Now, I’m tryng to introduce spring security in my login flow. So, following the spring web flow reference guide version 2.4.0 (section 8), and the spring security ldap section guide, I adjust my configuration to secure flow. In particular, I’m tryng to secure the success login page for only ROLE_USERS users.
What I tried to make is to extract username, password, and the role of the user from ldap database and use these information to apply security in my flow. So, I makes the following changes to my project:
Add secured attribute to displayLoginSuccessView view state in loginflow.xml
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.4.xsd"> <var name="loginCredentials" class="com.folkture.login.LoginCredentials"/> <view-state id="displayLoginView" view="/WEB-INF/views/display_login.jsp" model="loginCredentials"> <transition on="loginCredentialsEntered" to="performLoginAction"/> </view-state> <action-state id="performLoginAction"> <evaluate expression="loginService.performLogin(loginCredentials)"/> <transition to="displayLoginSuccessView"/> <transition on-exception="com.folkture.login.IncorrectLoginCredentialsException" to="displayLoginErrorView"/> </action-state> <view-state id="displayLoginSuccessView" view="/WEB-INF/views/display_login_success.jsp"> <secured attributes="ROLE_USER" /> </view-state> <view-state id="displayLoginErrorView" view="/WEB-INF/views/display_login_error.jsp"/> </flow>Add bean SecurityFlowExecutionListener to webflow-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:webflow="http://www.springframework.org/schema/webflow-config" xsi:schemaLocation="http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.4.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <webflow:flow-executor id="loginFlowExecutor" flow-registry="loginFlowRegistry"> <webflow:flow-execution-listeners> <webflow:listener ref="sfel" /> </webflow:flow-execution-listeners> </webflow:flow-executor> <webflow:flow-registry id="loginFlowRegistry"> <!-- Define the flow executor responsible for executing login web flow --> <webflow:flow-location id="loginFlow" path="/WEB-INF/flows/login-flow.xml" /> </webflow:flow-registry> <!-- Installs a listener to apply Spring Security authorities --> <bean id="sfel" class="org.springframework.webflow.security.SecurityFlowExecutionListener" />Add springSecurityFilterChain to my web.xml
Add spring security configuration in spring-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:ldap="http://www.springframework.org/schema/ldap" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <context:property-placeholder location="classpath:/ldap.properties" system-properties-mode="OVERRIDE" /> <context:annotation-config /> <ldap:context-source id="contextSource" url="${sample.ldap.url}" base="${sample.ldap.base}" authentication-source-ref="springSecurityAuthenticationSource" /> <ldap:ldap-template id="ldapTemplate" context-source-ref="contextSource" /> <bean id="loginService" class="com.folkture.login.LoginService"> <property name="ldapTemplate" ref="ldapTemplate" /> </bean> <bean id="springSecurityAuthenticationSource" class="org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource" /> <security:http auto-config="true"> <security:intercept-url pattern="/**" access="ROLE_USER" /> <security:form-login login-page="/views/display_login.jsp" /> </security:http> <security:authentication-manager> <security:ldap-authentication-provider user-search-filter="(cn={0})" user-search-base="ou=users" group-search-filter="(member={0})" group-search-base="ou=Groups" group-role-attribute="cn" /> </security:authentication-manager> <security:ldap-server url="ldap://localhost:389" manager-dn="${sample.ldap.userDn}" manager-password="${sample.ldap.password}" />And this is my java class with login method, LoginService.java
package com.folkture.login; import javax.naming.directory.DirContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ldap.NamingException; import org.springframework.ldap.core.AuthenticatedLdapEntryContextCallback; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.LdapEntryIdentification; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.filter.AndFilter; import org.springframework.ldap.filter.EqualsFilter; import org.springframework.security.core.Authentication; import org.springframework.security.ldap.authentication.LdapAuthenticator; import org.springframework.stereotype.Service; @Service public class LoginService implements LdapAuthenticator{ @Autowired private LdapTemplate ldapTemplate; public LoginService() { super(); } public void setLdapTemplate(LdapTemplate ldapTemplate) { this.ldapTemplate = ldapTemplate; System.out.println("setLdapTemplate "+ ldapTemplate); } public String performLogin(LoginCredentials loginCredentials) throws Exception{ if(login(loginCredentials.getLoginName(),loginCredentials.getPassword())) { System.out.println("autenticato!"); return "success"; } else { throw new IncorrectLoginCredentialsException(); } } public boolean login(String username, String password) throws Exception{ AndFilter filter = new AndFilter(); filter.and(new EqualsFilter("objectclass", "inetOrgPerson")).and(new EqualsFilter("cn", username)); if(ldapTemplate.authenticate("ou=users", filter.toString(), password, contextCallback)) return true; return false; } AuthenticatedLdapEntryContextCallback contextCallback = new AuthenticatedLdapEntryContextCallback() { @SuppressWarnings("deprecation") public void executeWithContext(DirContext ctx, LdapEntryIdentification ldapEntryIdentification) { try { try { ctx.lookup(ldapEntryIdentification.getRelativeDn()); } catch (javax.naming.NamingException e) { e.printStackTrace(); } } catch (NamingException e) { throw new RuntimeException("Failed to lookup " + ldapEntryIdentification.getRelativeDn(), e); } } }; @Override public DirContextOperations authenticate(Authentication authentication) { // TODO Auto-generated method stub return null; } }
After entering username and password I have the following error.
HTTP Status 500 - Request processing failed; nested exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'performLoginAction' of flow 'loginFlow'
________________________________________
type Exception report
message Request processing failed; nested exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'performLoginAction' of flow 'loginFlow'
description The server encountered an internal error that prevented it from fulfilling this request.
exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'performLoginAction' of flow 'loginFlow'
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
root cause
org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'performLoginAction' of flow 'loginFlow'
org.springframework.webflow.engine.impl.FlowExecutionImpl.wrap(FlowExecutionImpl.java:573)
org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:263)
org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:228)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
root cause
java.lang.NullPointerException
org.springframework.security.access.vote.RoleVoter.extractAuthorities(RoleVoter.java:115)
org.springframework.security.access.vote.RoleVoter.vote(RoleVoter.java:96)
org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:62)
org.springframework.webflow.security.SecurityFlowExecutionListener.decide(SecurityFlowExecutionListener.java:109)
org.springframework.webflow.security.SecurityFlowExecutionListener.stateEntering(SecurityFlowExecutionListener.java:74)
org.springframework.webflow.engine.impl.FlowExecutionListeners.fireStateEntering(FlowExecutionListeners.java:144)
org.springframework.webflow.engine.impl.FlowExecutionImpl.setCurrentState(FlowExecutionImpl.java:373)
org.springframework.webflow.engine.impl.RequestControlContextImpl.setCurrentState(RequestControlContextImpl.java:189)
org.springframework.webflow.engine.State.enter(State.java:191)
org.springframework.webflow.engine.Transition.execute(Transition.java:228)
org.springframework.webflow.engine.impl.FlowExecutionImpl.execute(FlowExecutionImpl.java:395)
org.springframework.webflow.engine.impl.RequestControlContextImpl.execute(RequestControlContextImpl.java:214)
org.springframework.webflow.engine.TransitionableState.handleEvent(TransitionableState.java:116)
org.springframework.webflow.engine.Flow.handleEvent(Flow.java:547)
org.springframework.webflow.engine.impl.FlowExecutionImpl.handleEvent(FlowExecutionImpl.java:390)
org.springframework.webflow.engine.impl.RequestControlContextImpl.handleEvent(RequestControlContextImpl.java:210)
org.springframework.webflow.engine.ActionState.doEnter(ActionState.java:105)
org.springframework.webflow.engine.State.enter(State.java:194)
org.springframework.webflow.engine.Transition.execute(Transition.java:228)
org.springframework.webflow.engine.impl.FlowExecutionImpl.execute(FlowExecutionImpl.java:395)
org.springframework.webflow.engine.impl.RequestControlContextImpl.execute(RequestControlContextImpl.java:214)
org.springframework.webflow.engine.TransitionableState.handleEvent(TransitionableState.java:116)
org.springframework.webflow.engine.Flow.handleEvent(Flow.java:547)
org.springframework.webflow.engine.impl.FlowExecutionImpl.handleEvent(FlowExecutionImpl.java:390)
org.springframework.webflow.engine.impl.RequestControlContextImpl.handleEvent(RequestControlContextImpl.java:210)
org.springframework.webflow.engine.ViewState.handleEvent(ViewState.java:231)
org.springframework.webflow.engine.ViewState.resume(ViewState.java:195)
org.springframework.webflow.engine.Flow.resume(Flow.java:537)
org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:259)
org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:228)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
I think the authentication provider settings in spring-config.xml do not match properly the java class for authentication and authorization. How can I successfully retrieving role from my ldap?