Playframework安全模块:您如何;“登录”;在功能测试中测试安全控制器?

Playframework安全模块:您如何;“登录”;在功能测试中测试安全控制器?,playframework,Playframework,编辑:我正在使用Play!版本1.2(生产版本) 我想测试由安全模块保护的控制器操作 类,因此我需要在测试控制器之前登录(否则我将被重定向到登录页面) 我已尝试在调用安全操作之前登录。下面是我的FunctionalTest的外观: @Test public void someTestOfASecuredAction() { Map<String, String> loginUserParams = new HashMap<String, String>();

编辑:我正在使用Play!版本1.2(生产版本)

我想测试由安全模块保护的控制器操作 类,因此我需要在测试控制器之前登录(否则我将被重定向到登录页面)

我已尝试在调用安全操作之前登录。下面是我的FunctionalTest的外观:

@Test
public void someTestOfASecuredAction() {
    Map<String, String> loginUserParams = new HashMap<String, String>(); 
    loginUserParams.put("username", "admin"); 
    loginUserParams.put("password", "admin");

    // Login here so the following request will be authenticated:
    Response response = POST("/login", loginUserParams);

    // The following is an action that requires an authenticated user:
    Map<String, String> params;
    params.put("someparam", "somevalue");
    response = POST("/some/secured/action", params);

    assertIsOk(response); // this always fails because it is a 302 redirecting to /login
}
@测试
public void someTestOfASecuredAction(){
Map loginUserParams=new HashMap();
loginUserParams.put(“用户名”、“管理员”);
loginUserParams.put(“密码”、“管理员”);
//在此处登录,以便对以下请求进行身份验证:
响应=POST(“/login”,loginUserParams);
//以下是需要经过身份验证的用户的操作:
映射参数;
参数put(“someparam”、“somevalue”);
响应=POST(“/some/secured/action”,参数);
assertesok(response);//这总是失败的,因为它是一个302重定向到/login
}
在代码中,我已经验证了登录帖子是否有效——它会导致重定向响应,并将位置设置为主页(这表示登录成功)

但在随后的安全操作调用中,我总是被重定向到 “/login”页-表示我以前的登录没有坚持第二次POST请求

查看FunctionalTest的源代码时,我发现有一个@Before拦截器可以清除所有cookie。我尝试在我自己的中介超类中重写这个拦截器(以保存cookies),但也没有成功

编辑:我把play.mvc.Before拦截器与org.junit.Before混淆了——前者用于play!控制器,后者用于JUnit测试。Funciontest中的@Before是JUnit拦截器,因此它应该对Cookie有任何影响(因为它在运行测试之前运行一次)

我不想为每个安全操作编写Selenium测试,因为几乎所有操作都是安全的。有没有办法“愚弄”安全模块,使其相信您已通过身份验证?或者是在功能测试中处理这个(看似常见)场景的其他非常明显的方法

提前感谢,

标记

编辑:工作代码,Codemwnci的答案标记为正确

Codemwnci的答案是正确的。以下是我在从一个请求到下一个请求之间保存cookie的解决方法:

@Test
public void someTestOfASecuredAction() {
    Map<String, String> loginUserParams = new HashMap<String, String>();
    loginUserParams.put("username", "admin");
    loginUserParams.put("password", "admin");
    Response loginResponse = POST("/login", loginUserParams);

    Request request = newRequest();
    request.cookies = loginResponse.cookies; // this makes the request authenticated
    request.url = "/some/secured/action";
    request.method = "POST";
    request.params.put("someparam", "somevalue");
    Response response = makeRequest(request);
    assertIsOk(response); // Passes!
}
@测试
public void someTestOfASecuredAction(){
Map loginUserParams=new HashMap();
loginUserParams.put(“用户名”、“管理员”);
loginUserParams.put(“密码”、“管理员”);
响应loginResponse=POST(“/login”,loginUserParams);
Request=newRequest();
request.cookies=loginResponse.cookies;//这会使请求经过身份验证
request.url=“/some/secured/action”;
request.method=“POST”;
request.params.put(“someparam”、“somevalue”);
响应=makeRequest(请求);
断言(响应);//通过!
}

我想一定是对@Before拦截器的功能有误解。它在测试执行之前执行。如果您的测试随后让您登录,则此事件发生在@Before代码执行之后,并且安全模块的结果应保存到Cookie中

因此,我只能假设Cookie没有随以下请求一起发送,因此,我建议尝试以下操作

登录后立即从响应对象获取安全cookie使用的cookie。创建一个请求对象并将Cookie设置为请求对象,然后调用POST方法,传入请求对象

我还没有测试过这段代码,所以不确定它对混合一个预构建的请求对象和传递一个URL会有什么反应,但不确定还有什么建议。

也许你看到了

我认为它应该会起作用

更新:我做了一些调试。我认为有两个错误

  • 在FunctionalTest中,如果Cookie没有最大值,则会将其丢弃
  • 在Scope.java的会话中,仅当cookie以前存在时,才会设置“TS”字段。因此,第一次设置会话cookie时,将其发送回服务器时会被忽略。如果您发出3个请求,那么它在请求2和3之间工作正常,但在请求1和2之间工作正常,因为在请求2上发送的cookie没有时间戳
  • 因此,如果不设置maxAge,bug 1会破坏它,如果设置maxAge,bug 2会破坏它


    更新2:我做了一些补丁来修复它:

    我想指出Mark S的最终解决方案中有一个错误

    这是错误的:
    request.url=“/some/secured/action”

    这是正确的:
    request.path=“/some/secured/action”


    “url”应该是完整的url。

    我也有同样的问题,但在Play 2.0.4的测试中

    我通过以下步骤和答案解决了问题,并通过检查API构建了以下解决方案:

    @Test
    public void listSomething() {
        running(fakeApplication(inMemoryDatabase()), new Runnable() {
            @Override
            public void run() {
                // LOGIN
                final Map<String, String> data = new HashMap<String, String>();
                data.put("email", "user@domain.com");
                data.put("password", "userpassword");
    
                Result result = callAction(
                controllers.routes.ref.Application.authenticate(),
                fakeRequest().withFormUrlEncodedBody(data));
    
                // RECOVER COOKIE FROM LOGIN RESULT
                final Cookie playSession = play.test.Helpers.cookie("PLAY_SESSION",
                                                                    result);
    
                // LIST SOMETHING (using cookie of the login result)
                result = callAction(controllers.routes.ref.Application.list(), 
                                    fakeRequest().withCookies(playSession));
    
                /* 
                 * WAS RECEIVING 'SEE_OTHER' (303) 
                 * BEFORE RECOVERING PLAY_SESSION COOKIE (BECAUSE NOT LOGGED IN).
                 *
                 * NOW, EXPECTED 'OK'
                 */ 
                assertThat(status(result)).isEqualTo(OK); 
                assertThat(contentAsString(result)).contains(
                        "Something found");
            }
        });
    }
    

    我不这么认为,因为我使用的是1.2版(将添加为问题的编辑)。起初,这似乎是有可能的,但我已经通过了一个测试,通过了微管理cookies,这表明会话正在多个请求中使用。如果你阅读了关于这个bug的最后评论,即使它被认为是关闭的,它似乎仍然存在。我也在使用play 1.2,我的FunctionalTest中的每个请求都会有一个新的会话,所以我遇到了与您相同的问题。如果你看FunctionalTest的代码,这里有一个savedCookies变量,看起来它应该在请求之间保存cookie,但对我来说它不起作用。到目前为止,我一直懒得调试它,但我认为这是一个功能最差的bug。i、 e.launchpad bug基本上需要重新打开。一种理论,看起来我已经在application.conf中注释掉了:#application.session.maxAge=1h;
    @Security.Authenticated(Secured.class)
    public static Result list() {
       return ok(list.render(...));
    }