Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my custom PermissionEvaluator not working?

I can’t understand why my security doesn’t work properly. Method hasPermission() in Evaluator class not even called. I think there is something wrong with my security config.

My Security config:

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

    <sec:http use-expressions="true">
        <sec:intercept-url pattern="/favicon.ico" access="permitAll"/>
        <sec:intercept-url pattern="/resources/**" access="permitAll"/>
        <sec:intercept-url pattern="/login" access="permitAll"/>
        <sec:form-login login-page="/login"
                        username-parameter="login"
                        password-parameter="password"
                        authentication-failure-url="/login?error"
                        authentication-success-handler-ref="successHandler"/>
        <sec:logout logout-url="/logout" logout-success-url="/login?logout"/>
        <sec:access-denied-handler error-page="/WEB-INF/views/error/403.jsp"/>
    </sec:http>

    <sec:authentication-manager>
        <sec:authentication-provider ref="userAuthenticationProvider"/>
    </sec:authentication-manager>

    <sec:global-method-security pre-post-annotations="enabled" secured-annotations="enabled">
        <sec:expression-handler ref="expressionHandler"/>
    </sec:global-method-security>

    <bean:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <bean:property name="permissionEvaluator" ref="permissionEvaluator"/>
    </bean:bean>

    <bean:bean id="permissionEvaluator" class="de.mark.project.security.UserPermissionEvaluator">
        <bean:constructor-arg ref="userSecurityService"/>
    </bean:bean>

    <bean:bean id="successHandler" class="de.mark.project.security.UrlAuthenticationSuccessHandler"/>
    <bean:bean id="userSecurityService" class="de.mark.project.service.UserService"/>

    <bean:bean name="userAuthenticationProvider" class="de.mark.project.security.UserAuthenticationProvider">
        <bean:constructor-arg ref="userSecurityService"/>
    </bean:bean>

</bean:beans>

Here is my custom evaluator:

public class UserPermissionEvaluator implements PermissionEvaluator {
    private UserService service;

    @Autowired
    public UserPermissionEvaluator(UserService service) {
        this.service = service;
    }

    @Override
    public boolean hasPermission(Authentication authentication, 
                                 Serializable targetId,  String targetType, 
                                 Object permission) {

        UserDetails principal = (UserDetails) authentication.getPrincipal();
        User authorizedUser = service.getUser(principal.getUsername());
        Collection<Permission> userPermissions = authorizedUser.getPermissions();

        for (Permission p : userPermissions) {
            if (p.getName().equals(allowedPermission)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication, 
                                 Serializable targetId,  String targetType, 
                                 Object permission) {
        throw new RuntimeException("Error");
    }
}

Method in the controller which using security:

@RequestMapping(method = RequestMethod.GET)
@PreAuthorize("hasPermission(null, 'create_user')")
public String createUserForm(@ModelAttribute("user") User user) {
    return "user/internal/form/credentialForm";
}

Any ideas how to fix it?

UPD:

<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_5.xsd">


    <!-- log4j configuration -->
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:/log4j.properties</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>


    <!-- Config Setup -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:/spring-config/security.xml
            classpath:/spring-config/data.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

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


    <!-- Spring MVC -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:/spring-config/mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


    <!-- Error handling -->
    <error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/views/error/404.jsp</location>
    </error-page>
</web-app>

UPD 2

Spring MVC config:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:ctx="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <mvc:annotation-driven/>
    <ctx:component-scan base-package="de.mark.project.web"/>
    <sec:global-method-security pre-post-annotations="enabled"/>
    <mvc:resources mapping="/resources/**" location="classpath:/style/"/>

    <mvc:interceptors>
        <bean id="webContentInterceptor" class="org.springframework.web.servlet.mvc.WebContentInterceptor">
            <property name="cacheSeconds" value="0"/>
            <property name="useExpiresHeader" value="true"/>
            <property name="useCacheControlHeader" value="true"/>
            <property name="useCacheControlNoStore" value="true"/>
        </bean>
    </mvc:interceptors>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
like image 891
user3279337 Avatar asked Oct 20 '22 12:10

user3279337


1 Answers

This is likely due to the fact that the <global-method-security> tag needs to be in the same context as your Spring MVC configuration otherwise your controllers will not be post processed. This is discussed in the FAQ.

So for example, if your web.xml looks like the following:

<!--
  - Location of the XML file that defines the root application context
  - Applied by ContextLoaderListener.
  -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/spring/*.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>

<!--
  - Loads the root application context of this web app at startup.
  - The application context is then available via
  - WebApplicationContextUtils.getWebApplicationContext(servletContext).
-->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/mvc/*.xml</param-value>
    </init-param>
</servlet>

To support method security on your controllers ensure the <global-method-security> tag is defined in a location within /WEB-INF/mvc/*.xml. Note that the remainder of the configuration should remain where it is. If you want to support method security on your services, you likely also need <global-method-security> in the parent (i.e. where it likely is now).

If this does not help, please post your web.xml or WebApplicationInitializer's if you are not using web.xml

like image 158
Rob Winch Avatar answered Oct 28 '22 21:10

Rob Winch