独立 Spring OAuth2 JWT 授权服务器 + CORS
Standalone Spring OAuth2 JWT Authorization Server + CORS
所以我从 Dave Syer
的 this example 中浓缩了以下授权服务器
@SpringBootApplication
public class AuthserverApplication {
public static void main(String[] args) {
SpringApplication.run(AuthserverApplication.class, args);
}
/* added later
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
protected static class MyWebSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http //.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
}
}*/
@Configuration
@EnableAuthorizationServer
protected static class OAuth2AuthorizationConfig extends
AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(
new ClassPathResource("keystore.jks"), "foobar".toCharArray())
.getKeyPair("test");
converter.setKeyPair(keyPair);
return converter;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
//.secret("acmesecret")
.authorizedGrantTypes(//"authorization_code", "refresh_token",
"password").scopes("openid");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager).accessTokenConverter(
jwtAccessTokenConverter());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
"isAuthenticated()");
}
}
}
当我 运行 它并用 curl 测试它时
curl acme@localhost:8110/oauth/token -d grant_type=password -d client_id=acme -d username=user -d password=password
我得到了一个 JWT 作为响应,但是当我尝试从我的前端访问 AuthServer(Angular 不同端口上的 JS)时,我收到了 CORS 错误。不是因为缺少 Headers,而是因为 OPTION 请求被拒绝并且缺少凭据。
Request URL:http://localhost:8110/oauth/token
Request Method:OPTIONS
Status Code:401 Unauthorized
WWW-Authenticate:Bearer realm="oauth", error="unauthorized", error_description="Full authentication is required to access this resource"
我已经知道我必须添加一个 CorsFilter 并且另外发现 this post 我在第一个答案中使用了代码片段来让 OPTIONS 请求访问 /oauth/token
而无需凭据:
@Order(-1)
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
}
}
之后我用 curl 得到了以下错误:
{"timestamp":1433370068120,"status":403,"error":"Forbidden","message":"Expected CSRF token not found. Has your session expired?","path":"/oauth/token"}
为了简单起见,我只是将 http.csrf().disable()
添加到 MyWebSecurity class 的 configure
方法中,这解决了 OPTION 请求的问题,但因此 POST 请求不再有效,我得到 There is no client authentication. Try adding an appropriate authentication filter.
(也有 curl)。
我试图找出是否必须以某种方式连接 MyWebSecurity class 和 AuthServer,但没有任何运气。原始示例(开头的link)也注入了 authenticationManager,但这对我没有任何改变。
找到问题的原因!
如果 CorsFilter 处理了 OPTIONS 请求,我只需要结束过滤器链并return立即得到结果!
SimpleCorsFilter.java
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
public SimpleCorsFilter() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
之后我可以忽略 AuthServer 中的 OPTIONS 预检请求 =D
所以服务器像上面的片段一样工作,你可以忽略开头带有 MyWebSecurity class 的块注释。
嗯,你是对的!这是一个解决方案,对我也有效(我遇到了同样的问题)
但我想为 Java 使用更智能的 CORS 过滤器实现:
http://software.dzhuvinov.com/cors-filter.html
这是 Java 应用程序的非常完整的解决方案。
实际上,您可以看到here您的观点是如何解决的。
我使用问题的解决方案找到了解决方案。但我有另一种方式来描述解决方案:
@Configuration
public class WebSecurityGlobalConfig extends WebSecurityConfigurerAdapter {
....
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS);
}
...
}
我使用以下方法遇到了类似的问题
- 后端
Spring Boot 1.5.8.RELEASE
- Spring OAuth2
Spring OAuth 2.2.0.RELEASE
w
Vuejs
应用使用 axios
ajax 请求库
postman
一切正常!当我开始从 Vuejs
应用发出请求时,出现以下错误
OPTIONS http://localhost:8080/springboot/oauth/token 401 ()
和
XMLHttpRequest cannot load http://localhost:8080/springboot/oauth/token. Response for preflight has invalid HTTP status code 401
稍作阅读后,我发现我可以通过覆盖 WebSecurityConfigurerAdapter
实现中的 configure
来指示 Spring OAuth
忽略 OPTIONS
请求 class 如下
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS);
}
添加上述内容有所帮助,但随后,我遇到了 CORS
特定错误
OPTIONS http://localhost:8080/springboot/oauth/token 403 ()
和
XMLHttpRequest cannot load http://localhost:8080/springboot/oauth/token. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 403.
并在 CorsConfig
的帮助下解决了上述问题,如下所示
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilterRegistrationBean() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.applyPermitDefaultValues();
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("*"));
config.setExposedHeaders(Arrays.asList("content-length"));
config.setMaxAge(3600L);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
添加上述class后,效果如预期。在我走之前 prod
我会研究 consequences
使用
web.ignoring().antMatchers(HttpMethod.OPTIONS);
以及 best practices
以上 Cors
配置。目前 *
可以完成工作,但对于生产来说绝对不安全。
Cyril 的回答对我有帮助 partially
然后我在这个 Github 问题中遇到了 CorsConfig
想法。
这里使用Spring引导2。
我必须在 AuthorizationServerConfigurerAdapter
中执行此操作
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
Map<String, CorsConfiguration> corsConfigMap = new HashMap<>();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
//TODO: Make configurable
config.setAllowedOrigins(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowedHeaders(Collections.singletonList("*"));
corsConfigMap.put("/oauth/token", config);
endpoints.getFrameworkEndpointHandlerMapping()
.setCorsConfigurations(corsConfigMap);
//additional settings...
}
我尝试了不同的方法来解决这个问题。我会说以下是我这边解决这个问题的方法(使用 Spring Boot 2)
1-Add the below method to the below method class that extends WebSecurityConfigurerAdapter:
// CORS settings
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS);
}
2-Add the below to my class that extends AuthorizationServerConfigurerAdapter
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// enable cors for "/oauth/token"
Map<String, CorsConfiguration> corsConfigMap = new HashMap<>();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowedHeaders(Collections.singletonList("*"));
corsConfigMap.put("/oauth/token", config);
endpoints.getFrameworkEndpointHandlerMapping()
.setCorsConfigurations(corsConfigMap);
// add the other configuration
}
所以我从 Dave Syer
的 this example 中浓缩了以下授权服务器@SpringBootApplication
public class AuthserverApplication {
public static void main(String[] args) {
SpringApplication.run(AuthserverApplication.class, args);
}
/* added later
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
protected static class MyWebSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http //.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
}
}*/
@Configuration
@EnableAuthorizationServer
protected static class OAuth2AuthorizationConfig extends
AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(
new ClassPathResource("keystore.jks"), "foobar".toCharArray())
.getKeyPair("test");
converter.setKeyPair(keyPair);
return converter;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
//.secret("acmesecret")
.authorizedGrantTypes(//"authorization_code", "refresh_token",
"password").scopes("openid");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager).accessTokenConverter(
jwtAccessTokenConverter());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
"isAuthenticated()");
}
}
}
当我 运行 它并用 curl 测试它时
curl acme@localhost:8110/oauth/token -d grant_type=password -d client_id=acme -d username=user -d password=password
我得到了一个 JWT 作为响应,但是当我尝试从我的前端访问 AuthServer(Angular 不同端口上的 JS)时,我收到了 CORS 错误。不是因为缺少 Headers,而是因为 OPTION 请求被拒绝并且缺少凭据。
Request URL:http://localhost:8110/oauth/token
Request Method:OPTIONS
Status Code:401 Unauthorized
WWW-Authenticate:Bearer realm="oauth", error="unauthorized", error_description="Full authentication is required to access this resource"
我已经知道我必须添加一个 CorsFilter 并且另外发现 this post 我在第一个答案中使用了代码片段来让 OPTIONS 请求访问 /oauth/token
而无需凭据:
@Order(-1)
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
}
}
之后我用 curl 得到了以下错误:
{"timestamp":1433370068120,"status":403,"error":"Forbidden","message":"Expected CSRF token not found. Has your session expired?","path":"/oauth/token"}
为了简单起见,我只是将 http.csrf().disable()
添加到 MyWebSecurity class 的 configure
方法中,这解决了 OPTION 请求的问题,但因此 POST 请求不再有效,我得到 There is no client authentication. Try adding an appropriate authentication filter.
(也有 curl)。
我试图找出是否必须以某种方式连接 MyWebSecurity class 和 AuthServer,但没有任何运气。原始示例(开头的link)也注入了 authenticationManager,但这对我没有任何改变。
找到问题的原因!
如果 CorsFilter 处理了 OPTIONS 请求,我只需要结束过滤器链并return立即得到结果!
SimpleCorsFilter.java
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
public SimpleCorsFilter() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
之后我可以忽略 AuthServer 中的 OPTIONS 预检请求 =D
所以服务器像上面的片段一样工作,你可以忽略开头带有 MyWebSecurity class 的块注释。
嗯,你是对的!这是一个解决方案,对我也有效(我遇到了同样的问题)
但我想为 Java 使用更智能的 CORS 过滤器实现: http://software.dzhuvinov.com/cors-filter.html
这是 Java 应用程序的非常完整的解决方案。
实际上,您可以看到here您的观点是如何解决的。
我使用问题的解决方案找到了解决方案。但我有另一种方式来描述解决方案:
@Configuration
public class WebSecurityGlobalConfig extends WebSecurityConfigurerAdapter {
....
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS);
}
...
}
我使用以下方法遇到了类似的问题
- 后端
Spring Boot 1.5.8.RELEASE
- Spring OAuth2
Spring OAuth 2.2.0.RELEASE
w Vuejs
应用使用axios
ajax 请求库
postman
一切正常!当我开始从 Vuejs
应用发出请求时,出现以下错误
OPTIONS http://localhost:8080/springboot/oauth/token 401 ()
和
XMLHttpRequest cannot load http://localhost:8080/springboot/oauth/token. Response for preflight has invalid HTTP status code 401
稍作阅读后,我发现我可以通过覆盖 WebSecurityConfigurerAdapter
实现中的 configure
来指示 Spring OAuth
忽略 OPTIONS
请求 class 如下
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS);
}
添加上述内容有所帮助,但随后,我遇到了 CORS
特定错误
OPTIONS http://localhost:8080/springboot/oauth/token 403 ()
和
XMLHttpRequest cannot load http://localhost:8080/springboot/oauth/token. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 403.
并在 CorsConfig
的帮助下解决了上述问题,如下所示
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilterRegistrationBean() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.applyPermitDefaultValues();
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("*"));
config.setExposedHeaders(Arrays.asList("content-length"));
config.setMaxAge(3600L);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
添加上述class后,效果如预期。在我走之前 prod
我会研究 consequences
使用
web.ignoring().antMatchers(HttpMethod.OPTIONS);
以及 best practices
以上 Cors
配置。目前 *
可以完成工作,但对于生产来说绝对不安全。
Cyril 的回答对我有帮助 partially
然后我在这个 Github 问题中遇到了 CorsConfig
想法。
这里使用Spring引导2。
我必须在 AuthorizationServerConfigurerAdapter
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
Map<String, CorsConfiguration> corsConfigMap = new HashMap<>();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
//TODO: Make configurable
config.setAllowedOrigins(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowedHeaders(Collections.singletonList("*"));
corsConfigMap.put("/oauth/token", config);
endpoints.getFrameworkEndpointHandlerMapping()
.setCorsConfigurations(corsConfigMap);
//additional settings...
}
1-Add the below method to the below method class that extends WebSecurityConfigurerAdapter: // CORS settings @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers(HttpMethod.OPTIONS); }
2-Add the below to my class that extends AuthorizationServerConfigurerAdapter@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // enable cors for "/oauth/token" Map<String, CorsConfiguration> corsConfigMap = new HashMap<>(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.setAllowedOrigins(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.setAllowedHeaders(Collections.singletonList("*")); corsConfigMap.put("/oauth/token", config); endpoints.getFrameworkEndpointHandlerMapping() .setCorsConfigurations(corsConfigMap); // add the other configuration }