0
votes

I'm using Spring Boot 2.x + Spring Security + JWT to create REST API for my Angular 5 application.

I can login using POSTMAN but from Angular application, my OPTIONS call fails:

Request URL: http://localhost:8080/api/v1/login
Request Method: OPTIONS
Status Code: 403 
Remote Address: [::1]:8080

SecurityConfiguration.java

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private DataSource dataSource;

    @Value("${spring.queries.users-query}")
    private String usersQuery;

    @Value("${spring.queries.roles-query}")
    private String rolesQuery;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().usersByUsernameQuery(usersQuery).authoritiesByUsernameQuery(rolesQuery).dataSource(dataSource)
                .passwordEncoder(bCryptPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().disable().csrf().disable().authorizeRequests().antMatchers("/").permitAll().antMatchers("/").permitAll();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
    }
}

application.properties

# ===============================
# = DATA SOURCE
# ===============================
spring.datasource.url=jdbc:mysql://localhost:3306/some_db

spring.datasource.username=some_user
spring.datasource.password=some_password
spring.datasource.driverClassName=com.mysql.jdbc.Driver

# ===============================
# = JPA / HIBERNATE SETTINGS
# ===============================

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto = validate

# =============================================================
# = Spring Security / Queries for AuthenticationManagerBuilder  
# =============================================================

spring.queries.users-query=select email, password from users where email=?
spring.queries.roles-query=select u.email, r.role from users u inner join user_role ur on(u.user_id=ur.user_id) inner join role r on(ur.role_id=r.role_id) where u.email=? 

# ===========================
# = JSON WEB TOKEN SETTINGS  
# ===========================

jwt.secret= JWTSuperSecretKey
jwt.expiration = 604800000

POSTMAN:

POST: http://localhost:8080/api/v1/login

Content-Type: application/x-www-form-urlencoded

Is it a header problem from angular side?

1

1 Answers

0
votes

You disable CORS which means that your server will not tell the frontend to allow request to any origin but itself. By telling the server to accepting requests from another origin, e.g. localhost:4200 and the methods GET, PUT, POST, DELETE and OPTIONS your application should work.

The following setup is a bit more complicated than it needs to be but it keeps things nice and tidy separated.

The definition of the allowed origins should be put in the application.yml e.g.

cors:
  allowed-origins:
    - https://localhost:4200
    - http://localhost:4200
    - http://127.0.0.1:4200
    - https://127.0.0.1:4200

Then you add two files, one to load the config...

@Configuration
@ConfigurationProperties("cors")
public class CorsConfig {
    private List<String> allowedOrigins = new ArrayList<>();
    private String[] allowedMethods = {
        HttpMethod.GET.name(),
        HttpMethod.HEAD.name(),
        HttpMethod.POST.name(),
        HttpMethod.PUT.name(),
        HttpMethod.DELETE.name()
    };

    public List<String> getAllowedOrigins() {
        return allowedOrigins;
    }

    public String[] getAllowedOriginsArray() {
        return getAllowedOrigins().toArray(new String[0]);
    }

    public String[] getAllowedMethods() {
        return allowedMethods;
    }
}

... and one to put it to work.

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    private final CorsConfig corsConfig;

    public WebMvcConfig(CorsConfig corsConfig) {
    this.corsConfig = corsConfig;
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowedOrigins(corsConfig.getAllowedOriginsArray())
            .allowedMethods(corsConfig.getAllowedMethods());
    }
}