Подтвердить что ты не робот

Разрешить метод HTTP OPTIONS для запроса oauth/token

Я пытаюсь включить извлечение маркера oauth2 для моего приложения angular. Моя конфигурация работает нормально (проверка подлинности работает корректно для всех запросов, выборка токенов работает нормально), но есть одна проблема.

Запросы CORS требуют, чтобы перед отправкой запроса OPTIONS на сервер. Чтобы сделать это хуже, этот запрос не содержит заголовков аутентификации. Я хотел бы, чтобы этот запрос всегда возвращался с 200 статусом без какой-либо проверки подлинности на сервере. Является ли это возможным? Может быть, я чего-то не хватает

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 запрос:

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);
    });
4b9b3361

Ответ 1

@EnableAuthorizationServer добавляет конфигурацию безопасности http для конечных точек, таких как /oauth/token, /oauth/token_key и т.д. В порядке 0. Итак, вам нужно определить правило безопасности http для конечной точки /oauth/token только для метода OPTIONS http, который находится на более высокого порядка.

Примерно так:

@Order(-1)
@Configuration
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
          .authorizeRequests()
          .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
   }
}

Ответ 3

Я просто добавляю

@Order(Ordered.HIGHEST_PRECEDENCE)

в

public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {....}

и настройте поддержку весны

@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;
}

работал на меня.

Ответ 4

Следующее работает для Spring Boot 2. В противном случае оно не подхватывает другие конфигурации CORS.

@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);
        }
    }


}

И, кроме того, уже упомянутое разрешение запроса OPTIONS:

@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);
   }
}

Ответ 5

Та же проблема с Spring-Boot 1.4.7.RELEASE

Мой WebSecurityConfigurerAdapter использовал SecurityProperties.ACCESS_OVERRIDE_ORDER, поэтому выбранный ответ не сработал.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter 

Таким образом, я добавил следующую конфигурацию фильтра с предыдущим порядком:

  @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;
  }

и он сделал свою работу.

Примечание: эквивалентный результат может быть достигнут с помощью компонента javax.servlet.Filter с аннотацией @Order(SecurityProperties.DEFAULT_FILTER_ORDER), как показано ниже:

@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);
    }
  }
  // ...
}