我在使用从react axios的spring引导后端获取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)
发送没有报头/数据的第一OPTION请求,并且401失败。
Access is denied (user is anonymous)
org.springframework.security.access.AccessDeniedException: Access is denied.
这是我在后台收到的请求,头/数据被删除。
[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
Option request/post令牌在curl和Postman中都能成功地使用get new访问令牌。
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 Boot后端安全和oauth2.0配置:
WebSecurityConfig:
@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");
}
}
AuthorizationServerConfig
@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
}
}
你知道我该怎么解决这个问题吗?
谢谢:(
经过大量搜索,我可以找到一个解决方案来解决春季启动后端的问题,事实上,通过启用authorizationServer,框架将在类AuthorizationServerEndpointsConfiguration中配置访问oauth端点的权限,并且不会将OPTION方法列入白名单,请参阅https://github.com/spring-projects/spring-security-oauth/issues/330
要解决401问题:1.在春季安全配置中,我添加了:
// 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,默认情况下会返回OK。(为了避免任何冲突,我在ScurityConfig中删除了CorsConfigurationSource和http.cors((的使用,请参阅我的问题中的实现(。过滤器的顺序为HIGHEST_PRECDENCE,它应该是每个请求的第一个执行过滤器。
@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);
}
}
}
这个解决方法解决了我的问题,但它不是一个完美的解决方案,我很乐意得到一些其他的解决方案。