한 애플리케이션 (스프링 부트 애플리케이션)에서 다른 애플리케이션 (angularjs)에서 REST 엔드 포인트를 호출하려고합니다. 애플리케이션은 다음 호스트 및 포트에서 실행 중입니다.
- REST 애플리케이션, 스프링 부트 사용,
http://localhost:8080
- angularjs를 사용하는 HTML 애플리케이션,
http://localhost:50029
나는 또한 spring-security
봄 부팅 응용 프로그램과 함께 사용 하고 있습니다. HTML 애플리케이션에서 REST 애플리케이션에 대해 인증 할 수 있지만 이후에도 여전히 REST 엔드 포인트에 액세스 할 수 없습니다. 예를 들어, 다음과 같이 정의 된 angularjs 서비스가 있습니다.
adminServices.factory('AdminService', ['$resource', '$http', 'conf', function($resource, $http, conf) {
var s = {};
s.isAdminLoggedIn = function(data) {
return $http({
method: 'GET',
url: 'http://localhost:8080/api/admin/isloggedin',
withCredentials: true,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
};
s.login = function(username, password) {
var u = 'username=' + encodeURI(username);
var p = 'password=' + encodeURI(password);
var r = 'remember_me=1';
var data = u + '&' + p + '&' + r;
return $http({
method: 'POST',
url: 'http://localhost:8080/login',
data: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
};
return s;
}]);
angularjs 컨트롤러는 다음과 같습니다.
adminControllers.controller('LoginController', ['$scope', '$http', 'AdminService', function($scope, $http, AdminService) {
$scope.username = '';
$scope.password = '';
$scope.signIn = function() {
AdminService.login($scope.username, $scope.password)
.success(function(d,s) {
if(d['success']) {
console.log('ok authenticated, call another REST endpoint');
AdminService.isAdminLoggedIn()
.success(function(d,s) {
console.log('i can access a protected REST endpoint after logging in');
})
.error(function(d, s) {
console.log('huh, error checking to see if admin is logged in');
$scope.reset();
});
} else {
console.log('bad credentials?');
}
})
.error(function(d, s) {
console.log('huh, error happened!');
});
};
}]);
호출에하는 http://localhost:8080/api/admin/isloggedin
, 내가 얻을 401 Unauthorized
.
REST 애플리케이션 측에는 다음과 같은 CORS 필터가 있습니다.
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
@Override
public void destroy() { }
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "http://localhost:50029");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token");
response.setHeader("Access-Control-Allow-Credentials", "true");
if(!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
chain.doFilter(req, res);
}
}
@Override
public void init(FilterConfig config) throws ServletException { }
}
내 봄 보안 구성은 다음과 같습니다.
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private JsonAuthSuccessHandler jsonAuthSuccessHandler;
@Autowired
private JsonAuthFailureHandler jsonAuthFailureHandler;
@Autowired
private JsonLogoutSuccessHandler jsonLogoutSuccessHandler;
@Autowired
private AuthenticationProvider authenticationProvider;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Value("${rememberme.key}")
private String rememberMeKey;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/", "/admin", "/css/**", "/js/**", "/fonts/**", "/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(jsonAuthSuccessHandler)
.failureHandler(jsonAuthFailureHandler)
.permitAll()
.and()
.logout()
.deleteCookies("remember-me", "JSESSIONID")
.logoutSuccessHandler(jsonLogoutSuccessHandler)
.permitAll()
.and()
.rememberMe()
.userDetailsService(userDetailsService)
.tokenRepository(persistentTokenRepository)
.rememberMeCookieName("REMEMBER_ME")
.rememberMeParameter("remember_me")
.tokenValiditySeconds(1209600)
.useSecureCookie(false)
.key(rememberMeKey);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider);
}
}
모든 핸들러는 {success: true}
사용자가 로그인했는지, 인증에 실패했는지, 로그 아웃했는지에 따라 JSON 응답을 작성 합니다. RestAuthenticationEntryPoint
외모는 다음을 좋아한다.
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException ex)
throws IOException, ServletException {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
내가 무엇을 놓치고 있거나 잘못하고 있는지에 대한 아이디어가 있습니까?
답변
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class SimpleCORSFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);
public SimpleCORSFilter() {
log.info("SimpleCORSFilter init");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
이 필터를 추가로 정의 할 필요가 없습니다.이 클래스를 추가하기 만하면됩니다. 봄이 스캔되어 추가됩니다. SimpleCORSFilter. 예 : spring-enable-cors
답변
나는 비슷한 상황에 처해 있었다. 연구와 테스트를 마친 후 발견 한 내용은 다음과 같습니다.
-
Spring Boot에서 전역 CORS를 활성화하는 권장 방법은 Spring MVC 내에서 선언하고 다음과
@CrossOrigin
같이 세분화 된 구성 과 결합하는 것입니다.@Configuration public class CorsConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*") .allowedHeaders("*"); } }; } }
-
이제 Spring Security를 사용하고 있기 때문에 Spring Security 수준에서 CORS를 활성화해야하며 Spring MVC 수준에서 정의 된 구성을 다음과 같이 활용할 수 있도록해야합니다.
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and()... } }
다음 은 Spring MVC 프레임 워크에서 CORS 지원을 설명하는 매우 훌륭한 튜토리얼입니다.
답변
필터를 사용하지 않거나 구성 파일없이 CORS를 활성화하려면 다음을 추가하십시오.
@CrossOrigin
컨트롤러의 맨 위로 이동하면 작동합니다.
답변
위의 다른 답변을 기반으로 빌드하려면 Spring 보안이 포함 된 Spring 부트 REST 서비스 애플리케이션 (Spring MVC 아님)이있는 경우 Spring 보안을 통해 CORS를 활성화하는 것으로 충분합니다 (Spring MVC를 사용하는 경우 WebMvcConfigurer
Yogen에서 언급 한대로 Bean 을 사용하면 Spring 보안으로가는 길은 거기에 언급 된 CORS 정의에 위임됩니다)
따라서 다음을 수행하는 보안 구성이 필요합니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//other http security config
http.cors().configurationSource(corsConfigurationSource());
}
//This can be customized as required
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
List<String> allowOrigins = Arrays.asList("*");
configuration.setAllowedOrigins(allowOrigins);
configuration.setAllowedMethods(singletonList("*"));
configuration.setAllowedHeaders(singletonList("*"));
//in case authentication is enabled this flag MUST be set, otherwise CORS requests will fail
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
이 링크에는 동일한 정보가 더 있습니다. https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors
노트 :
- prod 배포 응용 프로그램에 대해 모든 출처 (*)에 대해 CORS를 활성화하는 것이 항상 좋은 생각은 아닙니다.
- CSRF는 문제없이 Spring HttpSecurity 사용자 정의를 통해 활성화 될 수 있습니다.
- Spring을 사용하여 앱에서 인증을 활성화 한 경우 (
UserDetailsService
예 : a 를 통해 ) 다음을configuration.setAllowCredentials(true);
추가해야합니다.
Spring boot 2.0.0.RELEASE (즉, Spring 5.0.4.RELEASE 및 Spring security 5.0.3.RELEASE)에 대해 테스트되었습니다.
답변
임 사용 spring boot 2.1.0
나는 하고 나를 위해 일한 것은
A. 다음을 통해 cors 매핑을 추가합니다.
@Configuration
public class Config implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
}
B. HttpSecurity
봄 보안을 위해 아래 구성을 my 에 추가하십시오.
.cors().configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedHeaders(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
return config;
}
})
또한 Zuul 프록시의 경우이 INSTEAD OF A 및 BHttpSecurity.cors()
를 사용할 수 있습니다 (스프링 보안에서 활성화 하기 위해 사용).
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
답변
이것은 나를 위해 작동합니다.
@Configuration
public class MyConfig extends WebSecurityConfigurerAdapter {
//...
@Override
protected void configure(HttpSecurity http) throws Exception {
//...
http.cors().configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedHeaders(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
return config;
}
});
//...
}
//...
}
답변
저에게 스프링 보안이 사용될 때 100 % 효과가 있었던 유일한 것은 여분의 필터와 빈의 모든 추가 보풀과 간접적 인 “마법”사람들이 그들에게 효과가 있지만 저에게는 그렇지 않다고 제안하는 모든 것을 건너 뛰는 것입니다.
대신 일반으로 필요한 헤더를 작성하도록 강제하십시오 StaticHeadersWriter
.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// your security config here
.authorizeRequests()
.antMatchers(HttpMethod.TRACE, "/**").denyAll()
.antMatchers("/admin/**").authenticated()
.anyRequest().permitAll()
.and().httpBasic()
.and().headers().frameOptions().disable()
.and().csrf().disable()
.headers()
// the headers you want here. This solved all my CORS problems!
.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", "*"))
.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Methods", "POST, GET"))
.addHeaderWriter(new StaticHeadersWriter("Access-Control-Max-Age", "3600"))
.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Credentials", "true"))
.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Headers", "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization"));
}
}
이것이 제가 발견 한 가장 직접적이고 명시적인 방법입니다. 누군가에게 도움이되기를 바랍니다.