farnett
farnett

Reputation: 480

No bean named 'CustomAuthenticationProvider' is defined

I am trying to switch from a working jdbc-user-service to a customAuthenticationProvider which implements AuthenticationProvider, however spring is not finding it. I'm not sure if it has to do with something about having my configurations in xml or what is wrong. I started out with one in my dispatcher servlet however on advice from things that I read I added one to my spring-security.xml as well which didn't make any difference. Any ideas what is wrong with my configuration?

Thanks!

mvc-dispatch-servlet.xml

 <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="com.mkyong.*" />
    <!-- Currently not working. Made a work around by having resources at /resources and pages at /pages -->
  <mvc:resources location="/resources/" mapping="/resources/" />

      <!-- also add the following beans to get rid of some exceptions -->
     <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
     <bean
      class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
     </bean>

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix"><value>/WEB-INF/pages/</value></property>
        <property name="suffix"><value>.jsp</value></property>
    </bean>

</beans>

spring-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:beans="http://www.springframework.org/schema/beans" 
    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.2.xsd>

    <!-- enable use-expressions -->
    <http auto-config="true" use-expressions="true">
        <!-- login page must be available to all. The order matters, if this is after something which secures the page this will fail. -->
        <intercept-url pattern="/pages/login" access="permitAll" />
        <intercept-url pattern="/pages/admin/**" access="hasRole('_admin')" />
        <intercept-url pattern="/pages/trade/**" access="hasRole('_trader')" />
        <intercept-url pattern="/pages/discover/**" access="hasRole('_users')" />       
        <!-- access denied page -->
        <access-denied-handler error-page="/pages/403" />
        <form-login 
            login-page="/pages/login" 
            default-target-url="/pages/trade/index" 
            authentication-failure-url="/login?error" 
            username-parameter="username"
            password-parameter="password" />
        <logout logout-url="/pages/logout" logout-success-url="/pages/login?logout" />
        <!-- enable csrf protection -->
        <csrf/>
    </http>

    <!-- Select users and user_roles from database -->
    <authentication-manager>
        <authentication-provider ref="CustomAuthenticationProvider"/>
        <!--<jdbc-user-service data-source-ref="dataSource"
                users-by-username-query=
                    "select email,pwhash, enabled from users where email=?"
                authorities-by-username-query=
                    "select email, groupname from usergroups where email =?  " /> 
        </authentication-provider> -->
    </authentication-manager>  
</beans:beans>

CustomAuthenticationProvider.java

package com.mkyong.web.controller;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    User user = new User();
    user.name = authentication.getName();
    user.password = authentication.getCredentials().toString();
    try {
      user.id = Instance.users.getUserByEmail(user.name).getUserID();
    } catch (Exception e) {
      Instance.debug("CustomAuthenticationProvider authenticate","Error getting user" + e);
    }

    // use the credentials to try to authenticate against the third party system
    if (passVerify(user)) {
      List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
      try {
        UserRoles roles = Instance.users.getUser(user.id).roles;
        userRolesToDatabaseRoles(roles, grantedAuths);
      } catch (Exception e) {
        Instance.debug("CustomAuthenticationProvider authenticate","Error getting user" + e);
      }
      return new UsernamePasswordAuthenticationToken(user.name, user.password, grantedAuths);
    } else {
      Instance.debug("CustomAuthenticationProvider authenticate","Unable to authenticate");
      return null;
    }
  }

  private void userRolesToDatabaseRoles(UserRoles roles, List<GrantedAuthority> grantedAuths) {
    if(roles.admin){
      grantedAuths.add(new SimpleGrantedAuthority("_admin"));
    }
    if(roles.trader){
      grantedAuths.add(new SimpleGrantedAuthority("_trader"));
    }
    if(roles.analyst){
      grantedAuths.add(new SimpleGrantedAuthority("_users"));
    }


  }

  private boolean passVerify(User user) {
    StringBuffer MD5 = getMD5(user);    
    try {
      //User still has an MD5 password, so change them over to bcrypt
      if(MD5.toString().equals(Instance.users.getPasswordHash(user.name))){
        String hashedPassword = getBcrypt(user).toString();
        instance.users.changePassword(user.id, hashedPassword);
        return true;
      }
    } catch (Exception e) {
      instance.debug("CustomAuthenticationProvider passVerify","Error getting userpassword" + e);
    }

    StringBuffer bcrypt = getBcrypt(user);

    if(bcrypt.toString().equals(user.password)){
      return true;
    }



    return false;
  }

  public StringBuffer getBcrypt(User user) {
    //This sets how many rounds bcrypt will run. The high the number the longer it takes which will slow down user login, however it also slows
    //down a would be attacker. This is a key advantage of bcrypt over other algorithms. *IMPORTANT* changing the strength will result in needing to 
    //rehash all passwords. This is very doable but requires more work. 
    //See http://crypto.stackexchange.com/questions/3003/do-i-have-to-recompute-all-hashes-if-i-change-the-work-factor-in-bcrypt
    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(10);
    StringBuffer hashedPassword = new StringBuffer(); 
    hashedPassword.append(passwordEncoder.encode(user.password));

    return hashedPassword;
  }

  public StringBuffer getMD5(User user) {
    StringBuffer sb = null;
    MessageDigest md;

    String original = "a";
    try {
      md = MessageDigest.getInstance("MD5");
      md.update(original.getBytes());
      byte[] digest = md.digest();
      sb = new StringBuffer();
      for (byte b : digest) {
        sb.append(String.format("%02x", b & 0xff));
      }

    } catch (NoSuchAlgorithmException e) {
     instance.debug("CustomAuthenticationProvider hashMD5","Error getting MD5 instance" + e);
    }

    return sb;
  }


  @Override
  public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
  }

  public class User{
    public long id;
    protected String name, password;
  }



}

Not sure if web.xml is necessary to post but here it is:

web.xml

<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


        <!-- Spring MVC -->
    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

      <servlet>
    <servlet-name>InitServlet</servlet-name>
    <servlet-class>servletInitServlet</servlet-class>
    <init-param>
      <param-name>configfile</param-name>
      <param-value>C:/transmetric/dev/java/WebContent/WEB-INF/config.properties</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>AdminServlet</servlet-name>
    <servlet-class>servlet.admin</servlet-class>
    <load-on-startup>3</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>servlet.user</servlet-class>
    <load-on-startup>4</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>SignupUserServlet</servlet-name>
    <servlet-class>servlet.user.SignupUserServlet</servlet-class>
    <load-on-startup>5</load-on-startup>
  </servlet>

  <servlet>
   <servlet-name>ReceiveFile</servlet-name>
    <servlet-class>servlet.user</servlet-class>
    <load-on-startup>6</load-on-startup>
  </servlet>

  <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/pages/*</url-pattern>
    </servlet-mapping>

  <servlet-mapping>
   <servlet-name>AdminServlet</servlet-name>
   <url-pattern>/AdminServlet</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
   <servlet-name>UserServlet</servlet-name>
   <url-pattern>/UserServlet</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
   <servlet-name>SignupUserServlet</servlet-name>
   <url-pattern>/SignupUserServlet</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
   <servlet-name>ReceiveFile</servlet-name>
   <url-pattern>/ReceiveFile</url-pattern>
  </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-security.xml,
            /WEB-INF/spring-database.xml
        </param-value>
    </context-param>

    <!-- Spring Security -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

Upvotes: 0

Views: 2736

Answers (3)

Nikita Bosik
Nikita Bosik

Reputation: 865

In my case an exception was caused by actually missing .class files due to bad Maven build:

  • (YourApp)/target/classes
  • (YourApp)/target/(YourApp).war/WEB-INF/classes/
  • (YourApp)/target/(YourApp).war/WEB-INF/lib/

The root cause was Eclipse problem: it switched to use embedded Maven while I always use external one. To fix it I had to:

  1. Preferences > Maven > Installations, setup external Maven
  2. Delete project from workspace
  3. Delete all derived files/folders in project folder (target/, bin/, .settings/, .classpath & .project), leave only pom.xml and src/.
  4. File > Import... > Existing Maven Projects, select my project.

Once rebuilt, all required classes were there.

Upvotes: 0

Andrei Stefan
Andrei Stefan

Reputation: 52368

Change

<authentication-provider ref="CustomAuthenticationProvider"/>

to (not the uncapitalized bean name)

<authentication-provider ref="customAuthenticationProvider"/>

By default, the bean names for autodetected components annotated with @Component is the uncapitalized non-qualified class name. In your case this will be customAuthenticationProvider.

Upvotes: 1

coder
coder

Reputation: 4466

Try defining your CustomAuthenticationProvider bean in spring-security xml instead of using @Component annotation.

Or you can also try to put <context:component-scan base-package="com.mkyong.*" /> in your security xml

Upvotes: 2

Related Questions