Unit testing with Spring Security

后端 未结 11 2182
暖寄归人
暖寄归人 2020-11-29 15:19

My company has been evaluating Spring MVC to determine if we should use it in one of our next projects. So far I love what I\'ve seen, and right now I\'m taking a look at th

11条回答
  •  旧巷少年郎
    2020-11-29 15:45

    You are quite right to be concerned - static method calls are particularly problematic for unit testing as you cannot easily mock your dependencies. What I am going to show you is how to let the Spring IoC container do the dirty work for you, leaving you with neat, testable code. SecurityContextHolder is a framework class and while it may be ok for your low-level security code to be tied to it, you probably want to expose a neater interface to your UI components (i.e. controllers).

    cliff.meyers mentioned one way around it - create your own "principal" type and inject an instance into consumers. The Spring tag introduced in 2.x combined with a request scope bean definition, and the factory-method support may be the ticket to the most readable code.

    It could work like following:

    public class MyUserDetails implements UserDetails {
        // this is your custom UserDetails implementation to serve as a principal
        // implement the Spring methods and add your own methods as appropriate
    }
    
    public class MyUserHolder {
        public static MyUserDetails getUserDetails() {
            Authentication a = SecurityContextHolder.getContext().getAuthentication();
            if (a == null) {
                return null;
            } else {
                return (MyUserDetails) a.getPrincipal();
            }
        }
    }
    
    public class MyUserAwareController {        
        MyUserDetails currentUser;
    
        public void setCurrentUser(MyUserDetails currentUser) { 
            this.currentUser = currentUser;
        }
    
        // controller code
    }
    

    Nothing complicated so far, right? In fact you probably had to do most of this already. Next, in your bean context define a request-scoped bean to hold the principal:

    
        
    
    
    
        
        
    
    

    Thanks to the magic of the aop:scoped-proxy tag, the static method getUserDetails will be called every time a new HTTP request comes in and any references to the currentUser property will be resolved correctly. Now unit testing becomes trivial:

    protected void setUp() {
        // existing init code
    
        MyUserDetails user = new MyUserDetails();
        // set up user as you wish
        controller.setCurrentUser(user);
    }
    

    Hope this helps!

提交回复
热议问题