[SPRING] 스프링 부트 2 및 OAuth2 / JWT 구성
SPRING스프링 부트 2 및 OAuth2 / JWT 구성
내 Spring boot 1.5 애플리케이션을 Spring Boot 2로 포팅하려고한다.
지금은 OAuth2 액세스 토큰을 얻을 수 없습니다.
이것은 스프링 부트 1.5에서 성공적으로 사용한 코드입니다.
public static String loginAndGetAccessToken(String username, String password, int port) {
ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
resourceDetails.setUsername(username);
resourceDetails.setPassword(password);
resourceDetails.setAccessTokenUri(String.format("http://localhost:%d/api/oauth/token", port));
resourceDetails.setClientId("clientapp");
resourceDetails.setClientSecret("123456");
resourceDetails.setGrantType("password");
resourceDetails.setScope(Arrays.asList("read", "write"));
DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);
restTemplate.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
return restTemplate.getAccessToken().toString();
}
다음 예외가있는 경우 실패합니다.
java.lang.IllegalStateException: An OAuth 2 access token must be obtained or an exception thrown.
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:124)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
http : // localhost : % d / api / oauth / token 엔드 포인트가 현재 보안 상태이며 액세스 할 수 없습니다.
이것은 내 구성입니다.
OAuth2 서버 구성
@Configuration
public class OAuth2ServerConfig {
public static final String RESOURCE_ID = "restservice";
public static final String EXAMPLE_CLIENT_ID = "example_client_id";
@Value("${jwt.access.token.converter.signing.key}")
private String jwtAccessTokenConverterSigningKey;
@Value("${jwt.access.token.validity.seconds}")
private int accessTokenValiditySeconds;
@Autowired
private UserDetailsService userDetailsService;
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setAccessTokenValiditySeconds(accessTokenValiditySeconds);
return tokenServices;
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new UserAwareAccessTokenConverter();
converter.setSigningKey(jwtAccessTokenConverterSigningKey);
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
userTokenConverter.setUserDetailsService(userDetailsService);
accessTokenConverter.setUserTokenConverter(userTokenConverter);
converter.setAccessTokenConverter(accessTokenConverter);
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Value("${jwt.access.token.validity.seconds}")
private int accessTokenValiditySeconds;
@Autowired
private TokenStore tokenStore;
@Autowired
private TokenEnhancer tokenEnhancer;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// @formatter:off
endpoints
.tokenStore(tokenStore)
.tokenEnhancer(tokenEnhancer)
.authenticationManager(this.authenticationManager);
// @formatter:on
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter:off
clients
.inMemory()
.withClient("clientapp")
.authorizedGrantTypes("password","refresh_token")
.authorities("ROLE_CLIENT")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456")
.and()
.withClient(EXAMPLE_CLIENT_ID)
.authorizedGrantTypes("implicit")
.scopes("read", "write")
.autoApprove(true)
.and()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(accessTokenValiditySeconds);
// @formatter:on
}
}
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Autowired
private ResourceServerTokenServices tokenService;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
// @formatter:off
resources
.resourceId(RESOURCE_ID)
.tokenServices(tokenService);
// @formatter:on
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.antMatcher("/v1.0/**").authorizeRequests()
.antMatchers("/v1.0/search/**").permitAll()
.antMatchers("/v1.0/users/**").permitAll()
.antMatchers("/v1.0/decisions/**").permitAll()
.antMatchers("/v1.0/votes/**").permitAll()
.antMatchers("/v1.0/likes/**").permitAll()
.antMatchers("/v1.0/likeables/**").permitAll()
.antMatchers("/v1.0/flags/**").permitAll()
.antMatchers("/v1.0/flagtypes/**").permitAll()
.antMatchers("/v1.0/flaggables/**").permitAll()
.antMatchers("/v1.0/comments/**").permitAll()
.antMatchers("/v1.0/commentables/**").permitAll()
.antMatchers("/v1.0/subscribables/**").permitAll()
.antMatchers("/v1.0/favoritables/**").permitAll()
.antMatchers("/v1.0/import/**").permitAll()
.antMatchers("/v1.0/tags/**").permitAll()
.antMatchers("/v1.0/medias/**").permitAll()
.antMatchers("/swagger**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(STATELESS);
// @formatter:on
}
}
}
WebMvcConfig
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/login").setViewName("login");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
WebSecurityConfig
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Value("${logout.success.url}")
private String logoutSuccessUrl;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.cors()
.and()
.csrf().ignoringAntMatchers("/v1.0/**", "/logout")
.and()
.authorizeRequests()
.antMatchers("/oauth/authorize").authenticated()
//Anyone can access the urls
.antMatchers("/images/**").permitAll()
.antMatchers("/signin/**").permitAll()
.antMatchers("/v1.0/**").permitAll()
.antMatchers("/auth/**").permitAll()
.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/**").hasAuthority(Permission.READ_ACTUATOR_DATA)
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl(logoutSuccessUrl)
.permitAll();
// @formatter:on
}
/**
* Configures the authentication manager bean which processes authentication requests.
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Spring Boot 2와 함께 작동시키기 위해서는 무엇을 변경해야합니까?
해결법
-
==============================
1.OAuth2 서버 구성
OAuth2 서버 구성
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import com.decisionwanted.api.security.authentication.UserAwareAccessTokenConverter; import com.decisionwanted.domain.service.user.social.UserDetailsService; @Configuration public class OAuth2ServerConfig { public static final String RESOURCE_ID = "restservice"; public static final String CLIENT_ID = "client_id"; @Value("${jwt.access.token.converter.signing.key}") private String jwtAccessTokenConverterSigningKey; @Value("${jwt.access.token.validity.seconds}") private int accessTokenValiditySeconds; @Autowired private UserDetailsService userDetailsService; @Bean @Primary public DefaultTokenServices tokenServices() { DefaultTokenServices tokenServices = new DefaultTokenServices(); tokenServices.setTokenStore(tokenStore()); tokenServices.setSupportRefreshToken(true); tokenServices.setAccessTokenValiditySeconds(accessTokenValiditySeconds); return tokenServices; } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new UserAwareAccessTokenConverter(); converter.setSigningKey(jwtAccessTokenConverterSigningKey); DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter(); userTokenConverter.setUserDetailsService(userDetailsService); accessTokenConverter.setUserTokenConverter(userTokenConverter); converter.setAccessTokenConverter(accessTokenConverter); return converter; } @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Value("${jwt.access.token.validity.seconds}") private int accessTokenValiditySeconds; @Autowired private TokenStore tokenStore; @Autowired private TokenEnhancer tokenEnhancer; private PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // @formatter:off endpoints .tokenStore(tokenStore) .tokenEnhancer(tokenEnhancer) .authenticationManager(this.authenticationManager); // @formatter:on } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // @formatter:off clients .inMemory() .withClient("clientapp") .authorizedGrantTypes("password","refresh_token") .authorities("ROLE_CLIENT") .scopes("read", "write") .resourceIds(RESOURCE_ID) .secret(passwordEncoder.encode("changeit")) .and() .withClient(CLIENT_ID) .authorizedGrantTypes("implicit") .scopes("read", "write") .autoApprove(true) .and() .withClient("my-trusted-client") .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit") .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT") .scopes("read", "write", "trust") .accessTokenValiditySeconds(accessTokenValiditySeconds); // @formatter:on } } @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Autowired private ResourceServerTokenServices tokenService; @Override public void configure(ResourceServerSecurityConfigurer resources) { // @formatter:off resources .resourceId(RESOURCE_ID) .tokenServices(tokenService); // @formatter:on } @Override public void configure(HttpSecurity http) throws Exception { // @formatter:off http .antMatcher("/v1.0/**").authorizeRequests() .antMatchers("/v1.0/search/**").permitAll() .antMatchers("/v1.0/users/**").permitAll() .antMatchers("/v1.0/decisiongroups/**").permitAll() .antMatchers("/swagger**").permitAll() .anyRequest().authenticated() .and() .cors() .and() .csrf().disable() .sessionManagement() .sessionCreationPolicy(STATELESS); // @formatter:on } } }
WebMvcConfig
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/login").setViewName("login"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } }
WebSecurityConfig
import java.util.Arrays; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import com.decisionwanted.domain.model.neo4j.security.permission.Permission; import com.decisionwanted.domain.service.user.social.UserDetailsService; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Value("${logout.success.url}") private String logoutSuccessUrl; PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); @Override protected void configure(HttpSecurity http) throws Exception { // @formatter:off http .cors() .and() .csrf().ignoringAntMatchers("/v1.0/**", "/logout") .and() .authorizeRequests() .antMatchers("/oauth/authorize").authenticated() //Anyone can access the urls .antMatchers("/images/**").permitAll() .antMatchers("/signin/**").permitAll() .antMatchers("/v1.0/**").permitAll() .antMatchers("/auth/**").permitAll() .antMatchers("/actuator/health").permitAll() .antMatchers("/actuator/**").hasAuthority(Permission.READ_ACTUATOR_DATA) .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .loginProcessingUrl("/login") .failureUrl("/login?error=true") .usernameParameter("username") .passwordParameter("password") .permitAll() .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl(logoutSuccessUrl) .permitAll(); // @formatter:on } /** * Configures the authentication manager bean which processes authentication requests. */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("*")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token")); configuration.setExposedHeaders(Arrays.asList("x-auth-token")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
SecurityTestUtils
import java.util.Arrays; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; public class SecurityTestUtils { public static final String AUTH_HEADER_NAME = "Authorization"; public static final String AUTH_COOKIE_NAME = "AUTH-TOKEN"; public static String loginAndGetAccessToken(String username, String password, int port) { ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails(); resourceDetails.setUsername(username); resourceDetails.setPassword(password); resourceDetails.setAccessTokenUri(String.format("http://localhost:%d/api/oauth/token", port)); resourceDetails.setClientId("clientapp"); resourceDetails.setClientSecret("changeit"); resourceDetails.setGrantType("password"); resourceDetails.setScope(Arrays.asList("read", "write")); DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); restTemplate.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter())); return restTemplate.getAccessToken().toString(); } }
-
==============================
2.이 url / api / oauth / token을 허용하도록 봄 보안 설정을 시도하십시오. 이걸로 무엇을 할지를 정의하지 않은 것으로 보입니다.
이 url / api / oauth / token을 허용하도록 봄 보안 설정을 시도하십시오. 이걸로 무엇을 할지를 정의하지 않은 것으로 보입니다.
from https://stackoverflow.com/questions/45391264/spring-boot-2-and-oauth2-jwt-configuration by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] 런타임시 스프링 / 스프링 부트 속성 설정 / 무시 (0) | 2019.03.21 |
---|---|
[SPRING] Eclipse : javadoc 추가 (0) | 2019.03.21 |
[SPRING] Projection List에 의해 중첩 된 객체를 가져 오기위한 Hibernate Criteria를 작성하는 방법? (0) | 2019.03.21 |
[SPRING] MongoDB의 트랜잭션 (0) | 2019.03.21 |
[SPRING] 분리 된 객체는 최대 절전 모드에서 어떻게 작동합니까? (0) | 2019.03.21 |