zl程序教程

您现在的位置是:首页 >  其他

当前栏目

spring security authorization server token端点配置跨域访问

2023-04-18 13:09:37 时间

版本

spring-security-oauth2-2.3.8

问题

在网页端跨域访问spring-security-oauth2搭建的授权服务器,以ClientCredentials授权模式获取token,发生跨域请求失败。

解决

方案1

  1. WebSecurityConfig 增加忽略OPTIONS请求配置
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/oauth/token");
    }
}
  1. AuthorizationServerConfig 增加CORS配置
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    	...
    	CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setMaxAge(3600L);
        Map<String, CorsConfiguration> corsConfigurationMap = new HashMap<>();
        corsConfigurationMap.put("/oauth/token", corsConfiguration);
        endpoints.getFrameworkEndpointHandlerMapping().setCorsConfigurations(corsConfigurationMap);
    }
}

方案2

增加CROS过滤器

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyCorsFilter implements Filter {

    public MyCorsFilter() {
    }

    @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() {
    }
}

分析

跨域请求时会先发送OPTIONS请求,而OPTIONS请求头并不会携带认证信息 Spring源码中地址/oauth/token默认的访问控制策略是”fullyAuthenticated“,导致跨域时OPTIONS请求因认证失败而跳转/ERROR返回错误响应。 org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration

@Override
protected void configure(HttpSecurity http) throws Exception {
	AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
	FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping();
	http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);
	configure(configurer);
	http.apply(configurer);
	String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");
	String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");
	String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");
	if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
		UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
		endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
	}
	// @formatter:off
	http
       	.authorizeRequests()
           	.antMatchers(tokenEndpointPath).fullyAuthenticated()
           	.antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess())
           	.antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess())
       .and()
       	.requestMatchers()
           	.antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath)
       .and()
       	.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
	// @formatter:on
	http.setSharedObject(ClientDetailsService.class, clientDetailsService);
}