No 'Access-Control-Allow-Origin' header > Allow requests from production website to internal Spring application - spring

I would like to test my application (Angular) in production. So I transfered all necessary files and I successfully started the Spring-Boot app (jar).
Angular is sending requests to 127.0.0.1 :
apiHost: 'http://127.0.0.1:8080/'
And the Configurer is like following :
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#CrossOrigin("*")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "userService")
private UserDetailsService userDetailsService;
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().
authorizeRequests()
.antMatchers("/generate-token", "/signup","/config","/saveConfig/","/get/file/*").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
This gives the following errors :
Failed to load http://127.0.0.1:8080/salesStats: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://yzyzyzyz.com' is therefore not allowed access. The response had HTTP status code 503.
Am I doing something wrong ?

Related

Spring erro Cors

I have a problem with the spring Cors.
I get this error on chome:
Access to XMLHttpRequest at 'http://localhost:8080/api/informationWS' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
My file WebSecurityConfigurerAdapter
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LoginService loginService;
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(loginService)
.passwordEncoder(this.passwordEncoderAutentication());
}
#Bean
public PasswordEncoder passwordEncoderAutentication() {
String idForEncode = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
return passwordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
My file ResourceServerConfigurerAdapter
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/informationWS").permitAll()
.antMatchers(HttpMethod.POST, "/api/work").authenticated()
.anyRequest().denyAll();
}
}
I tried to work with Cors in the two ways below, but neither of them worked, generating the same error
My file cors
#Configuration
#EnableWebMvc
public class Cors implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:4200");
}
}
My file Cors2
#Configuration
public class Cors {
#Bean
public FilterRegistrationBean<CorsFilter> corsFilterFilterRegistrationBean(){
List<String> host = Arrays.asList("http://localhost:4200");
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(host);
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", corsConfiguration);
CorsFilter corsFilter = new CorsFilter(source);
FilterRegistrationBean<CorsFilter> filter = new FilterRegistrationBean<>(corsFilter);
filter.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filter;
}
}
What you could try/check:
check if the application code is executed - maybe server stops execution for some reason, and so your spring code cannot add a header.
maybe there is preflight request and server does not allow it (so again server stopped execution and your backend code could not send the header)
maybe you yourself stop script somewhere before the header is added, like System.exit(0);
maybe there is redirect to code which does not add header, for example some exception
try running the request from Postman - you should not get the error and maybe you will see something surprising.
does this .antMatchers(HttpMethod.GET, "/api/informationWS") really match the request? Maybe there is a way to add wildcard just for testing and see if it works? Are you sending GET request?
More details, technologies different but concept same: https://dariuscoder.com/2021/09/16/how-to-debug-cors/

CSRF disable doesn't work in Spring Security

I'm currently testing a REST API with Spring Security. Because this is just testing, I'm disabling CSRF. With the code below, a Postman get request to /users works perfectly, but any other type of request from Postman (e.g. post, delete, put) give me back a 403 FORBIDDEN error. Really can't figure it out.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
Do you have any cors configuration setup to allow methods other than "GET"?
You can create a CorsConfigurationSource bean that should allow access to those types of requests
#Configuration
public class CorsConfig {
private static final String[] CORS_ALLOWED_HEADERS = new String[] {
"*",
};
private static final String[] CORS_ALLOWED_METHODS = new String[] {
"GET",
"POST",
"PUT",
"OPTIONS"
};
private static final String CORS_API_PATTERN = "/api/**";
private static final long CORS_MAX_AGE = 3600;
#Value("${cors.allowed-origins}")
private String[] corsAllowedOrigins;
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(corsAllowedOrigins));
configuration.setAllowedMethods(Arrays.asList(CORS_ALLOWED_METHODS));
configuration.setAllowCredentials(true);
configuration.setMaxAge(CORS_MAX_AGE);
configuration.setAllowedHeaders(Arrays.asList(CORS_ALLOWED_HEADERS));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(CORS_API_PATTERN, configuration);
return source;
}

CORS problems with Spring Security and Websocket

I am developing an Ionic app based on a Spring backend.
I implemented Spring Security with JWT authentication. My app will have a chat room where users can talk each other in private or public chat. So, I am implementing a WebSocket system in order to get all updates in real time.
This is my Security Configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private UserDetailsService userDetailsService;
private AuthenticationManager authenticationManager;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(this.userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
// configurazione Cors per poter consumare le api restful con richieste ajax
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.setAllowedMethods(Arrays.asList("POST, PUT, GET, OPTIONS, DELETE"));
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().cors().and()
.authorizeRequests()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/image/**").permitAll()
.antMatchers("/socket/**").permitAll()
.antMatchers("/public/**").permitAll().and()
.authorizeRequests().anyRequest().authenticated().and();
httpSecurity.headers().cacheControl();
}
#Bean
public AuthenticationManager customAuthenticationManager() throws Exception {
return authenticationManager();
}
}
This is my WebSocket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/socket")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/chat")
.enableSimpleBroker("/subscribe");
}
}
In this condition, I am currently facing this error:
Access to XMLHttpRequest at
'http://localhost:8080/SpringApp/socket/info?t=1547732425329' from
origin 'http://localhost:8100' has been blocked by CORS policy: The
value of the 'Access-Control-Allow-Origin' header in the response must
not be the wildcard '*' when the request's credentials mode is
'include'. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
Each call is working (i am perfectly authorized with jwt) but the WebSocket can't work.
So, I tried to simply remove the .cors() in configure method in my security configuration class. This lead me to an opposite problem:
error in chrome
Indeed, now WebSocket works perfectly, instead each api call gives me 401.
What's the correct way to resolve this problem?
Thank you
Yeah, I got the same error when I was working in a related issue in one of my projects. The solution was that I had to set the allowed-origin header value to the URL of my application. The wildcard value (*) is not allowed if you send credentials.

Angular 6 Http Interceptors, request headers not modified

I created an interceptor to add an authorization header to each request sent by the client, here is the code :
import { HttpInterceptor, HttpRequest, HttpHandler, HttpHeaderResponse, HttpSentEvent, HttpProgressEvent, HttpResponse, HttpUserEvent, HttpEvent, HttpHeaders } from "#angular/common/http";
import { Observable } from "rxjs";
import { Injectable } from "#angular/core";
#Injectable()
export class AuthenticationInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log(localStorage.getItem('jwtToken'));
if(localStorage.getItem('jwtToken')){
const request = req.clone({
setHeaders: {
Authorization: `bearer ${localStorage.getItem('jwtToken')}`
}
});
console.log(request.headers.get("Authorization"));
return next.handle(request);
}
return next.handle(req);
}
}
When a request is sent the function intercept is called and the authorization header is correclty set with the token value in the variable "request" as you can see there :
token console screenshot
But the authorization header doesn't appear in the request sent by my browser : network request headers and the backend cannot resolve the token.
Do you know why ?
Here is my spring config:
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public final static String AUTHORIZATION_HEADER = "Authorization";
#Autowired
UserService userService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(getProvider());
}
#Bean
public JwtTokenFilter jwtAuthenticationFilter() {
return new JwtTokenFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/auth/**")
.permitAll()
.anyRequest()
.authenticated();
}
#Bean
public AuthenticationProvider getProvider() {
AppAuthProvider provider = new AppAuthProvider();
provider.setUserDetailsService(userService);
return provider;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
CorsConfig.java
#Configuration
public class CorsConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:4200")
.allowedMethods("*")
.allowedHeaders("*");
}
};
}
}
Your problem resides into backend services. For security reasons by default only some headers are accepted, the others are ignored.
To fix your problem you need to setup custom accepted headers. Authorization header, even if is like a standard for JWT, is considered a custom header.
I can give you an example of my Spring Security configuration:
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
config.addAllowedMethod("PUT");
config.addAllowedMethod("DELETE");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
Note the line
config.addAllowedHeader("*");
That means that my REST services accept all possible headers sent by the client.
Obviously it's not a good configuration, you should limit allowed headers and other things to match your needs, as restricted as is possible.
Obviously if you don't use Spring Security you need to find the way to do the same thing with yout language/framework.
This is my SecurityConfig.java It's a bit different from yours.
Try this and let me know
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private WLUserDetailsService userDetailsService;
#Value("${jwt.header}")
private String tokenHeader;
#Value("${jwt.route.authentication.path}")
private String authenticationPath;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoderBean());
}
#Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// we don't need CSRF because our token is invulnerable
.csrf().disable()
// TODO adjust CORS management
.cors().and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
httpSecurity
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// // disable page caching
// httpSecurity
// .headers()
// .frameOptions().sameOrigin() // required to set for H2 else H2 Console will be blank.
// .cacheControl();
}
#Override
public void configure(WebSecurity web) {
// AuthenticationTokenFilter will ignore the below paths
web
.ignoring()
.antMatchers(
HttpMethod.POST,
authenticationPath
)
// allow anonymous resource requests
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js"
);
}
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
// config.addExposedHeader("Authorization, x-xsrf-token, Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, " +
// "Content-Type, Access-Control-Request-Method, Custom-Filter-Header");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
config.addAllowedMethod("PUT");
config.addAllowedMethod("DELETE");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}

Allow OPTIONS HTTP Method for oauth/token request

I'm trying to enable oauth2 token fetching for my angular application. My configuration is working fine (authentication is working correctly for all requests, token fetching is working fine as well) but there is one problem.
CORS requests require that before GET an OPTIONS request is sent to the server. To make it worse, that request does not contain any authentication headers.
I would like to have this request always returning with 200 status without any authentication done on the server. Is it possible? Maybe I'm missing something
my spring security config:
#Configuration
#EnableWebSecurity
#EnableAuthorizationServer
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);
#Inject
private UserService userService;
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
#Bean
public WebResponseExceptionTranslator webResponseExceptionTranslator() {
return new DefaultWebResponseExceptionTranslator() {
#Override
public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
ResponseEntity<OAuth2Exception> responseEntity = super.translate(e);
OAuth2Exception body = responseEntity.getBody();
HttpHeaders headers = new HttpHeaders();
headers.setAll(responseEntity.getHeaders().toSingleValueMap());
headers.set("Access-Control-Allow-Origin", "*");
headers.set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
headers.set("Access-Control-Max-Age", "3600");
headers.set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
return new ResponseEntity<>(body, headers, responseEntity.getStatusCode());
}
};
}
#Bean
public AuthorizationServerConfigurer authorizationServerConfigurer() {
return new AuthorizationServerConfigurer() {
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
oAuth2AuthenticationEntryPoint.setExceptionTranslator(webResponseExceptionTranslator());
security.authenticationEntryPoint(oAuth2AuthenticationEntryPoint);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("secret-client")
.secret("secret")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_LOGIN")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(60 * 60 * 12); // 12 hours
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenServices(tokenServices());
endpoints.authenticationManager(authenticationManager());
}
};
}
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
log.warn("FIX ME: REMOVE AFTER DEBUG!!!!!!!!!!!!");
log.debug("authenticate: " + authentication.getPrincipal() + ":" + authentication.getCredentials());
final Collection<GrantedAuthority> authorities = new ArrayList<>();
WomarUser user = userService.findUser(authentication.getPrincipal().toString(), authentication.getCredentials().toString());
for (UserRole userRole : user.getRoles()) {
authorities.add(new SimpleGrantedAuthority(userRole.getName()));
}
return new UsernamePasswordAuthenticationToken(user.getLogin(), user.getPassword(), authorities);
}
};
}
#Bean
public OAuth2AuthenticationManager auth2AuthenticationManager() {
OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
oAuth2AuthenticationManager.setTokenServices(tokenServices());
return oAuth2AuthenticationManager;
}
#Bean
public OAuth2AuthenticationProcessingFilter auth2AuthenticationProcessingFilter() throws Exception {
OAuth2AuthenticationProcessingFilter oAuth2AuthenticationProcessingFilter = new OAuth2AuthenticationProcessingFilter();
oAuth2AuthenticationProcessingFilter.setAuthenticationManager(auth2AuthenticationManager());
return oAuth2AuthenticationProcessingFilter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
oAuth2AuthenticationEntryPoint.setRealmName("realmName");
oAuth2AuthenticationEntryPoint.setTypeName("Basic");
oAuth2AuthenticationEntryPoint.setExceptionTranslator(webResponseExceptionTranslator());
http
.antMatcher("/**").httpBasic()
.authenticationEntryPoint(oAuth2AuthenticationEntryPoint)
.and().addFilterBefore(auth2AuthenticationProcessingFilter(), BasicAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/rest/womar/admin/**").hasRole("ADMIN")
.antMatchers("/rest/womar/**").hasRole("USER");
}
}
angular request:
var config = {
params: {
grant_type: 'password',
username: login,
password: password
},
headers: {
Authorization: 'Basic ' + Base64.encode('secret-client' + ':' + 'secret')
}
};
$http.get("http://localhost:8080/oauth/token", config)
.success(function(data, status) {
$log.log('success');
$log.log(data);
$log.log(status);
})
.error(function(data, status) {
$log.log('error');
$log.log(data);
$log.log(status);
});
#EnableAuthorizationServer is adding http security configuration for endpoints like /oauth/token, /oauth/token_key etc at order 0. So what you should do is to define a http security rule for /oauth/token endpoint only for the OPTIONS http method which is at a higher order.
Something like this:
#Order(-1)
#Configuration
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
}
}
I was using the solution proposed by idursun. The OPTION call started to work, but still had problems with Access-Control-Allow-Origin.
This filter implementation definitively worked for me:
Standalone Spring OAuth2 JWT Authorization Server + CORS
I just add
#Order(Ordered.HIGHEST_PRECEDENCE)
in
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {....}
and config the support of spring
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Worked for me.
Same problem with Spring-Boot 1.4.7.RELEASE
My WebSecurityConfigurerAdapter was using SecurityProperties.ACCESS_OVERRIDE_ORDER so, selected answer did not work.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter
Thus, I added the following filter configuration with preceding order:
#Bean
public FilterRegistrationBean corsFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(corsConfigurationSource()));
bean.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER);
return bean;
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return source;
}
and it got the job done.
Note: equivalent result can be achieved with a javax.servlet.Filter bean with #Order(SecurityProperties.DEFAULT_FILTER_ORDER) annotation as below:
#Component
#Order(SecurityProperties.DEFAULT_FILTER_ORDER)
public class CorsFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin" , "*" );
response.setHeader("Access-Control-Allow-Methods" , "POST, PUT, GET, OPTIONS, DELETE" );
response.setHeader("Access-Control-Allow-Headers" , "Authorization, Content-Type" );
response.setHeader("Access-Control-Max-Age" , "3600" );
if("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
}
else {
chain.doFilter(req, res);
}
}
// ...
}
The following works for Spring Boot 2. It does not pick up other CORS configurations otherwise.
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
// this is a Spring ConfigurationProperty use any way to get the CORS values
#Autowired
private CorsProperties corsProperties;
// other things
//...
#Override
public void configure(
AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
if (corsProperties.getAllowedOrigins() != null) {
Map<String, CorsConfiguration> corsConfigMap = new HashMap<>();
Arrays.asList(corsProperties.getAllowedOrigins().split(",")).stream()
.filter(StringUtils::isNotBlank).forEach(s -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin(s.trim());
if (corsProperties.getAllowedMethods() != null) {
config.setAllowedMethods(Arrays.asList(corsProperties.getAllowedMethods().split(",")));
}
if (corsProperties.getAllowedHeaders() != null) {
config.setAllowedHeaders(Arrays.asList(corsProperties.getAllowedHeaders().split(",")));
}
// here the /oauth/token is used
corsConfigMap.put("/oauth/token", config);
});
endpoints.getFrameworkEndpointHandlerMapping()
.setCorsConfigurations(corsConfigMap);
}
}
}
And in addition the already mentioned allowance of the OPTIONS request:
#Order(-1)
#Configuration
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
authorizeRequests()
.antMatchers("/**/oauth/token").permitAll()
.and().httpBasic().realmName(securityRealm)
// would throw a 403 otherwise
.and().csrf().disable()
// optional, but with a token a sesion is not needed anymore
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}

Resources