5

I'm trying to configure spring secure annotations, I already managed to set spring security configuration in xml(configured by intercept-url elements), but now I'm want to use security annotations in my beans. But secured annotation is totaly ignored when try to access secured controller method without logging. Here is my controller bean:

package com.bill.controllers;

import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MainController {

    @Secured({"ROLE_USER"})
    @RequestMapping("/index.html")
    public String main(ModelMap model) {
        model.addAttribute("test", "test");

        return "main";
    }
}

and login controller:

package com.bill.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginController {

    @RequestMapping("/login")
    public String login(ModelMap model) {
        return "login";
    }
}

and configurations: web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app 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">

    <display-name>tests</display-name>

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</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-servlet.xml,
            /WEB-INF/spring-security.xml
        </param-value>
    </context-param>

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

spring-servlet.xml

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.bill" />

    <context:annotation-config />

    <!-- validation configuration -->
    <bean id="validator"
        class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

    <mvc:annotation-driven validator="validator" />

    <!-- view configuration for thymeleaf -->
    <bean id="templateResolver"
        class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
        <property name="prefix" value="/WEB-INF/templates/" />
        <property name="suffix" value=".html" />
        <property name="templateMode" value="HTML5" />
        <property name="characterEncoding" value="UTF-8" />
        <property name="cacheable" value="false" />
    </bean>

    <bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver" />
    </bean>

    <bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
        <property name="templateEngine" ref="templateEngine" />
        <property name="characterEncoding" value="UTF-8" />
    </bean>

    <!-- messages configuration -->
    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:messages/messages" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>

    <!-- internalization configuration -->
    <bean id="localeChangeInterceptor"
        class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
        <property name="paramName" value="language" />
    </bean>

    <bean
        class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
        <property name="interceptors">
            <list>
                <ref bean="localeChangeInterceptor" />
            </list>
        </property>
    </bean>

    <!-- datasource configuration for hibernate 4 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/bill" />
        <property name="username" value="root" />
        <property name="password" value="****" />
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="com.bill" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <tx:annotation-driven />

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

</beans>

and spring-security.xml

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

    <http auto-config="true">
        <form-login login-page="/login" default-target-url="/index.html" authentication-failure-url="/login" />
        <logout logout-success-url="/index.html" />
        <anonymous granted-authority="ROLE_GUEST" username="Guest"/>
    </http>

    <authentication-manager>
        <authentication-provider>
            <password-encoder hash="sha-256"/>
            <jdbc-user-service data-source-ref="dataSource"
               users-by-username-query="
                  select LOGIN, PASSWORD, 'true' 
                  from USERS where LOGIN=?" 
               authorities-by-username-query="
                  select LOGIN, ROLE from USERS
                  where LOGIN=?" 
            />
        </authentication-provider>
    </authentication-manager>

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

</beans:beans>

If it will be needed I can also paste here my pom.xml, but I think that this is poblem with my configuration(because this example without security works fine, and with security configured in xml it also works). I will be realy glad if someone can tell me where is my mistake.

tereško
  • 58,060
  • 25
  • 98
  • 150
gandalfml
  • 908
  • 1
  • 10
  • 23

2 Answers2

6

You have global method security enabled in the spring-security.xml which is processed by the root web application context.

The controllers reside inside the dispatcher servlet context and are unaffected by the bean postprocessors of the root web app context.

So you have to declare <security:global-method-security secured-annotations="enabled" /> inside the dispatcher servlet context or use the web level spring security tags instead(which seems to be natural for the web pages).

See Difference between applicationContext.xml and spring-servlet.xml in Spring Framework

Technically the bean post processors(and therefore AOP tools too) work on per container basis - therefore the things like @Secured or @Transactional will only work in the same application context where the respective annotations - <security:global-method-security ../> / <tx:annotation-driven/> have been applied.

Community
  • 1
  • 1
Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
  • @user1895422 p.s. I guess in your current configuration all the classes are created twice(but only the ones declared in the root app context are working with `@secured` but they are effectively shadowed by the servlet context) because you have put spring-servlet.xml into contextConfigLocation by mistake. You need to have at least two different configuration files. You can also check [my answer](http://stackoverflow.com/questions/11708967/what-is-the-difference-between-applicationcontext-and-webapplicationcontext-in-s?lq=1) regarding the application contexts. – Boris Treukhov Jan 18 '13 at 19:57
  • I think that I don't understand. First of all, I put my spring-servlet and spring-security files as values of contextConfigLocation because it that way it was done in a lots of tutorials. From your comment I understand that this files should be parsed by spring servlet with name spring by default, am I right? But when I remove contextConfigLocation param from web.xml and try to deply aplication, server give me errror that file /WEB-INF/applicationContext.xml cannot be found. So, do I relly need this file? and what should be in its content? – gandalfml Jan 18 '13 at 20:29
  • When you remove contextConfigLocation parameter Spring chooses applicationContext.xml as the default file name for the root web app configuration - you need to have another configuration file(you can name it applicationContext.xml if you don't want to specify the name explicitly). In this case I think you should rename your spring-servlet to applicationContext.xml(or something appropriate) and create a separate file for MVC/Controller(ie mvc-annotation-driven tag) configuration and name it spring-servlet.xml. – Boris Treukhov Jan 18 '13 at 20:36
  • As for the default name of the servlet configuration file - it's spring-servlet.xml because your servlet is named "spring" – Boris Treukhov Jan 18 '13 at 20:44
  • 1
    Ok, I had to leave for a while. For now I notice that when I move global-method-security from spring-security to spring-servlet, the annotations work as expected, so the problem is solved I think, but to be sure about how the whole config should be done: actual spring-servlet should be renamed to applicationContext.xml, to new sping-servlet from new applicationContext.xml should be moved all elements which are not beans, spring-security should remains as it is now(excepted global-method-security which should be in spring-servlet), contextConfigLocation parameter can be then removed, yes? – gandalfml Jan 18 '13 at 22:02
  • You should move elements that belong to MVC layer to spring-servlet. You can't remove contextConfigLocation because you have to specify spring-security configuration file(Spring Security for Web can only be configured via root web app context because it's a filter), that forces you to specify the actual file names for the root context. – Boris Treukhov Jan 18 '13 at 22:06
0

Are you sure your spring-security.xml is being loaded, and if it is, that it's being loaded in the right place. Have a look at this for some hints.

CodeChimp
  • 8,016
  • 5
  • 41
  • 79
  • yes, I'm sure, because in this file I put my xml security configuration(intercept-url elements) in previous version and it works – gandalfml Jan 18 '13 at 20:03
  • If you visit the link, which says pretty much the same thing @Boris Truekhov mentions, it speaks about items loaded by DispatchServlet vs. the root context. I think Boris is on the right path there. I think you are either creating multiple version of your beans, with one masking the other, or your global-method-security is being applied at the wrong layer, as it cannot see the sub-Context if it's build at the root-level. – CodeChimp Jan 18 '13 at 21:02