使用Spring Security的OAuth2登录,对控制器进行测试

我想做的事

在使用外部Idp进行登录实现的应用程序中,当要对控制器进行测试时,希望以已登录状态下进行控制器的测试。

如果有一种可以通过相当氛围来完成的感觉,那么就请告诉我Spring是否提供了这样的选项。

考察对象是这样的家伙

build.gradle(摘要)

buildscript {
    ext {
        defaultEncoding  = 'UTF-8'
        compatibility    = 1.8
        springBootVersion = '2.0.0.RELEASE'
    }
    repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/snapshot" }
        maven { url "https://repo.spring.io/milestone" }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

repositories {
    mavenCentral()
    maven { url "https://repo.spring.io/snapshot" }
    maven { url "https://repo.spring.io/milestone" }
}

dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-actuator')
    compile('org.springframework.boot:spring-boot-starter-mail')
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('org.springframework.boot:spring-boot-starter-web')

    compile('org.springframework.security:spring-security-oauth2-client')
    compile('org.springframework.security:spring-security-oauth2-jose')

    runtime('org.springframework.boot:spring-boot-devtools')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile('org.springframework.security:spring-security-test')
    testCompile group: 'com.h2database', name: 'h2', version: '1.4.196'

}

应用程序配置文件

####################################################################################################
# spring
####################################################################################################
spring:
  security.oauth2.client.registration:
      google:
        client-id: xxxxx
        client-secret: xxxxx

控制器

import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;


/**
 * Controller for login.
 */
@Controller
public class LoginController {

    /**
     * 認証後に表示するページ。
     *
     * @param authentication authentication info
     * @return user info
     */
    @GetMapping("/")
    public String index(final OAuth2AuthenticationToken authentication) {
        return "index";
    }

    /**
     * ログイン画面を表示する.
     *
     * @return login page
     */
    @GetMapping("/login")
    public String viewLogin() {
        return "login";
    }
}

设置

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * Configuration about security.
 *
 * @since
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final HttpSecurity http) throws Exception { // NOPMD
        // Define NOPMD because HttpSecurity$authorizeRequests throws Exception.
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .logout().permitAll()
                .and()
                .oauth2Login()
                .loginPage("/login").permitAll();
    }
}

考えられる。

嘲笑用户

当这个标注被添加时,可以以经过认证的状态来访问。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.springframework.security.test.context.support.WithSecurityContext;

/**
 * Annotation for test with OAuth2 Authentication.
 */
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithOAuth2SecurityContextFactory.class)
public @interface WithMockOAuth2User {

    /**
     * Username.
     *
     * @return username
     */
    String username() default "testuser";

}

安全上下文工厂

无论如何,只需要一种选择,原文的内容可以被改写成:
无论如何,我们只需创建一个OAuth2AuthenticationToken对象并将其添加到SecurityContext中。如果想要以XXX用户身份进行测试,似乎在这里进行一些相应的操作即可。

import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.test.context.support.WithSecurityContextFactory;

/**
 * OAuth2 token provider(put to {@SecurityContext} for integration test.
 */
public class WithOAuth2SecurityContextFactory implements WithSecurityContextFactory<WithMockOAuth2User> {

    /**
     * Create security context with OAuth2 token.
     *
     * @param user user
     * @return security context
     */
    @Override
    public SecurityContext createSecurityContext(final WithMockOAuth2User user) {
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        Map<String, Object> claims = new HashMap<>();
        claims.put("sub", "testsub");

        Set<GrantedAuthority> authorities = new HashSet<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));

        OAuth2User oidcUser = new DefaultOidcUser(authorities, new OidcIdToken("sampletoken", Instant.MIN, Instant.MAX, claims));
        OAuth2AuthenticationToken token = new OAuth2AuthenticationToken(oidcUser, authorities, "test-client");
        context.setAuthentication(token);
        return context;
    }
}

考试班

只要在@WithMockOAuth2User下,就可以在登录后的世界中访问URL。


import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;



/**
 * Test for {@link LoginController}.
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class LoginControllerTest {

    /**
     * MockMvc.
     */
    @Autowired
    private MockMvc mockMvc;

    /**
     * Test for index page before login (redirect to login page).
     *
     * @throws Exception exception
     */
    @Test
    public void indexBeforeLogin() throws Exception { // NOPMD
        mockMvc.perform(get("/")).andExpect(status().is3xxRedirection());
    }

    /**
     * Test for index page after login.
     *
     * @throws Exception exception
     */
    @Test
    @WithMockOAuth2User()
    public void indexAfterLogin() throws Exception { // NOPMD
        mockMvc.perform(get("/")).andExpect(status().isOk());
    }


    /**
     * Test for login page.
     *
     * @throws Exception exception
     */
    @Test
    public void viewLogin() throws Exception { // NOPMD
        mockMvc.perform(get("/login")).andExpect(status().isOk());
    }
}

bannerAds