Reputation: 709
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
Reputation: 1477
It could be related to Spring Security+MVC annotations Illegal argument exception
if it solves, see the explanation. http://sergey-karpushin.blogspot.com/2012/10/spring-mvc-security-secured-annotation.html
Upvotes: 1
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
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