0

How can I create one LoginController bean for each logged user. The problem is that the moment a new user logs, the same login controller is being used and the only thing that changes is the current user.

He is my LoginController Bean:

@ManagedBean(name = "loginController")

@SessionScoped

@Controller

public class LoginController implements Serializable {

private static final long serialVersionUID = 1L;

@Autowired
IUserService userService;

@Autowired
@Qualifier("authenticationManager")
protected AuthenticationManager authenticationManager;

// save the current user after login to be able to inject it in other places
// as needed
private User currentUser;

private RequestCache requestCache = new HttpSessionRequestCache();

// inject error message strings
@Value("${loginError.title}")
private String loginErrorTitle;

@Value("${loginError.noAccount}")
private String loginErrorNoAccount;

@Value("${loginError.badCredentials}")
private String loginErrorBadCredentials;

@Value("${loginError.accountLocked}")
private String loginErrorAccountLocked;

@Value("${loginError.accountLocked}")
private String loginErrorAccountDisabled;

/**
 * @return the userService
 */
public IUserService getUserService() {
    return userService;
}

/**
 * @param userService
 *            the userService to set
 */
public void setUserService(IUserService userService) {
    this.userService = userService;
}

/**
 * @return the currentUser
 */
public User getCurrentUser() {
    return currentUser;
}

/**
 * @param currentUser
 *            the currentUser to set
 */
public void setCurrentUser(User currentUser) {
    this.currentUser = currentUser;
}

/**
 * This action logs the user in and returns to the secure area.
 * 
 * @return String path to secure area
 */
public void loginUsingSpringAuthenticationManager() {
    // get backing bean for simple redirect form
    LoginFormBackingBean loginFormBean = (LoginFormBackingBean) FacesUtils
            .getBackingBean("loginFormBean");

    try {

        // check if we have the user with the specified username in the DB
        // if we do, authenticate him
        User user = userService.getUserByUsername(loginFormBean
                .getUserName().trim());
        if (null != user) {
            // simple token holder
            Authentication authenticationRequestToken = createAuthenticationToken(loginFormBean);

            Authentication authenticationResponseToken = authenticationManager
                    .authenticate(authenticationRequestToken);

            Authentication authCopy = null;
            final Object principal = authenticationResponseToken
                    .getPrincipal();
            if (principal instanceof LdapUserDetailsImpl) {
                LdapUserDetailsImpl userImpl = (LdapUserDetailsImpl) principal;
                userImpl.getUsername();
                // set the obtained user as the current user
                setCurrentUser(user);
                List<GrantedAuthority> grAuth = new ArrayList<GrantedAuthority>();
                // after this, do the role authority stuff
                // here loop through user roles if he has more and

                // get the highest role for the user and set it as authority
                Role role = userService.getMaxRoleForUser(currentUser);
                grAuth.add(new SimpleGrantedAuthority(role.getName()));

                authCopy = new UsernamePasswordAuthenticationToken(
                        authenticationResponseToken.getPrincipal(),
                        authenticationResponseToken.getCredentials(),
                        grAuth);
            }

            SecurityContextHolder.getContext().setAuthentication(authCopy);
            // ok, test if authenticated, if yes reroute
            if (authenticationResponseToken.isAuthenticated()) {
                // if authentication is successful, get the redirect URL and
                // go to that page;
                // if redirect URL is null -> go to index.xhtml
                HttpServletRequest request = (HttpServletRequest) FacesContext
                        .getCurrentInstance().getExternalContext()
                        .getRequest();

                HttpServletResponse response = (HttpServletResponse) FacesContext
                        .getCurrentInstance().getExternalContext()
                        .getResponse();

                SavedRequest savedRequest = requestCache.getRequest(
                        request, response);
                String savedRedirectUrl = savedRequest != null ? savedRequest
                        .getRedirectUrl() : null;

                FacesContext
                        .getCurrentInstance()
                        .getExternalContext()
                        .redirect(
                                savedRedirectUrl != null ? savedRedirectUrl
                                        : "index.xhtml");

            }
        } else {
            // we have no user with this username yet, show message
            FacesContext.getCurrentInstance().addMessage(
                    null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR,
                            loginErrorTitle, loginErrorNoAccount));
        }
    } catch (BadCredentialsException badCredentialsException) {
        FacesContext.getCurrentInstance().addMessage(
                null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        loginErrorTitle, loginErrorBadCredentials));
    } catch (LockedException lockedException) {
        FacesContext.getCurrentInstance().addMessage(
                null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        loginErrorTitle, loginErrorAccountLocked));
    } catch (DisabledException disabledException) {
        FacesContext.getCurrentInstance().addMessage(
                null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        loginErrorTitle, loginErrorAccountDisabled));
    } catch (IOException e) {
        FacesContext.getCurrentInstance().addMessage(
                null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        loginErrorTitle, e.getStackTrace().toString()));
    }
}

/**
 * Creates an authentication token from the username and password collected
 * from the login form.
 * 
 * @param loginFormBean
 * @return
 */
private Authentication createAuthenticationToken(
        LoginFormBackingBean loginFormBean) {
    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
            loginFormBean.getUserName(), loginFormBean.getPassword());
    return usernamePasswordAuthenticationToken;
}
}

EDIT:

Thank you for your answer. I have verified the hash code of two loginControllers specific for two logged user and they are the same, so only one logginController is created. My understandings of JSF and Spring is very poor. The code I posted is implemented by another developer. Here is configurations:

<!-- This is where we configure Spring-Security  -->
<security:http pattern="/javax.faces.resource/**" security="none"/>
<security:http auto-config="true">
    <security:intercept-url pattern="/login.xhtml" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
    <security:intercept-url pattern="/**" access="ROLE_EMPLOYEE, ROLE_TEAM_LEADER, ROLE_MANAGER"/>
    <security:intercept-url pattern="/pages/management/" access="ROLE_MANAGER"/>
    <security:intercept-url pattern="/pages/requests/" access="ROLE_EMPLOYEE, ROLE_TEAM_LEADER, ROLE_MANAGER"/>

    <security:form-login login-page="/login.xhtml" 
                    default-target-url="/index.xhtml"
                    always-use-default-target="false" />
    <security:logout logout-url="/j_spring_security_logout"
            logout-success-url="/login.xhtml"
            invalidate-session="true"/>
</security:http>

<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider ref = "authProvider"/>
</security:authentication-manager>

<bean id="authProvider" 
    class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <constructor-arg value="infobest.tm" />
    <constructor-arg value="ldap://192.168.1.6:389"/> 
</bean>

Do you have any sugestions and usefull materials that could help me?

Sorry for my poor english.

Dima Sendrea
  • 138
  • 1
  • 9

1 Answers1

1

Here, you're mixing JSF bean management annotations with Spring bean management annotation.

@ManagedBean(name = "loginController")
@SessionScoped
@Controller

Essentially, you have declared the class to be used as a managed bean by both JSF via @ManagedBean and Spring via @Controller. Essentially, you end up with two completely separate managed instances of the same class. The JSF one is declared to be placed in the session scope via @SessionScoped. The Spring one hasn't any explicit @Scope declared and therefore defaults to the application scope. As the Spring managed bean has apparently precedence over the JSF managed bean while EL-evaluating it as #{loginController}, you ultimately end up getting the application scoped Spring managed bean instance. This totally explains your concrete problem.

Now you understand the cause, the solution should be obvious enough. Use the one or the other framework to manage your bean and set the right scope on it. So, you should use either

@ManagedBean
@SessionScoped

OR

@Controller
@Scope("session")

Do not mix JSF+Spring bean management annotations on the same class, it'll only confuse you further. Also note that the managed bean name defaults to #{loginController} already, based on the Javabean naming conventions.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • With ManagedBean SessionScoped I am getting this exception org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.infobest.vms.authentication.LoginController] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {org.springframework.beans.factory.annotation.Autowired(required=true)} – Dima Sendrea Sep 02 '13 at 11:46
  • 1
    That's because you're using Spring bean management specific `@Autowired` annotation. It works only on a Spring managed bean, not on a JSF managed bean. The JSF alternative would be `@ManagedProperty` and/or `@EJB`. – BalusC Sep 02 '13 at 11:46
  • and with Controller Scope("session") org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginController': Scope 'session' is not active for the current thread; – Dima Sendrea Sep 02 '13 at 11:47
  • consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. – Dima Sendrea Sep 02 '13 at 11:47
  • This part is beyond me. I don't use Spring. I just use standard Java EE stack. You'd need to read the Spring documentation how to properly register a session scoped Spring managed bean, perhaps you need some XML for that like as for many other Spring artifacts. Here's at least a link to a question about the concrete problem: http://stackoverflow.com/questions/7598412/using-session-scope-in-spring-beans – BalusC Sep 02 '13 at 11:47
  • I have defined it in applicationContext.xml in this way: and now it works – Dima Sendrea Jan 09 '14 at 10:02