使用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());
}
}