Despite lot of subject, i cant figure out how to authenticate with my angular project to my back with spring boot so i try to post with my setup.
So far, all my authentification is handle by spring boot and work
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout().clearAuthentication(true)
.logoutSuccessUrl("/")
.permitAll();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
i started a new angular project and try to bind it with angular-oauth2-oidc.
in auth.config.js
import { AuthConfig } from 'angular-oauth2-oidc';
export const authConfig: AuthConfig = {
clientId: 'xxxxxx',
issuer: 'https://accounts.google.com/',
// loginUrl: 'http://localhost:8080',
redirectUri: window.location.origin + '/user.html',
scope: 'openid profile email',
tokenEndpoint: 'https://www.googleapis.com/oauth2/v3/token',
// strictDiscoveryDocumentValidation: false,
userinfoEndpoint: 'http://localhost:8080/user',
// disableAtHashCheck: true,
// nonceStateSeparator: ',',
// clearHashAfterLogin: false,
};
in login.component.ts
import { Component, OnInit } from '#angular/core';
import { OAuthService, JwksValidationHandler } from 'angular-oauth2-oidc';
import { authConfig } from '../auth.config';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
constructor(private oauthService: OAuthService) {
this.oauthService.configure(authConfig);
this.oauthService.tokenValidationHandler = new JwksValidationHandler();
this.oauthService.loadDiscoveryDocumentAndTryLogin();
}
ngOnInit() {
this.oauthService.initImplicitFlow(encodeURIComponent('http://localhost8080/'));
}
}
I dont understand how the authentication must be handle in this config.
The annotation #EnableOAuth2Sso transforms your spring application in an OAuth2 client
In your scenario, instead, you want that your application is a ResourceServer
So you should use the #EnableResourceServer annotation.
Spring security should be configured like this:
#Configuration
#EnableWebSecurity
#EnableResourceServer
#PropertySource(value = { "classpath:application.properties" }, encoding = "UTF-8", ignoreResourceNotFound = false)
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
private Environment env;
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/swagger-ui.html","/webjars/**","/swagger-resources/**", "/v2/**","/csrf")
.permitAll()
.antMatchers("/**")
.authenticated()
.and()
.cors()
.configurationSource(corsConfigurationSource())
.and()
.exceptionHandling()
.accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
#Override
public void configure(final ResourceServerSecurityConfigurer config) {
config
.tokenServices(tokenServices())
.resourceId("RES_ID");
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
return tokenServices;
}
#Bean
public TokenStore tokenStore()
{
JwkTokenStore result = new JwkTokenStore("JWTKS_URL", accessTokenConverter());
return result;
}
#Bean
public JwtAccessTokenConverter accessTokenConverter()
{
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setAccessTokenConverter(new DefaultAccessTokenConverter() {
#Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
final OAuth2Authentication auth = super.extractAuthentication(map);
auth.setDetails(map);
return auth;
}
});
return converter;
}
#Bean
public JwtClaimsSetVerifier jwtClaimsSetVerifier() {
return new DelegatingJwtClaimsSetVerifier(Arrays.asList(issuerClaimVerifier(), customJwtClaimVerifier()));
}
#Bean
public JwtClaimsSetVerifier issuerClaimVerifier() {
try {
return new IssuerClaimVerifier(new URL("ISSUER CLAIMS URL"));
} catch (final MalformedURLException e) {
throw new RuntimeException(e);
}
}
#Bean
public JwtClaimsSetVerifier customJwtClaimVerifier() {
return new CustomClaimVerifier();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
boolean abilitaCors = new Boolean(env.getProperty("profile.manager.web.cors.enbaled"));
if( abilitaCors )
{
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedOrigin("*");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setExposedHeaders(Arrays.asList("X-Auth-Token","x-auth-token", "x-requested-with", "x-xsrf-token","Access-Control-Allow-Origin", "content-type"));
source.registerCorsConfiguration("/**", configuration);
}
return source;
}
}
On angular side I suggest to you to use angulat-oauth2-oidc plugin https://github.com/manfredsteyer/angular-oauth2-oidc
Related
I'm currently upgrading from Spring Boot 2.7 to Spring 3 but I'm having issues when upgrading my HTTP Security Config.
I am using Azure B2C for my oauth server and that part is working fine as I can see the valid JWT being returned.
I've changed from using authorizeRequests to authorizeHttpRequests as suggested but all my HTTP requests are returning 403.
If I use authorizeRequests with all of the other Spring 3 suggested updates it still works as expected.
Configuration File:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
#Qualifier("preAuthProvider")
private AuthenticationProvider preAuthProvider;
#Value("${oauthTokenChecks.audience}")
private String audience;
#Value("${oauthTokenChecks.issuer}")
private String issuer;
#Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
private String jwkUri;
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
configuration.setAllowedHeaders(Collections.singletonList("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().
configurationSource(corsConfigurationSource()).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests((authorize) -> authorize
.requestMatchers("/api/**")
.authenticated()
)
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.csrf()
.disable()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new Auth0TokenConverter());
return http.build();
}
#Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(authenticationProvider);
authenticationManagerBuilder.authenticationProvider(preAuthProvider);
return authenticationManagerBuilder.build();
}
#Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder =
NimbusJwtDecoder.withJwkSetUri(jwkUri).build();
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> withAudience =
new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
// jwtDecoder.setClaimSetConverter(new UsernameSubClaimAdapter());
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
}
}
Auth Converter:
public class Auth0TokenConverter implements Converter<Jwt, AbstractAuthenticationToken> {
#Override
public AbstractAuthenticationToken convert(final Jwt jwt) {
var emails = jwt.getClaimAsStringList("emails");
if (emails == null || emails.size() != 1) {
throw new RuntimeException("One and only one email expected in token claim");
}
var user =
ImplementUser.builder()
.username(emails.get(0))
.auth0Id(null)
.enabled(true)
.accountNonExpired(true)
.accountNonLocked(true)
.credentialsNonExpired(true)
.build();
return new PreAuthenticatedAuthenticationToken(user, jwt);
}
}
CustomPreAuthProvider:
#Component("preAuthProvider")
public class CustomPreAuthProvider extends PreAuthenticatedAuthenticationProvider {
#Autowired
private CustomPreAuthUserDetailsService userService;
public CustomPreAuthProvider() {
super();
}
#PostConstruct
public void init() {
super.setPreAuthenticatedUserDetailsService(userService);
}
}
CustomPreAuthUserDetailsService:
#Service
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class CustomPreAuthUserDetailsService
implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
private final AuthUserDetailsService authUserDetailsService;
#Override
public final UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) {
var principal = (ImplementUser) token.getPrincipal();
var email = principal.getUsername();
var user = authUserDetailsService.loadUserDetails(email);
var implementUser = (ImplementUser) token.getPrincipal();
return implementUser.toBuilder()
.firstName(user.getFirstName())
.lastName(user.getLastName())
.globalUserId(user.getGlobalUserId())
.profiles(user.getProfiles())
.activeProfile(user.getActiveProfile())
.defaultProfileId(user.getDefaultProfileId())
.build();
}
}
Below are the error logs
Log output
I have a problem with CORS, but only at some versions of Firefox and Safari: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ... (Reason: CORS request did not succeed). At Chrome it's fine for all testing machines. Here's my configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableAutoConfiguration
public class ApplicationConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/some_public_urls")
);
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
TokenAuthenticationProvider provider;
public ApplicationConfig(final TokenAuthenticationProvider provider) {
super();
this.provider = requireNonNull(provider);
}
#Autowired
private Environment env;
#Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(provider);
}
#Override
public void configure(final WebSecurity web) {
web.ignoring().requestMatchers(PUBLIC_URLS);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.headers()
.and()
.sessionManagement()
.sessionCreationPolicy(STATELESS)
.and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
.and()
.authenticationProvider(provider)
.addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(PROTECTED_URLS)
.authenticated()
.and()
.cors()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
}
#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("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
TokenAuthenticationFilter restAuthenticationFilter() throws Exception {
final TokenAuthenticationFilter filter = new TokenAuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
#Bean
SimpleUrlAuthenticationSuccessHandler successHandler() {
final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
successHandler.setRedirectStrategy((httpServletRequest, httpServletResponse, s) -> {
// No redirect is required
});
return successHandler;
}
/**
* Disable Spring boot automatic filter registration.
*/
#Bean
FilterRegistrationBean disableAutoRegistration(final TokenAuthenticationFilter filter) {
final FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
#Bean
AuthenticationEntryPoint forbiddenEntryPoint() {
return new HttpStatusEntryPoint(FORBIDDEN);
}
}
Each RestController is annotated with:
#RestController
#CrossOrigin
I could guess that you are performing testing on some old browsers and it doesn't work.
Here is the landscape of CORS support in browsers. Please check it out.
As of mid-2014, approximately 83% of the browsers out there have full
support for CORS, and another 6% have partial support.
If it's the case, you could try some other techniques like
JSON-P or using Proxy Server to make cross-origin requests in older browser.
Ok,
It turned out, that it was certificate issue. We have a certificate bundle (wildcard certificate) and it was placed in wrong order. Some browsers could handle this, some versions of Firefox were blocking it.
I am trying to configure Spring Boot OAuth2 with keycloak. Code is working fine when I am using properties from application.properties file. Current config is as follow:
rest.security.issuer-uri=http://172.30.30.172:8080/auth/realms/<REALM_NAME>
security.oauth2.resource.id=test
security.oauth2.resource.token-info-uri=${rest.security.issuer-uri}/protocol/openid-connect/token/introspect
security.oauth2.resource.user-info-uri=${rest.security.issuer-uri}/protocol/openid-connect/userinfo
security.oauth2.resource.jwt.key-value=<PUBLIC KEY>
security.oauth2.client.client-id=<CLIENT ID>
security.oauth2.client.client-secret=<CLIENT SECRET>
security.oauth2.client.user-authorization-uri=${rest.security.issuer-uri}/protocol/openid-connect/auth
security.oauth2.client.access-token-uri=${rest.security.issuer-uri}/protocol/openid-connect/token
security.oauth2.client.scope=openid
security.oauth2.client.grant-type=client_credentials
I want to configure the client config settings in a dynammic manner by fetching the client config from database for each client (its own realm) request. What should be the Java Spring Boot Security configuration for setting the client properties in dynamic manner.
Current Security configuration is as follow:
#Configuration
#EnableWebSecurity
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ConditionalOnProperty(prefix = "rest.security", value = "enabled", havingValue = "true")
#Import({SecurityProperties.class})
public class SecurityConfigurer extends ResourceServerConfigurerAdapter{
private ResourceServerProperties resourceServerProperties;
private SecurityProperties securityProperties;
/* Using spring constructor injection, #Autowired is implicit */
public SecurityConfigurer(ResourceServerProperties resourceServerProperties, SecurityProperties securityProperties) {
this.resourceServerProperties = resourceServerProperties;
this.securityProperties = securityProperties;
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceServerProperties.getResourceId());
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.cors()
.configurationSource(corsConfigurationSource())
.and()
.headers()
.frameOptions()
.disable()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers(securityProperties.getApiMatcher())
.authenticated();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
if (null != securityProperties.getCorsConfiguration()) {
source.registerCorsConfiguration("/**", securityProperties.getCorsConfiguration());
}
return source;
}
#Bean
public JwtAccessTokenCustomizer jwtAccessTokenCustomizer(ObjectMapper mapper) {
return new JwtAccessTokenCustomizer(mapper);
}
}
I tried to override bean ResourceServerProperties, using following code. However its throwing NoUniqueBeanDefinitionException exception.
#Configuration
#Import({ResourceServerProperties.class})
public class ResourceSecurityProperties {
#Primary
#Bean
ResourceServerProperties resourceServerProperties(){
ResourceServerProperties resourceServerProperties= new ResourceServerProperties("<CLIENT-ID>", "<CLIENT-SECRET>");
ResourceServerProperties.Jwt jwt= resourceServerProperties.new Jwt();
resourceServerProperties.setId("test001001");
resourceServerProperties.setTokenInfoUri("http://172.30.30.172:8080/auth/realms/conf/protocol/openid-connect/token/introspect");
resourceServerProperties.setUserInfoUri("http://172.30.30.172:8080/auth/realms/conf/protocol/openid-connect/userinfo");
jwt.setKeyValue("<PUBLIC KEY>");
resourceServerProperties.setJwt(jwt);
return resourceServerProperties;
}
}
I want to create a simple REST API, I am using Angular 6 and Spring Boot. I wanted to implement logging to my app but whenever I try to sign up I get 404 Not Found /login. I have looked up to existing similar issues I found but nothing seems to help me.
Here's my spring security configuration ( I am sure that my sign up form and controller works properly, so I guess the issue lies somewhere down there)
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${allowedOriginAddress}")
private String allowedOriginAddress;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public UserDataDetailsService userDataDetailsService() {
return new UserDataDetailsService();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(
Arrays.asList(
allowedOriginAddress,
allowedOriginAddress + "/*")
);
configuration.setAllowedMethods(Arrays.asList("GET","POST","DELETE","OPTIONS","PUT"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("*"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.authorizeRequests()
.anyRequest().permitAll()
.and().csrf().disable()
.formLogin()
.loginPage("/")
.usernameParameter("email")
.loginProcessingUrl("/login")
.failureForwardUrl("/prev")
.successForwardUrl("/next")
.and().authorizeRequests().anyRequest().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDataDetailsService()).passwordEncoder(passwordEncoder());
}
}
#SpringBootApplication
#Configuration
public class WhispererApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(WhispererApplication.class, args);
}
}
I cannot find the authentication configuration in your project. Please try adding the next code:
.anyRequest().authenticated()
I think your code will be:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/public/**").permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.anyRequest().authenticated().
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
The JWTAuthenticationFilter and JWTAuthorizationFilter are to manage the JWT if you are using that to authenticate the user.
I'm trying to configure Spring for CORS in order to use Angular web UI:
I tried this:
#Configuration
#ComponentScan("org.datalis.admin.config")
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
PropertySourcesPlaceholderConfigurer conf = new PropertySourcesPlaceholderConfigurer();
conf.setLocation(new ClassPathResource("application.properties"));
return conf;
}
#Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("127.0.0.1");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<CorsFilter>(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
Apache server with Angular FE is running with Wildly server on the same server so I configured 127.0.0.1 for source.
But still I get:
Access to XMLHttpRequest at 'http://123.123.123.123:8080/api/oauth/token' from origin 'http://123.123.123.123' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
auth:1 Failed to load resource: the server responded with a status of 404 (Not Found)
Do you know how I can fix this issue?
Second way that I tried:
#Configuration
#EnableResourceServer
public class ResourceSecurityConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("resource_id").stateless(true);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/users/**").permitAll()
.anyRequest().authenticated()
.and()
.cors().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest()
.fullyAuthenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
#Bean
public CorsConfigurationSource corsConfigurationSources() {
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;
}
}
With the second configuration I get has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
auth:1 Failed to load resource: the server responded with a status of 404 (Not Found)
What is the best way to achieve this result?
Your allowed origin is 127.0.0.1 but your client side has the ip 123.123.123.123. Try to change this:
config.addAllowedOrigin("127.0.0.1");
To this:
config.addAllowedOrigin("123.123.123.123");
You need to tell Spring Security to use the CORS Configuration you created.
In my project I configured Spring Security in this way:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers("/rest/protected/**")
.authenticated()
//Other spring sec configruation and then:
.and()
.cors()
.configurationSource(corsConfigurationSource())
}
Where corsConfigurationSource() is:
#Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
boolean abilitaCors = new Boolean(env.getProperty("templating.oauth.enable.cors"));
if( abilitaCors )
{
if( logger.isWarnEnabled() )
{
logger.warn("CORS ABILITATI! Si assume ambiente di sviluppo");
}
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200","http://localhost:8080", "http://localhost:8180"));
configuration.setAllowedMethods(Arrays.asList( RequestMethod.GET.name(),
RequestMethod.POST.name(),
RequestMethod.OPTIONS.name(),
RequestMethod.DELETE.name(),
RequestMethod.PUT.name()));
configuration.setExposedHeaders(Arrays.asList("x-auth-token", "x-requested-with", "x-xsrf-token"));
configuration.setAllowedHeaders(Arrays.asList("X-Auth-Token","x-auth-token", "x-requested-with", "x-xsrf-token"));
source.registerCorsConfiguration("/**", configuration);
}
return source;
}
I hope it's useful
Angelo
This is my working #Configuration class to handle CORS requests used only in dev environment.
#Configuration
//#Profile(PROFILE_DEV)
public class CorsConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("*");
}
};
}
}
You have also to configure Spring Security to ignore HttpMethod.OPTIONS used by preflight request (as the exception you mentioned)
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//...
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
//others if you need
.antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.exceptionHandling()
.and()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/**").authenticated();
}
}
Because when you use cors you have Simple Request and Preflighted Request that triggers an HttpMethod.OPTIONS
I recommend you to use a WebMvcConfigurer, and in the addCorsMappings method set the CORS configuration.
Somethingo like this
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:9798")
.allowedMethods("POST", "GET")
//.allowedHeaders("header1", "header2", "header3")
//.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
}
}
Here there is a link with a fully functional Spring with CORS project, just download and run it.
https://github.com/reos79/spring-cors
It has a html page (person.html) this page does nothing but call the service on the port (9797). So you need to load this project twice, once on port 9797 to load the service and the other on port (9798). Then on you browser you call the page person on the server localhost:9798 and it will call the service on localhost:9797, in the file application.properties I configured the port.
You need to add #CrossOrigin class level in your controller class like below
#CrossOrigin
public class SampleController {
// Your code goes here
}
annotation to your rest controller class
Try changing your bean name to corsConfigurationSource removing the "s"
Documentation https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors
// by default uses a Bean by the name of corsConfigurationSource