0

I am building a REST API using Spring Boot with Shiro for authentication. I am getting a NullPointerException when I try to Autowire a repository in my realm code. Autowiring the repository elsewhere does not produce this error.

Here is my authorizing realm:

@Component
public class CCDAuthorizingRealm extends AuthorizingRealm {

    @Autowired
    private UserRepository userRepository;

    public CCDAuthorizingRealm() { }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        AuthenticationInfo authenticationInfo = null;
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        User user = null;
        try {
            user = userRepository.findByUsername(username);
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }

        if (user != null) {
            authenticationInfo = new SimpleAuthenticationInfo(
                    new SimplePrincipalCollection(username, username),
                    user.getPassword());
        }

        return authenticationInfo;
    }
    ....
}

The exception occurs when I try to call the findByUsername() method.

Here is the code for the repository that I am injecting.

@Transactional(readOnly=true)
@RepositoryRestResource(collectionResourceRel="users", path="users")
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
    User findByUsername(@Param("user") String user);
    ....
}

To test, I autowired the repository into my Application class and printed the results, and I got no errors. For completeness, here is the code to show that:

@SpringBootApplication
public class Application implements CommandLineRunner {
    @Autowired
    public UserRepository userRepository;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... strings) {
        System.out.println("\nServer up\n");
        User user = userRepository.findOne(new Long(1));
        System.out.println(user.getUsername());
    }
}

Any help is greatly appreciated.

EDIT

As far as I can tell this question is not a duplicate, and the answer to the other question does not resolve my problem. I am injecting the bean and informing the IoC container of the component, but the autowiring still results in a NPE.

Using scopes does not help, because I only need the single service object. The Configurable tag is not necessary, because I am autowiring and do not need to create a new object.

UPDATE

Here is where I am calling CCDAuthorizingRealm. I'm new to using Shiro, but I'm pretty sure this is correct.

@RestController
@RequestMapping(produces="application/json")
public class AuthenticationController {

    CCDAuthorizingRealm realm = new CCDAuthorizingRealm();

    SecurityManager sm = new DefaultSecurityManager(realm);

    public AuthenticationController() { }

    /**
     * Authenticate user with supplied credentials
     *
     * @param login - credentials
     * @return success or failure message
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST, consumes="application/json")
    public String login(@RequestBody LoginInfo login) {
        String response;
        UsernamePasswordToken token = new UsernamePasswordToken(
                    login.getUsername(),
                    login.getPassword(),
                    false);
        try {
            SecurityUtils.setSecurityManager(sm);
            Subject currentUser = SecurityUtils.getSubject();
            if (currentUser.isAuthenticated()) {
                currentUser.logout();
            }
            currentUser.login(token);
            response = token.toString();
        } catch (AuthenticationException e) {
            response = "Error: Incorrect username or password";
        }

        return response;
    }
}

I currently try creating a new instance of my realm in the controller. I do this because when I try:

@Autowired
CCDAuthorizingRealm realm;
SecurityManager sm = new DefaultSecurityManager(realm);

I would get a runtime error when initializing the Security Manager because it would complain about realm being null. I don't understand why realm is null, however, since it is annotated as a component. Perhaps this is the root of my problem.

mrks_
  • 359
  • 4
  • 20
  • You haven't shown where you're injecting the `CCDAuthorizingRealm` into its caller. – chrylis -cautiouslyoptimistic- May 15 '15 at 16:04
  • No, I said that repository method works correctly when injected into the Application class. The NPE occurs when I try to Autowire it into the CCDAuthorizingRealm class. The realm is annotated with @Component, so, as far as I'm aware, the application should find it since Spring Boot includes component scanning. – mrks_ May 15 '15 at 16:07
  • Shouldn't the bean implementing `UserRepository` interface have the `@Component` annotation instead? – Ceiling Gecko May 15 '15 at 16:11
  • Unless I am misunderstanding you, both of the beans in which I implement UserRepository are annotated as components. One is UserService, which is annotated as a service (which includes the component annotation), and the other is the realm, which is also annotated as a component. – mrks_ May 15 '15 at 16:26
  • Being that this question is not a strict duplicate, since it involves Apache Shiro setup and autowiring a repository into it, can the duplicate tag be removed? – mrks_ May 15 '15 at 16:48
  • The entire point of the duplicate is that *annotating a class with `@Component` doesn't make all instances of that class autowired*, only the instance(s) that Spring is managing. The fact that you're calling `new CCDAuthorizingRealm();` makes this an *exact* duplicate. You need to (1) use constructor injection to pass in the `CCDAuthorizingRealm` or (2) create your `SecurityManager` in `@PostConstruct`. – chrylis -cautiouslyoptimistic- May 15 '15 at 17:19
  • You are correct in saying I should not instantiate a new CCDAuthorizingRealm in my AuthController. I was doing so because Shiro was not being configured correctly through my xml files, and it was a way to at least have the security manager running to poke around at. The problem appears to be because Shiro is not being managed by Spring, but *not* because of incorrect injection. Thus, this question is **not a duplicate**, as it is in the Shiro configuration part of my code. I believe I solved my issue by changing some configuration settings. I will post an answer once I verify it myself. – mrks_ May 15 '15 at 19:56

1 Answers1

1

I have discovered the answer to my problem. Here is what I had in my applicationContext.xml:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <property name="loginUrl" value="/login" />
    <property name="successUrl" value="/home" />
    <property name="unauthorizedUrl" value="/login" />
    <property name="filterChainDefinitions">
        <value>
            /login = authc
        </value>
    </property>
</bean>

The problem with this configuration is that the login URL was being filtered out. The fix was to simply change the value to home like so:

<value>
    /home = authc
</value>

Which I believe sets the home URL behind the filter, instead.

mrks_
  • 359
  • 4
  • 20