Doug
Doug

Reputation: 709

Spring 3.1 ignoring my @Secured Annotations

OK, I've got another one of those "Spring is ignoring my @Secured annotations". I've read a number of the other solutions on here and other places and none seem to solve my problems.

I'm using Netbeans 7.4beta, along with Spring 3.1.1 and Spring Security 3.1.4.

I've got a Controller that should be restricted to logged in Users:

package my.phonedirectory.mvc.controllers;

import javax.annotation.Resource;
import org.apache.log4j.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import my.phonedirectory.mvc.services.PhoneDirectoryService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {

    protected static void debug(String msg) {
        Logger logger = Logger.getLogger("Controller");
        System.out.println("Logger Category: " + logger.getParent().toString());
        logger.debug(new Object() { }.getClass().getEnclosingClass().getSimpleName() + ": " + msg);
    }

    @Resource(name="phoneDirectoryService")
    PhoneDirectoryService phoneDirectoryService;
    public void setPhoneDirectoryService(PhoneDirectoryService phoneDirectoryService) {
        this.phoneDirectoryService = phoneDirectoryService;
    }

    @RequestMapping("/index.html")
    @Secured("ROLE_USER")
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
                                                 HttpServletResponse response)
            throws Exception {
        debug("handleRequestInternal");

        String username;


        SecurityContext sc = SecurityContextHolder.getContext();
        Authentication auth = sc.getAuthentication();
        if (auth == null) {
            username = "SecurityContextHolder.getAuthentication() returned NULL";
        } else {
            Object principal = auth.getPrincipal();
            if (principal == null) {
                username = "SecurityContextHolder.getAuthentication().getPrincipal() returned NULL";
            } else {
                if (principal instanceof User) {
                    User user = (User)principal;
                    debug("User logged in: " + user.getUsername());
                    username = user.getUsername();
                } else {
                    debug("No user logged in: " + principal.toString());
                    username = principal.toString();
                }
            }
        }


        ModelAndView mv = new ModelAndView("index");
        mv.addObject("message", "Hello " + username);        
        return mv;
    }

}

And my index.jsp files is:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Phone Directory</title>
    </head>

    <body>
        <h1> Welcome to the Phone Directory</h1>
    <h2><%= (String)request.getAttribute("message") %></h2>
    <p>
        To see the Phone Directory, click <a href="list.html">HERE</a>.
    </body>
</html>

When I load localhost:8080/PhoneDirectory/index.html, I get:

 Welcome to the Phone Directory
Hello anonymousUser

To see the Phone Directory, click HERE. 

so, as you can see, it doesn't force a log-in attempt, and instead allows the anonymous user to access the method that is supposed to be protected by the @Secured("ROLE_USER").

This is what shows up in my log when I enabled debug logging for Spring Security...

INFO: PhoneDirectory was successfully deployed in 5,899 milliseconds.
INFO: 16:15:25,889 DEBUG web.FilterChainProxy:337 - /index.html at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO: 16:15:25,891 DEBUG context.HttpSessionSecurityContextRepository:139 - HttpSession returned null object for SPRING_SECURITY_CONTEXT
INFO: 16:15:25,891 DEBUG context.HttpSessionSecurityContextRepository:85 - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@36c63b9. A new one will be created.
INFO: 16:15:25,900 DEBUG web.FilterChainProxy:337 - /index.html at position 2 of 10 in additional filter chain; firing Filter: 'LogoutFilter'
INFO: 16:15:25,901 DEBUG web.FilterChainProxy:337 - /index.html at position 3 of 10 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
INFO: 16:15:25,901 DEBUG web.FilterChainProxy:337 - /index.html at position 4 of 10 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
INFO: 16:15:25,902 DEBUG web.FilterChainProxy:337 - /index.html at position 5 of 10 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
INFO: 16:15:25,902 DEBUG web.FilterChainProxy:337 - /index.html at position 6 of 10 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
INFO: 16:15:25,904 DEBUG web.FilterChainProxy:337 - /index.html at position 7 of 10 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
INFO: 16:15:25,907 DEBUG authentication.AnonymousAuthenticationFilter:102 - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6faeba70: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 33784332ecf862dfef93485b8bbf; Granted Authorities: ROLE_ANONYMOUS'
INFO: 16:15:25,907 DEBUG web.FilterChainProxy:337 - /index.html at position 8 of 10 in additional filter chain; firing Filter: 'SessionManagementFilter'
INFO: 16:15:25,908 DEBUG web.FilterChainProxy:337 - /index.html at position 9 of 10 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
INFO: 16:15:25,908 DEBUG web.FilterChainProxy:337 - /index.html at position 10 of 10 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
INFO: 16:15:25,909 DEBUG intercept.FilterSecurityInterceptor:185 - Public object - authentication not attempted
INFO: 16:15:25,910 DEBUG web.FilterChainProxy:323 - /index.html reached end of additional filter chain; proceeding with original chain
INFO: Logger Category: org.apache.log4j.spi.RootLogger@3fb9f67a
INFO: 16:15:25,967 DEBUG Controller:32 - IndexController: handleRequestInternal
INFO: Logger Category: org.apache.log4j.spi.RootLogger@3fb9f67a
INFO: 16:15:25,968 DEBUG Controller:32 - IndexController: No user logged in: anonymousUser
INFO: 16:15:25,976 DEBUG support.DefaultListableBeanFactory:1498 - Invoking afterPropertiesSet() on bean with name 'index'
INFO: 16:15:25,993 DEBUG support.DefaultListableBeanFactory:245 - Returning cached instance of singleton bean 'org.springframework.security.methodSecurityMetadataSourceAdvisor'
INFO: 16:15:26,151 DEBUG context.HttpSessionSecurityContextRepository:269 - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO: 16:15:26,152 DEBUG context.HttpSessionSecurityContextRepository:269 - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO: 16:15:26,155 DEBUG access.ExceptionTranslationFilter:115 - Chain processed normally
INFO: 16:15:26,156 DEBUG context.SecurityContextPersistenceFilter:97 - SecurityContextHolder now cleared, as request processing completed

And here is my web.xml file (note: I've renamed the usual applicationContext.xml and applicationContext-security.xml to root-context.xml and root-security.xml):

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <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>



    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/root-context.xml
                     /WEB-INF/root-security.xml
        </param-value>
    </context-param>


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

    <servlet>
        <servlet-name>phoneDirectory</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>phoneDirectory</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>

    <welcome-file-list>
        <welcome-file>redirect.jsp</welcome-file>
    </welcome-file-list>

</web-app>

And my root-context.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">





</beans>

And my root-security.xml file:

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


    <global-method-security secured-annotations="enabled" />

    <http auto-config="true"
                 use-expressions="true"
                 access-denied-page="/PhoneDirectory/accessDenied" >



        <form-login
            login-page="/PhoneDirectory/login"
            authentication-failure-url="/PhoneDirectory/login?error=true"
            default-target-url="/PhoneDirectory/list"/>

        <logout
            invalidate-session="true"
            logout-success-url="/PhoneDirectory/login"
            logout-url="/PhoneDirectory/logout"/>

    </http>

    <authentication-manager>
         <authentication-provider user-service-ref="userDetailsService">
         </authentication-provider>
    </authentication-manager>

  <user-service id="userDetailsService">
     <user name="user" password="user_password" authorities="ROLE_USER, ROLE_ADMIN" />
     <user name="admin" password="admin_password" authorities="ROLE_USER" />
   </user-service>



</beans:beans>

And my phoneDirectory-servlet.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-3.1.xsd
                           http://www.springframework.org/schema/security 
                           http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <security:global-method-security secured-annotations="enabled" />

    <bean id="appService"
          class="my.phonedirectory.mvc.services.PhoneDirectoryService">        
    </bean>

    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>

    <context:component-scan base-package="my.phonedirectory.mvc" />

    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />



</beans>

I know the main advice is always "put the tag in the myApp-servlet.xml file instead of the top-level applicationContext.xml (or root-context.xml, in my example). But that didn't solve my problem...

Upvotes: 1

Views: 1859

Answers (3)

Bernhard Seebass
Bernhard Seebass

Reputation: 31

Spring seems to have problems handling @Resource (javax.annotation.Resource) annotations correctly. We observed that the declared scope of the resource got ignored.

The solution was to use Autowired instead of Resource. You might try the following:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
...
    @Autowired
    @Qualifier("phoneDirectoryService")
    PhoneDirectoryService phoneDirectoryService;

Upvotes: 1

coder
coder

Reputation: 4466

can you please try using @secured annotation directly in your service class (PhoneDirectoryService) and moving bean of PhoneDirectoryService to root-application context

Upvotes: 0

Related Questions