Announcement Announcement Module
Collapse
No announcement yet.
Urgent, Pls help!! Customize usersbyusernamequery Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Urgent, Pls help!! Customize usersbyusernamequery

    Hi,

    I am learning to use Spring and Acegi. Now I have a login form which contains username, password and login option. The login id can be email or mobileno which means when user click the radio button of email, the usersbyusernamequery should be:
    SELECT email AS USERNAME, password, '1' AS ENABLED FROM user WHERE email = ?

    or if user clicks on mobileno, the query will be:

    SELECT mobile AS USERNAME, password, '1' AS ENABLED FROM user WHERE mobile = ?

    as stated above, the login page login.jsp is:
    Login id: <input type="text" name="j_username">
    Password: <input type="text" name="j_password">
    Auth Type:
    Email: <input type="radio" name="emailmobile" value="email" checked="checked">
    Mobile no: <input type="radio" name="emailmobile" value="mobile">
    <INPUT type="submit" name="Submit">
    After searching and reading tutorials for a few days on the internet, I think I need to extend the JdbcDaoImpl class to get the emailmobile field's value. The next thing needs to be done is to override the loadUserByUsername method:

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
    if (emailmobile.equals("email")) {
    this.usersByUsernameQuery = "SELECT email AS USERNAME, password, '1' AS ENABLED FROM user WHERE email = ?";
    } else if (emailmobile.equals("mobile")) {
    this.usersByUsernameQuery = "SELECT mobile AS USERNAME, password, '1' AS ENABLED FROM user WHERE mobile = ?";
    }
    :
    :
    }
    I want to know whether I got the correct solution or I have to re-implement the user class to form the user object with the extra field? My current problem is how I can get the emailmobile field value from the request object if this is the correct way?

    Please help!! Thanks.
    Last edited by kuanfai; Apr 24th, 2007, 12:00 PM.

  • #2
    The problem you have here though is how to you tell the Dao which query to select? You would need to add something to the username to know which query was correct, e.g. username:mobile or username:email.

    Comment


    • #3
      Hi karldmoore, thanks for your reply. can you pls elaborate your solution further? how am i going to implement it?

      i am currently trying to override the AuthenticationProcessingFilter as

      public class XdAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
      private String j_emailmobile;

      protected String obtainEmailmobile(HttpServletRequest request) {
      return request.getParameter(StandardValue.EMAILMOBILE);
      }

      public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {
      String username = obtainUsername(request);
      String password = obtainPassword(request);
      this.j_emailmobile = obtainEmailmobile(request);

      if (username == null) {
      username = "";
      }

      if (password == null) {
      password = "";
      }

      UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

      // Place the last username attempted into HttpSession for views
      request.getSession().setAttribute(ACEGI_SECURITY_L AST_USERNAME_KEY, username);

      // Allow subclasses to set the "details" property
      setDetails(request, authRequest);

      return this.getAuthenticationManager().authenticate(authR equest);
      }


      public String getJ_emailmobile() {
      return j_emailmobile;
      }

      public void setJ_emailmobile(String string) {
      j_emailmobile = string;
      }

      }
      and extend JdbcDaoImpl as:

      public class XdJdbcDaoImpl extends JdbcDaoImpl {

      private XdAuthenticationProcessingFilter authFilter;

      public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {

      String emailmobile = authFilter.getJ_emailmobile();

      if (emailmobile.equalsIgnoreCase(StandardValue.EMAIL) ) {
      this.setUsersByUsernameQuery("SELECT email AS USERNAME, password, '1' AS ENABLED " +
      "FROM user, access WHERE user.id = access.id AND user.email = ?");
      } else if (emailmobile.equalsIgnoreCase(StandardValue.MOBILE NO)) {
      this.setUsersByUsernameQuery("SELECT mobileno AS USERNAME, password, '1' AS ENABLED " +
      "FROM user, access WHERE user.id = access.id AND user.mobileno = ?");
      }

      return this.loadUserByUsername(username);
      }

      /**
      * @return
      */
      public XdAuthenticationProcessingFilter getAuthFilter() {
      return authFilter;
      }

      /**
      * @param filter
      */
      public void setAuthFilter(XdAuthenticationProcessingFilter filter) {
      authFilter = filter;
      }

      }

      my xml configuration is:

      <bean id="authenticationProcessingFilter" class="com.spring.acegis.XdAuthenticationProcessin gFilter">
      <property name="authenticationManager" ref="authenticationManager"/>
      <property name="authenticationFailureUrl" value="/login.jsp?login_error=1"/>
      <property name="defaultTargetUrl" value="/"/>
      <property name="filterProcessesUrl" value="/j_acegi_security_check"/>
      </bean>

      <bean id="jdbcDaoImpl" class="com.spring.acegis.XdJdbcDaoImpl">
      <property name="dataSource"><ref bean="dataSource"/></property>
      <property name="authoritiesByUsernameQuery">
      <value>SELECT ? AS USERNAME, 'ROLE_USER' AS AUTHORITY FROM access</value>
      </property>
      <property name="authFilter"><ref bean="authenticationProcessingFilter"/> </property>
      </bean>

      however when i execute it, it seems like running into dead loop.
      Last edited by kuanfai; Apr 26th, 2007, 09:18 PM.

      Comment


      • #4
        Originally posted by kuanfai View Post

        I want to know whether I got the correct solution or I have to re-implement the user class to form the user object with the extra field? My current problem is how I can get the emailmobile field value from the request object if this is the correct way?
        Why don't you change your selects to be unions from the same file - easy !

        SELECT email AS USERNAME, password, '1' AS ENABLED FROM user WHERE email = ?
        union
        SELECT mobile AS USERNAME, password, '1' AS ENABLED FROM user WHERE mobile = ?

        I assume you have no users with usernames equal to their email address ?

        Comment


        • #5
          Hi Paul, this is not feasible as only one value can be attached to the query, but there needs two inputs. I have tried it with nullpointer exception thrown.
          Last edited by kuanfai; Apr 24th, 2007, 12:00 PM.

          Comment


          • #6
            Originally posted by kuanfai View Post
            Hi Paul, this is not feasible as only one value can be attached to the query, but there needs two inputs. I have tried it with nullpointer exception thrown.
            Ah, OK ! Back to the drawing board !

            Comment


            • #7
              As the username is the only value sent down to the JdbcDaoImpl then your username needs to contain everything you need to query on. Therefore in the filter you would need to do something like username:mobile, or username:email and use that as the username. In the Dao you can then split this up and query til your hearts content. I know this doesn't look like the best solution but it will work.

              Comment


              • #8
                hi karldmoore, sorry for my limited knowledge, but it just sounds too abstract for me to understand. would appreciate if you can give me a simple query for illustration. thanks!

                Comment


                • #9
                  Originally posted by kuanfai View Post
                  hi karldmoore, sorry for my limited knowledge, but it just sounds too abstract for me to understand. would appreciate if you can give me a simple query for illustration. thanks!
                  Errrr, in the filter pass the username into UsernamePasswordAuthenticationToken as username + ":" + j_emailmobile. In the dao split this up again into two Strings. You can then query on it. The problem with the current code the thread-safety aspects.

                  Comment


                  • #10
                    Hi karldmoore, thanks for your explanation. i managed to achieve the goal by extending two classes: AuthenticationProcessingFilter and JdbcDaoImpl. That is to add the emailmobile field into the filter to form the authentication object and then retrieve the emailmobile field value from the filter in JdbcDaoImpl extended class to determine which query string to use. In case someone else confronted the same problem as me, the code is as below:

                    public class XdAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
                    private String j_emailmobile;

                    protected String obtainEmailmobile(HttpServletRequest request) {
                    return request.getParameter(StandardValue.EMAILMOBILE);
                    }

                    public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {
                    String username = obtainUsername(request);
                    String password = obtainPassword(request);
                    this.j_emailmobile = obtainEmailmobile(request);

                    if (username == null) {
                    username = "";
                    }

                    if (password == null) {
                    password = "";
                    }

                    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

                    // Place the last username attempted into HttpSession for views
                    request.getSession().setAttribute(ACEGI_SECURITY_L AST_USERNAME_KEY, username);

                    // Allow subclasses to set the "details" property
                    setDetails(request, authRequest);

                    return this.getAuthenticationManager().authenticate(authR equest);
                    }


                    /**
                    * @return
                    */
                    public String getJ_emailmobile() {
                    return j_emailmobile;
                    }

                    /**
                    * @param string
                    */
                    public void setJ_emailmobile(String string) {
                    j_emailmobile = string;
                    }

                    }
                    and

                    public class XdJdbcDaoImpl extends JdbcDaoImpl {

                    private XdAuthenticationProcessingFilter authFilter;
                    private String userquery = super.getUsersByUsernameQuery();
                    private boolean usernameBasedPrimaryKey;
                    protected MappingSqlQuery usersByUsernameMapping;

                    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {

                    String emailmobile = authFilter.getJ_emailmobile();

                    if (emailmobile.equalsIgnoreCase(StandardValue.EMAIL) ) {
                    userquery = "SELECT email AS USERNAME, password, '1' AS ENABLED " +
                    "FROM user, access WHERE user.id = access.id AND user.email = ?";
                    } else if (emailmobile.equalsIgnoreCase(StandardValue.MOBILE NO)) {
                    userquery = "SELECT mobileno AS USERNAME, password, '1' AS ENABLED " +
                    "FROM user, access WHERE user.id = access.id AND user.mobileno = ?";
                    }

                    this.usersByUsernameMapping = new UsersByUsernameMapping(getDataSource());

                    List users = usersByUsernameMapping.execute(username);

                    if (users.size() == 0) {
                    throw new UsernameNotFoundException("User not found");
                    }

                    UserDetails user = (UserDetails) users.get(0); // contains no GrantedAuthority[]

                    List dbAuths = authoritiesByUsernameMapping.execute(user.getUsern ame());

                    addCustomAuthorities(user.getUsername(), dbAuths);

                    if (dbAuths.size() == 0) {
                    throw new UsernameNotFoundException("User has no GrantedAuthority");
                    }

                    GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);

                    String returnUsername = user.getUsername();

                    if (!usernameBasedPrimaryKey) {
                    returnUsername = username;
                    }

                    return new User(returnUsername, user.getPassword(), user.isEnabled(), true, true, true, arrayAuths);

                    }

                    protected class UsersByUsernameMapping extends MappingSqlQuery {
                    protected UsersByUsernameMapping(DataSource ds) {
                    super(ds, userquery);
                    declareParameter(new SqlParameter(Types.VARCHAR));
                    compile();
                    }

                    protected Object mapRow(ResultSet rs, int rownum)
                    throws SQLException {
                    String username = rs.getString(1);
                    String password = rs.getString(2);
                    boolean enabled = rs.getBoolean(3);
                    UserDetails user = new User(username, password, enabled, true, true, true, new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});

                    return user;
                    }
                    }

                    /**
                    * @return
                    */
                    public XdAuthenticationProcessingFilter getAuthFilter() {
                    return authFilter;
                    }

                    /**
                    * @param filter
                    */
                    public void setAuthFilter(XdAuthenticationProcessingFilter filter) {
                    authFilter = filter;
                    }

                    /**
                    * @return
                    */
                    public boolean isUsernameBasedPrimaryKey() {
                    return usernameBasedPrimaryKey;
                    }

                    /**
                    * @param b
                    */
                    public void setUsernameBasedPrimaryKey(boolean b) {
                    usernameBasedPrimaryKey = b;
                    }

                    }
                    the corresponding xml should be:
                    <bean id="authenticationProcessingFilter" class="com.spring.acegis.XdAuthenticationProcessin gFilter">
                    <property name="authenticationManager" ref="authenticationManager"/>
                    <property name="authenticationFailureUrl" value="/login.jsp?login_error=1"/>
                    <property name="defaultTargetUrl" value="/"/>
                    <property name="filterProcessesUrl" value="/j_acegi_security_check"/>
                    </bean>


                    <bean id="jdbcDaoImpl" class="com.spring.acegis.XdJdbcDaoImpl">
                    <property name="dataSource"><ref bean="dataSource"/></property>
                    <property name="authoritiesByUsernameQuery">
                    <value>SELECT ? AS USERNAME, 'ROLE_USER' AS AUTHORITY FROM access</value>
                    </property>
                    <property name="authFilter"><ref bean="authenticationProcessingFilter"/> </property>
                    </bean>

                    Comment


                    • #11
                      Ok, but how many filters are ever created. If the filter has global state and two users try to login are you going to have problems?

                      Comment


                      • #12
                        Originally posted by karldmoore View Post
                        Ok, but how many filters are ever created. If the filter has global state and two users try to login are you going to have problems?
                        Hi karldmoore, thank you so much for your remind. The problem with my implementation is that the one user will be automatically logged out if another user logs out. Referring to your implementation, the question is how to get the UsernamePasswordAuthenticaitonToken from dao class? Since the dao class doesn't have the access to the request or session object.
                        Last edited by kuanfai; Apr 30th, 2007, 12:03 AM.

                        Comment


                        • #13
                          Originally posted by kuanfai View Post
                          Hi karldmoore, thank you so much for your remind. The problem with my implementation is that the one user will be automatically logged out if another user logs out. Referring to your implementation, the question is how to get the UsernamePasswordAuthenticaitonToken from dao class? Since the dao class doesn't have the access to the request or session object.
                          Referring to my previous reply, I figured out the way to implement your suggestion:

                          public class XdAuthenticationProcessingFilter extends AuthenticationProcessingFilter {

                          protected String obtainEmailmobile(HttpServletRequest request) {
                          return request.getParameter(StandardValue.EMAILMOBILE);
                          }

                          public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {
                          String username = obtainUsername(request);
                          String password = obtainPassword(request);
                          String emailmobile = obtainEmailmobile(request);

                          if (username == null) {
                          username = "";
                          } else {
                          username = username + ":" + emailmobile;
                          }

                          if (password == null) {
                          password = "";
                          }

                          UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

                          // Place the last username attempted into HttpSession for views
                          request.getSession().setAttribute(ACEGI_SECURITY_L AST_USERNAME_KEY, username);

                          // Allow subclasses to set the "details" property
                          setDetails(request, authRequest);

                          return this.getAuthenticationManager().authenticate(authR equest);
                          }

                          }
                          and

                          public class XdJdbcDaoImpl extends JdbcDaoImpl {

                          private String userquery = super.getUsersByUsernameQuery();
                          private boolean usernameBasedPrimaryKey;

                          public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {

                          String emailmobile = username.substring(username.indexOf(":") + 1);
                          String queryusername = username.substring(0, username.indexOf(":"));

                          if (emailmobile.equalsIgnoreCase(StandardValue.EMAIL) ) {
                          userquery = "SELECT email AS USERNAME, password, '1' AS ENABLED " +
                          "FROM user, access WHERE user.id = access.id AND user.email = ?";
                          } else if (emailmobile.equalsIgnoreCase(StandardValue.MOBILE NO)) {
                          userquery = "SELECT mobileno AS USERNAME, password, '1' AS ENABLED " +
                          "FROM user, access WHERE user.id = access.id AND user.mobileno = ?";
                          }

                          this.usersByUsernameMapping = new UsersByUsernameMapping(getDataSource());

                          List users = usersByUsernameMapping.execute(queryusername);

                          if (users.size() == 0) {
                          throw new UsernameNotFoundException("User not found");
                          }

                          UserDetails user = (UserDetails) users.get(0); // contains no GrantedAuthority[]

                          List dbAuths = authoritiesByUsernameMapping.execute(user.getUsern ame());

                          addCustomAuthorities(user.getUsername(), dbAuths);

                          if (dbAuths.size() == 0) {
                          throw new UsernameNotFoundException("User has no GrantedAuthority");
                          }

                          GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);

                          String returnUsername = user.getUsername();

                          if (!usernameBasedPrimaryKey) {
                          returnUsername = queryusername;
                          }

                          return new User(returnUsername, user.getPassword(), user.isEnabled(), true, true, true, arrayAuths);

                          }

                          protected class UsersByUsernameMapping extends MappingSqlQuery {
                          protected UsersByUsernameMapping(DataSource ds) {
                          super(ds, userquery);
                          declareParameter(new SqlParameter(Types.VARCHAR));
                          compile();
                          }

                          protected Object mapRow(ResultSet rs, int rownum)
                          throws SQLException {
                          String username = rs.getString(1);
                          String password = rs.getString(2);
                          boolean enabled = rs.getBoolean(3);
                          UserDetails user = new User(username, password, enabled, true, true, true, new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});

                          return user;
                          }
                          }

                          }
                          It does not need to get the session in order to get the emailmobile. It has been passed into the loadUserByUsername as part of the input "username".

                          Regarding to the logout issue, I realized it happens only to the two tabs under one explorer, both implementations are ok for separate explorers. This might be not a problem in the first place as you questioned me for the global filter concern, but just because I tested it in the same window. Anyway, thanks for your help, the second methods seem to be more concise.

                          Comment


                          • #14
                            Thanks for posting back. That is exactly what I was suggesting, although it still doesn't feel quite right, it solves the problem! Glad you got it working!

                            Comment

                            Working...
                            X