Я разрабатываю весеннее загрузочное веб-приложение с конечными точками отдыха с защитой OAuth 2.Все конечные точки надежно защищены паролем grant_type во время выполнения.
Но тестовые случаи с MockMvc не учитывают токен.Он может получить доступ к защищенным конечным точкам без токена.
В чем причина этого.
AuthorizationServerConfig.java
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
static final String CLIENT_ID = "web-client";
static final String CLIENT_SECRET = "web-secret";
static final String GRANT_TYPE_PASSWORD = "password";
static final String AUTHORIZATION_CODE = "authorization_code";
static final String REFRESH_TOKEN = "refresh_token";
static final String IMPLICIT = "implicit";
static final String SCOPE_READ = "read";
static final String SCOPE_WRITE = "write";
static final String TRUST = "trust";
static final int ACCESS_TOKEN_VALIDITY_SECONDS = 1*60*60;
static final int REFRESH_TOKEN_VALIDITY_SECONDS = 6*60*60;
@Autowired
private TokenStore tokenStore;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(CLIENT_ID)
.secret(CLIENT_SECRET)
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT )
.scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(tokenEnhancer(), defaultAccessTokenConverter()));
endpoints.tokenStore(tokenStore).authenticationManager(authenticationManager)
.accessTokenConverter(defaultAccessTokenConverter()).tokenEnhancer(tokenEnhancerChain);
}
@Bean
public TokenStore tokenStore(){
return new JwtTokenStore(defaultAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter defaultAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
}
ResourceServerConfig.java
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource-server-rest-api";
@Autowired
private DefaultTokenServices tokenServices;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID)
.tokenServices(createTokenServices());
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().and()
.authorizeRequests()
.antMatchers("/auth/user/recovery").permitAll()
.antMatchers("/**").authenticated();
}
@Bean
public DefaultTokenServices createTokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(createTokenStore());
return defaultTokenServices;
}
@Bean
public TokenStore createTokenStore() {
return new JwtTokenStore(createJwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter createJwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
converter.setAccessTokenConverter(new JwtConverter());
return converter;
}
public static class JwtConverter extends DefaultAccessTokenConverter implements JwtAccessTokenConverterConfigurer {
@Override
public void configure(JwtAccessTokenConverter converter) {
converter.setAccessTokenConverter(this);
}
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
OAuth2Authentication auth = super.extractAuthentication(map);
auth.setDetails(map); //this will get spring to copy JWT content into Authentication
return auth;
}
}
}
SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder encoder;
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(encoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().and()
.authorizeRequests()
.antMatchers("/auth/user/recovery").permitAll();
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
RestControllerTest.java
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
@SpringBootTest(classes = Main.class)
@ActiveProfiles("test")
public class RestControllerTest {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext wac;
@Autowired
private FilterChainProxy springSecurityFilterChain;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).apply(springSecurity())
.addFilter(springSecurityFilterChain).build();
}
@Test
public void test() throws Exception {
String accessToken = obtainAccessToken("test",
"test");
ResultActions result
= mockMvc.perform(post("/test")
//.header("Authorization", "Bearer " + accessToken)
.contentType(MediaType.APPLICATION_JSON)
.andExpect(status().isOk());
System.out.println(result.andReturn().getResponse().getContentAsString());
}
private String obtainAccessToken(String username, String password) throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "password");
params.add("username", username);
params.add("password", password);
String base64ClientCredentials = new String(Base64.encodeBase64("web-client:web-secret".getBytes()));
ResultActions result
= mockMvc.perform(post("/oauth/token")
.params(params)
.header("Authorization", "Basic " + base64ClientCredentials)
.accept("application/json;charset=UTF-8"))
.andExpect(status().isOk());
String resultString = result.andReturn().getResponse().getContentAsString();
JacksonJsonParser jsonParser = new JacksonJsonParser();
return jsonParser.parseMap(resultString).get("access_token").toString();
}
}
MyRestController.java
@RestController
@CrossOrigin
@RequestMapping(value = "")
public class MyRestController implements MyRestControllerInf {
@Override
@RequestMapping(value = "/test", method = RequestMethod.POST)
public ResponseEntity test() {
return new ResponseEntity("test", HttpStatus.OK);
}
}