Karan
Karan

Reputation: 285

Spring Security - Autowired not working

This is a practice project I am working on, to learn Spring Security and OAuth2.

Link to the zip of the project - https://drive.google.com/open?id=0BxsYBg5XwboXdkpUdkhXWUd1Vmc

As soon as I add the below class, my autowired service fails :( It gives NullPointerException.

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new OAuth2MethodSecurityExpressionHandler();
    }
}

And I get NullPointerException userService in below rest controller.

@RestController
@RequestMapping("/api/user")
public class UserCtrl {

@Autowired
@Qualifier("userService")
UserService userService;

@PreAuthorize("#oauth2.hasScope('write')")
@RequestMapping(method=RequestMethod.POST,consumes="application/json")
private @ResponseBody ResponseMessage registerUser(@RequestBody User user){
    user.setUser_id(-1);
    return userService.createUser(user);
}

@PreAuthorize("#oauth2.hasScope('none')")
@RequestMapping(method=RequestMethod.GET)
private @ResponseBody ResponseMessage loginUser(){

    CustomUserDetails temp =  (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    System.out.println(temp.getUsername() + " logged in");

    ResponseMessage message = userService.getUserByUsername(temp.getUsername());

    if(message.getCode() == ResponseMessage.CODE_NOT_FOUND){
        return message;
    }else{
        temp.setUser((User)message.getData());
    }

    return userService.loginUser(temp.getUser().getUsername(),temp.getUser().getPassword());
}

@PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping(method=RequestMethod.PUT,consumes="application/json")
private @ResponseBody ResponseMessage updateUser(@RequestBody User user){
    return userService.updateUser(user);
}

@PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping(method=RequestMethod.DELETE,consumes="application/json")
private @ResponseBody ResponseMessage deleteUser(@RequestBody User user){
    return userService.deleteUser(user.getUser_id());
}
}

Here's how I defined my service :

@Service("userService")
public class UserServiceImpl implements UserService {

@Autowired
private UserDAO userDAO;

@Autowired
private RoleService roleService;

public UserServiceImpl() {
    System.out.println("service");
}

@Override
@Transactional
public ResponseMessage createUser(User user) {
    if (null == userDAO.getUserByUsername(user.getUsername())) {
        int id = userDAO.createUser(user);
        user.setUser_id(id);
        roleService.createRole(new Role(RoleType.ROLE_USER, user));

        return new ResponseMessage(ResponseMessage.CODE_CREATED, ResponseMessage.DESC_CREATED, user);
    } else {
        return new ResponseMessage(ResponseMessage.CODE_EXISTS, ResponseMessage.DESC_EXISTS, user);
    }
}

Mvc configuration :

@EnableWebMvc
@Configuration
@ComponentScan({"com.rawal.sweetnote.*"})
public class WebMvcConfig extends WebMvcConfigurerAdapter{

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
}
}

Spring security config:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

@Autowired
@Qualifier("userService")
UserService userDetailsService;

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService);
}   
}

Security initializer :

@Order(1)
public class SpringSecurityInitializer extends  AbstractSecurityWebApplicationInitializer{

}

DispatcherServletInitializer

public class AppInit extends AbstractAnnotationConfigDispatcherServletInitializer{

@Override
protected Class<?>[] getRootConfigClasses() {
    // TODO Auto-generated method stub
    return new Class[]{MethodSecurityConfig.class,WebMvcConfig.class};
}

@Override
protected Class<?>[] getServletConfigClasses() {
    // TODO Auto-generated method stub
    return null;
}

@Override
protected String[] getServletMappings() {
    // TODO Auto-generated method stub
    return new String[]{"/"};
}
}

Hibernate config:

@Configuration
@EnableTransactionManagement
@PropertySource(value = { "classpath:app.properties" })
public class HibernateConfig {
/**
 * This field is automatically injected by the spring framework from app.properties
 */
@Autowired
private Environment environment;



/**
 * Creates a session factory
 * @return {@link SessionFactory}
 */
@Bean
public LocalSessionFactoryBean sessionFactory() {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(dataSource());
    sessionFactory.setPackagesToScan(new String[] { "com.rawal.sweetnote.entities" });
    sessionFactory.setHibernateProperties(hibernateProperties());
    return sessionFactory;
}


/**
 * Creates a datasource after setting various database properties
 * @return {@link DataSource}
 */
@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(environment.getRequiredProperty("db.drivername"));
    dataSource.setUrl(environment.getRequiredProperty("db.url"));
    dataSource.setUsername(environment.getRequiredProperty("db.username"));
    dataSource.setPassword(environment.getRequiredProperty("db.password"));
    return dataSource;
}


/**
 * Sets up the hibernate properties and returns an instance of it
 * @return {@link Properties}
 */
private Properties hibernateProperties() {
    Properties properties = new Properties();
    properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
    properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
    return properties;
}



/**
 * Creates a transaction manager using the sessionfactory injected by spring and returns an instance of it
 * @param {@link SessionFactory}
 * @return {@link HibernateTransactionManager}
 */
@Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
    HibernateTransactionManager txManager = new HibernateTransactionManager();
    txManager.setSessionFactory(s);
    return txManager;
}


/**
 * Creates a hibernate template instance from the injected session factory.
 * We use this to perform various db transactions.
 * @param {@link SessionFactory}
 * @return
 */
@Bean
@Autowired
public HibernateTemplate hibernateTemplate(SessionFactory sessionFactory) {
    HibernateTemplate hibernateTemplate = new HibernateTemplate();
    hibernateTemplate.setSessionFactory(sessionFactory);
    return hibernateTemplate;
}
}

OAuth2 Resource Config

@Configuration
@EnableResourceServer
public class OAuthResourceConfig extends ResourceServerConfigurerAdapter{

private static final String RESOURCE_ID = "restservice";

@Override
public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/api/**").authenticated()
    .antMatchers("/register").permitAll()
    .and().formLogin().permitAll()
    .and().logout().permitAll()
    .and().csrf().disable();
}

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources.resourceId(RESOURCE_ID).stateless(false);
}
}

OAuth2 Server Config

@Configuration
@EnableAuthorizationServer
@PropertySource("classpath:app.properties")
public class OAuthServerConfig extends AuthorizationServerConfigurerAdapter{

@Autowired
private Environment env;


@Autowired
private UserService userDetailsService;

@Autowired
private AuthenticationManager authenticationManager;

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancer()).authenticationManager(authenticationManager).userDetailsService(userDetailsService);
}

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    security.allowFormAuthenticationForClients();
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory().withClient("client")
    .authorizedGrantTypes("password","refresh_token","implicit")
    .scopes("read","write","trust")
    .refreshTokenValiditySeconds(Integer.parseInt(env.getProperty("refresh_token_validity")))
    .accessTokenValiditySeconds(Integer.parseInt(env.getProperty("access_token_validity")));
}

@Bean
public TokenStore tokenStore() {
    InMemoryTokenStore store = new InMemoryTokenStore();
    store.setAuthenticationKeyGenerator(new UniqueAuthenticationKeyGenerator());
    return store;
}

@Bean
public TokenEnhancer tokenEnhancer() {
    return new CustomTokenEnhancer();
}
}

Stacktrace for the exception:

java.lang.NullPointerException
com.rawal.sweetnote.controllers.UserCtrl.loginUser(UserCtrl.java:40)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:177)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:121)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:670)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

UPDATE 1

Modified the user controller as below(added greet1() and greet2()). Now the funny thing is, the UserService property isn't null for these new methods. But it is still null for the other methods.

@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping("/greet")
public String greet() {
    System.out.println(service.sayHello());
    return "Hello";
}

@PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping("/greet2")
public String greet2() {
    System.out.println(service.sayHello());
    return "Hello";
}


@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(method=RequestMethod.POST,consumes="application/json")
private @ResponseBody ResponseMessage registerUser(@RequestBody User user){
    user.setUser_id(-1);
    return service.createUser(user);
}

@PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping(path="/",method=RequestMethod.GET)
private @ResponseBody ResponseMessage loginUser(){

    CustomUserDetails temp =  (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    System.out.println(temp.getUsername() + " logged in");


    service.sayHello();
    ResponseMessage message = service.getUserByUsername(temp.getUsername());

    if(message.getCode() == ResponseMessage.CODE_NOT_FOUND){
        return message;
    }else{
        temp.setUser((User)message.getData());
    }

    return service.loginUser(temp.getUser().getUsername(),temp.getUser().getPassword());
}

However, if I disable prePostEnabled, everything works fine. This feels really stupid! Help!!!!!

Upvotes: 2

Views: 3305

Answers (2)

dasunse
dasunse

Reputation: 3089

Make sure your controllers to public not private as an example

@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(method=RequestMethod.POST,consumes="application/json")
public ResponseMessage registerUser(@RequestBody User user){
   // your code here
}

Upvotes: 1

Karan
Karan

Reputation: 285

Okay, this is really stupid of "ME". I just noticed that all methods in my controller were private. :( Making them public solved the problem.

HOWEVER!!!!! I STILL WONDER HOW IT WORKED WHEN THE @EnableGlobalMethodSecurity(prePostEnabled = true) was set to false.

Please comment if you get this.

Hope this helps someone as stupid as me. :/

Upvotes: 5

Related Questions