POST oauth 令牌最初发送没有数据的 OPTIONS 请求/headers 并失败并返回 401
POST oauth token sends initially OPTIONS request without data / headers and fails with 401
我在使用 spring 从反应 axios 启动后端获取 oauth 令牌时遇到问题:
async login() {
const tokenurl = 'http://localhost:8080/oauth/token';
const data = {
grant_type: 'password',
username: this.state.email,
password: this.state.password,
scope: 'write'
};
var headers = {
headers: {'Authorization' : 'Basic ' + btoa('client:secret') ,
'Content-Type': 'application/x-www-form-urlencoded'}
}
axios.post(tokenurl,data,headers)
第一个没有 headers / 数据的 OPTION 请求被发送并且 401 失败。
Access is denied (user is anonymous)
org.springframework.security.access.AccessDeniedException: Access is denied.
这是我在后端收到的请求,header/数据已删除。
[OPTIONS /oauth/token HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: */*
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization
Referer: http://localhost:3000/signin/
Origin: http://localhost:3000
Connection: keep-alive
选项请求/post 令牌在 curl 和 Postman 中成功运行并获得新的访问令牌。
curl -X POST -u "client:secret" -d "grant_type=password&username=tata@tata.com&password=test&scope=write" -H "origin:http://localhost:3000" -H "Access-Control-Request-Headers:authorization" http://localhost:8080/oauth/token
curl -X OPTIONS -u "client:secret" -d "grant_type=password&username=tata@tata.com&password=test&scope=write" -H "origin:http://localhost:3000" -H "Access-Control-Request-Headers:authorization" http://localhost:8080/oauth/token
我注意到在 curl OPTIONS 请求中删除 -u "client:secret" 会导致与 axios 相同的错误。
Spring 引导后端安全和 oauth2.0 配置:
网络安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(corsConfigurationSource()).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().httpBasic().realmName(securityRealm).and().csrf().disable();
}
@Bean(name="CorsSource")
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.applyPermitDefaultValues(); configuration.addAllowedMethod(HttpMethod.OPTIONS);
configuration.addAllowedOrigin("http://localhost:3000");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
资源配置:
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(HttpMethod.POST,"/api/**").hasRole("PROVIDER").antMatchers(HttpMethod.GET, "/api/**").hasRole("CLIENT").antMatchers("/admin/**").hasRole("ADMIN");
}
}
授权服务器配置
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer.inMemory().withClient(clientId).secret(passwordEncoder.encode(clientSecret)).authorizedGrantTypes(grantType).scopes(scopeRead,scopeWrite).resourceIds(esourceIds); //ressourceIds:api,admin
}
}
你知道我该如何解决这个问题吗?
谢谢:)
经过大量搜索后,我找到了解决 spring 启动后端问题的解决方法,事实上,启用 authorizationServer 后,框架将配置权限以访问 class AuthorizationServerEndpointsConfiguration 并且没有将 OPTION 方法列入白名单,请参阅 https://github.com/spring-projects/spring-security-oauth/issues/330
修复 401 问题:
1. 在 spring 安全配置中,我添加了:
// ignore spring security preflight request for oauth (OPTIONS)
@Override
public void configure(WebSecurity web) throws Exception {
// TODO Auto-generated method stub
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/oauth/**");
}
我添加了一个 cors 过滤器,如果方法是 OPTIONS,默认情况下 return OK。
(为避免任何冲突,我在 ScurityConfig 中删除了 CorsConfigurationSource 和 http.cors() 的使用,请参阅我的问题中的实现)。过滤器被排序为 HIGHEST_PRECEDENCE 并且它应该是每个请求的第一个执行。
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter("/*")
public class CorsFilter implements Filter {
public CorsFilter() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
final HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Max-Age", "3600");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
}
这个变通办法解决了我的问题,但它不是一个完美的解决方案,我会很高兴得到一些其他的解决方案。
我在使用 spring 从反应 axios 启动后端获取 oauth 令牌时遇到问题:
async login() {
const tokenurl = 'http://localhost:8080/oauth/token';
const data = {
grant_type: 'password',
username: this.state.email,
password: this.state.password,
scope: 'write'
};
var headers = {
headers: {'Authorization' : 'Basic ' + btoa('client:secret') ,
'Content-Type': 'application/x-www-form-urlencoded'}
}
axios.post(tokenurl,data,headers)
第一个没有 headers / 数据的 OPTION 请求被发送并且 401 失败。
Access is denied (user is anonymous)
org.springframework.security.access.AccessDeniedException: Access is denied.
这是我在后端收到的请求,header/数据已删除。
[OPTIONS /oauth/token HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: */*
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization
Referer: http://localhost:3000/signin/
Origin: http://localhost:3000
Connection: keep-alive
选项请求/post 令牌在 curl 和 Postman 中成功运行并获得新的访问令牌。
curl -X POST -u "client:secret" -d "grant_type=password&username=tata@tata.com&password=test&scope=write" -H "origin:http://localhost:3000" -H "Access-Control-Request-Headers:authorization" http://localhost:8080/oauth/token
curl -X OPTIONS -u "client:secret" -d "grant_type=password&username=tata@tata.com&password=test&scope=write" -H "origin:http://localhost:3000" -H "Access-Control-Request-Headers:authorization" http://localhost:8080/oauth/token
我注意到在 curl OPTIONS 请求中删除 -u "client:secret" 会导致与 axios 相同的错误。
Spring 引导后端安全和 oauth2.0 配置:
网络安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(corsConfigurationSource()).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().httpBasic().realmName(securityRealm).and().csrf().disable();
}
@Bean(name="CorsSource")
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.applyPermitDefaultValues(); configuration.addAllowedMethod(HttpMethod.OPTIONS);
configuration.addAllowedOrigin("http://localhost:3000");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
资源配置:
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(HttpMethod.POST,"/api/**").hasRole("PROVIDER").antMatchers(HttpMethod.GET, "/api/**").hasRole("CLIENT").antMatchers("/admin/**").hasRole("ADMIN");
}
}
授权服务器配置
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer.inMemory().withClient(clientId).secret(passwordEncoder.encode(clientSecret)).authorizedGrantTypes(grantType).scopes(scopeRead,scopeWrite).resourceIds(esourceIds); //ressourceIds:api,admin
}
}
你知道我该如何解决这个问题吗?
谢谢:)
经过大量搜索后,我找到了解决 spring 启动后端问题的解决方法,事实上,启用 authorizationServer 后,框架将配置权限以访问 class AuthorizationServerEndpointsConfiguration 并且没有将 OPTION 方法列入白名单,请参阅 https://github.com/spring-projects/spring-security-oauth/issues/330
修复 401 问题: 1. 在 spring 安全配置中,我添加了:
// ignore spring security preflight request for oauth (OPTIONS)
@Override
public void configure(WebSecurity web) throws Exception {
// TODO Auto-generated method stub
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/oauth/**");
}
我添加了一个 cors 过滤器,如果方法是 OPTIONS,默认情况下 return OK。 (为避免任何冲突,我在 ScurityConfig 中删除了 CorsConfigurationSource 和 http.cors() 的使用,请参阅我的问题中的实现)。过滤器被排序为 HIGHEST_PRECEDENCE 并且它应该是每个请求的第一个执行。
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter("/*")
public class CorsFilter implements Filter {
public CorsFilter() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
final HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Max-Age", "3600");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
}
这个变通办法解决了我的问题,但它不是一个完美的解决方案,我会很高兴得到一些其他的解决方案。