我一直遇到CORS的问题而且我已经尝试过在Stack Overflow上找到的所有,基本上我在谷歌上发现的任何东西都没有运气。
所以我的后端有用户身份验证,我的前端有一个登录页面。我用Axios连接了登录页面,所以我可以发一个帖子请求并尝试登录,但我一直收到像“预检请求”这样的错误,所以我修复了然后我开始收到“Post 403 Forbidden”错误。
看起来像这样:
POST http://localhost:8080/api/v1/login/ 403 (Forbidden)
即使尝试使用Postman登录也行不通,所以显然有些错误。将在
下面发布类文件在我的后端,我有一个名为WebSecurityConfig的类,它处理所有CORS的东西:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS");
}
};
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*"); // TODO: lock down before deploying
config.addAllowedHeader("*");
config.addExposedHeader(HttpHeaders.AUTHORIZATION);
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http
.cors()
.and()
.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/h2/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/v1/login").permitAll()
.anyRequest().authenticated()
.and()
// We filter the api/login requests
.addFilterBefore(new JWTLoginFilter("/api/v1/login", authenticationManager()),
UsernamePasswordAuthenticationFilter.class);
// And filter other requests to check the presence of JWT in header
//.addFilterBefore(new JWTAuthenticationFilter(),
// UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Create a default account
auth.userDetailsService(userDetailsService);
// auth.inMemoryAuthentication()
// .withUser("admin")
// .password("password")
// .roles("ADMIN");
}
}
在我们的前端,用VueJS编写并使用Axios进行调用
<script>
import { mapActions } from 'vuex';
import { required, username, minLength } from 'vuelidate/lib/validators';
export default {
data() {
return {
form: {
username: '',
password: ''
},
e1: true,
response: ''
}
},
validations: {
form: {
username: {
required
},
password: {
required
}
}
},
methods: {
...mapActions({
setToken: 'setToken',
setUser: 'setUser'
}),
login() {
this.response = '';
let req = {
"username": this.form.username,
"password": this.form.password
};
this.$http.post('/api/v1/login/', req)
.then(response => {
if (response.status === 200) {
this.setToken(response.data.token);
this.setUser(response.data.user);
this.$router.push('/dashboard');
} else {
this.response = response.data.error.message;
}
}, error => {
console.log(error);
this.response = 'Unable to connect to server.';
});
}
}
}
</script>
因此,当我通过Chrome工具(网络)调试时,我注意到OPTIONS请求如下所示:
以下是POST错误的图片:
这是另一个处理OPTIONS请求的类(WebSecurityConfig中引用的JWTLoginFilter):
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
@Override
public Authentication attemptAuthentication(
HttpServletRequest req, HttpServletResponse res)
throws AuthenticationException, IOException, ServletException {
AccountCredentials creds = new ObjectMapper()
.readValue(req.getInputStream(), AccountCredentials.class);
if (CorsUtils.isPreFlightRequest(req)) {
res.setStatus(HttpServletResponse.SC_OK);
return null;
}
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
Collections.emptyList()
)
);
}
@Override
protected void successfulAuthentication(
HttpServletRequest req,
HttpServletResponse res, FilterChain chain,
Authentication auth) throws IOException, ServletException {
TokenAuthenticationService
.addAuthentication(res, auth.getName());
}
}
答案 0 :(得分:1)
配置Axios时,只需简单地一劳永逸地指定标题:
import axios from "axios";
const CSRF_TOKEN = document.cookie.match(new RegExp(`XSRF-TOKEN=([^;]+)`))[1];
const instance = axios.create({
headers: { "X-XSRF-TOKEN": CSRF_TOKEN }
});
export const AXIOS = instance;
然后(在这里我假设您使用SpringBoot 2.0.0,尽管它也应在SpringBoot 1.4.x及更高版本中使用)在您的Spring Boot应用程序中,您应该添加以下安全配置。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// CSRF Token
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// you can chain other configs here
}
}
通过这种方式,Spring将在响应中将令牌作为cookie返回(我假设您首先执行GET
),然后您将在AXIOS配置文件中读取它。
答案 1 :(得分:0)
除了少数特殊情况外,您不应该根据Spring Security文档禁用CSRF。此代码将CSRF标头置于VUE。我使用了vue-resource。
//This token is from Thymeleaf JS generation.
var csrftoken = [[${_csrf.token}]];
console.log('csrf - ' + csrftoken) ;
Vue.http.headers.common['X-CSRF-TOKEN'] = csrftoken;
希望这有帮助。
答案 2 :(得分:0)
默认情况下,Axios将正确处理X-XSRF-TOKEN。
因此,唯一的操作就是配置服务器,就像JeanValjean解释的那样:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// CSRF Token
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// you can chain other configs here
}
}
Axios将在请求标头中自动发送正确的令牌,因此无需更改前端。
答案 3 :(得分:-1)
我遇到了同样的问题,GET请求正在运行,但是回复的POST请求状态为403。
我发现,就我而言,这是因为默认启用了CSRF保护。
确保此案例的一种快速方法是禁用CSRF:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// …
@Override
protected void configure(HttpSecurity http) throws Exception {
// …
http.csrf().disable();
// …
}
// …
}
有关Spring-Security网站的更多信息。
请注意,禁用CSRF并不总是正确的答案,因为它是出于安全目的。