Search code examples
jsfjakarta-eeprimefacesjavabeansshiro

Apache Shiro, can not pass values from login form to backing bean


I can not pass values from my login form to backing bean to authenticate user by apache shiro in my web app. Does anybody know why?

shiro.ini

 [main]
authc.loginUrl = /login.jsf
authc.successUrl = /pages/welcome.jsf

[users]
admin = 1234

[urls]
/login.jsf = authc
/pages/** = authc

login.xhtml

<ui:define name="content">
<div id="login_form">
    <h:form prependId="false">
        <center>
        <h1>Sign in <p:link value="or create an account" outcome="registration" style="color: #0A9CE5; text-decoration: none;" /></h1>
            <table> 
                <tr>
                    <td><p:inputText id="username" value="#{shiroLoginBean.username}" placeholder="Login" required="true" /></td>
                </tr>
                <tr>
                    <td><p:password id="password" value="#{shiroLoginBean.password}" placeholder="Password" required="true" /></td>
                </tr>
            </table>
        </center>
        <p:commandButton value="Sign in" action="#{shiroLoginBean.doLogin}" />
    </h:form>
</div>

BEAN:

    package utils.security;

import java.io.IOException;
import java.io.Serializable;

import javax.ejb.Stateless;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.inject.Named;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Named("shiroLoginBean")
@Stateless
@ViewScoped
public class ShiroLoginBean implements Serializable {
    private static final long serialVersionUID = 1L;

    private static final Logger log = LoggerFactory.getLogger(ShiroLoginBean.class);

    private String username;
    private String password;

    public ShiroLoginBean() {

    }

    /**
     * Try and authenticate user. 
     */
    public void doLogin() {
        Subject subject = SecurityUtils.getSubject();   
        UsernamePasswordToken token = new UsernamePasswordToken(getUsername(), getPassword());

        try {
            subject.login(token);
            FacesContext.getCurrentInstance().getExternalContext().redirect("pages/welcome.jsf");
        } catch (UnknownAccountException ex) {
            log.error(ex.getMessage(), ex);
        } catch (IncorrectCredentialsException ex) {
            log.error(ex.getMessage(), ex);
        } catch (LockedAccountException ex) {
            log.error(ex.getMessage(), ex);
        } catch (AuthenticationException | IOException ex) {
            log.error(ex.getMessage(), ex);
        } finally {
            token.clear();
        }
    }

    //~ Setters and Getters
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }

}

Problem is on this row... UsernamePasswordToken token = new UsernamePasswordToken(getUsername(), getPassword()); ... getUsername and getPassword is always null. Thank you so much for answer!!


Solution

  • Your @ViewScoped import is wrong - since you use @Named (meaning its a CDI bean) you should use

    import javax.faces.view.ViewScoped; 
    

    Since you omitted a relevant scope you'll get @Dependent scope, which I've never seen the use for but its very short lived (inputs will be bound to one bean, but when doLogin() runs it will be in a new one).