Spring中的NullPointerException安全性PreFilter有两个参数方法

时间:2015-11-04 15:53:14

标签: spring spring-mvc spring-security expression spring-el

我正在使用 Spring 4.1.7.RELEASE Spring Security 4.0.2.RELEASE

security.xml文件:

...
<global-method-security pre-post-annotations="enabled"  />
<http auto-config="true" use-expressions="true">
    <form-login login-page="/user/login" />
    <logout invalidate-session="true" logout-success-url="/user/login?logout"/>
    <intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/user/login**" access="permitAll" />
    <intercept-url pattern="/login**" access="permitAll" />

    <intercept-url pattern="/user/create" access="hasAuthority('user_access_full')"/>

    <csrf />
</http>

<authentication-manager>
    <authentication-provider user-service-ref="myUserDetailsService" >
        <password-encoder hash="bcrypt" />    
    </authentication-provider>
</authentication-manager>
<beans:bean id="myUserDetailsService" class="my.service.MyUserDetailsService" autowire="byType"/>
...

我想使用@PreFilter过滤用户服务输入,以便为新用户分配角色。

实体:JRole.java:

@Entity
public class JRole implements Serializable {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

   public Long getId() {
       return id;
   }

   public void setId(Long id) {
       this.id = id;
   }

   @NotEmpty
   @Column(nullable = false, unique = true)
   private String name;
   private String displayName;

   public JRole(){

   }
   public JRole(String name) {
       this.name = name;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getDisplayName() {
       return displayName;
   }

   public void setDisplayName(String displayName) {
       this.displayName = displayName;
   }
}

UserService.java:

...
@Transactional
@PreFilter(value = "hasRole('admin') or (filterObject.name!='admin' and filterObject.name!='office_admin')")
public void create(JUser user, Set<JRole> roles) {
    JUser u2 = userDao.findByUsername(user.getUsername());
    if (u2 != null) {
        throw new MessageException("username duplicate!");
    }
    if (roles == null || roles.isEmpty()) {
        throw new MessageException("empty roles");
    }
    user.setRoles(roles);
    doCreate(user);
}

如果我的控制器调用上述方法:

UserController中:

@RequestMapping(value = "/create", method = RequestMethod.POST)
public String create2(Model model, @Valid @ModelAttribute("user") JUser user, BindingResult result) {
    addAttributes(model, CREATE);
    if (!result.hasErrors()) {
        if (user.getOfficeType() == null) {
            user.setOfficeType(office.getType());
        }
        if (user.getEnabled() == null) {
            user.setEnabled(true);
        }
        System.out.println(user.getRoles().size());
        try {
            userService.create(user, user.getRoles());// method arguments for PreFilter
            model.addAttribute("createSuccess", "کاربر به نام " + user.getFullName() + " ساخته شد.<br/>" + " نام کاربری:" + user.getUsername());
        } catch (MessageException ex) {

            String mess = NUtil.getDeepMessage(ex);
            result.rejectValue(null, "hey", mess + "salam");
        }
        System.out.println(user.getFullName());
    }

    System.out.println(result);
    return "user/manage";
}

我遇到这个例外:

java.lang.NullPointerException
at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.findFilterTarget(ExpressionBasedPreInvocationAdvice.java:71)
at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:35)
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:57)
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:25)
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:62)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
at ir.iais.nezarat.service.UserService$$EnhancerBySpringCGLIB$$3a7efc2e.create(<generated>)
at ir.iais.nezarat.controller.UserController.create2(UserController.java:105)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:393)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(Applicatio ...
...
...

此行发生NullPointerException:

//user is not null, user.getRoles() is not null
userService.create(user, user.getRoles());

请帮忙!

1 个答案:

答案 0 :(得分:3)

虽然Spring Security reference说:

  

您也可以使用@PreFilter在方法调用之前进行过滤,尽管这是一个不太常见的要求。语法是一样的,但如果有多个参数是集合类型,那么您必须使用此批注的filterTarget属性按名称选择一个。

source code仅检查参数的数量,而不检查参数的类型:

private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation mi) {
    Object filterTarget = null;

    if (filterTargetName.length() > 0) {
        filterTarget = ctx.lookupVariable(filterTargetName);
        if (filterTarget == null) {
            throw new IllegalArgumentException(
                "Filter target was null, or no argument with name "
                    + filterTargetName + " found in method");
        }
    }
    else if (mi.getArguments().length == 1) {
        Object arg = mi.getArguments()[0];
        if (arg.getClass().isArray() || arg instanceof Collection<?>) {
            filterTarget = arg;
        }
        if (filterTarget == null) {
            throw new IllegalArgumentException(
                "A PreFilter expression was set but the method argument type"
                    + arg.getClass() + " is not filterable");
        }
    }

    if (filterTarget.getClass().isArray()) {
        throw new IllegalArgumentException(
            "Pre-filtering on array types is not supported. "
                + "Using a Collection will solve this problem");
    }

    return filterTarget;
}

Spring Security API说:

  

对于具有单个参数(即集合类型)的方法,此参数将用作过滤器目标。

     

[...]

     

应该过滤的参数的名称(必须是非空的集合实例)如果方法包含单个集合参数,则可以省略此属性。

您尚未定义filterTarget但有两个参数,因此您获得了NullPointerException

您必须添加filterTarget参数:

@Transactional
@PreFilter(filterTarget="roles", value = "hasRole('admin') or (filterObject.name!='admin' and filterObject.name!='office_admin')")
public void create(JUser user, Set<JRole> roles) {
    JUser u2 = userDao.findByUsername(user.getUsername());
    if (u2 != null) {
        throw new MessageException("username duplicate!");
    }
    if (roles == null || roles.isEmpty()) {
        throw new MessageException("empty roles");
    }
    user.setRoles(roles);
    doCreate(user);
}