Spring + GraphQL - 可选授权
Spring + GraphQL - optional authorization
我目前正在使用 Spring Boot Starter and GraphQL Java Tools 在我的 Spring 应用程序中使用 GraphQL。只要我授权 graphql 端点,它就可以与我的授权过滤器一起使用。现在我想打开 public 的某些突变或查询(因此不需要授权),这就是我绊倒的地方。如何打开 graphql 端点但仍然能够使用 Spring 安全性的 @PreAuthorize
注释进行方法级授权?换句话说:是否可以在端点上获得 "optional" 授权?
这是我的配置:
@Override
protected void configure(HttpSecurity http) throws Exception {
log.debug("configureHttpSecurity");
// Only authorize the request if it is NOT in the permitAllEndpoints AND matches API_ROOT_URL OR
// MESSAGING_ROOT_URL
List<RequestMatcher> requestMatchers = new ArrayList<>();
requestMatchers.add(new SkipPathRequestMatcher(permitAllEndpointList, API_ROOT_URL));
requestMatchers.add(new AntPathRequestMatcher(MESSAGING_ROOT_URL));
OrRequestMatcher apiMatcher = new OrRequestMatcher(requestMatchers);
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(permitAllEndpointList.toArray(new String[0]))
.permitAll()
.and()
.authorizeRequests()
.antMatchers(API_ROOT_URL, MESSAGING_ROOT_URL)
.authenticated()
.and()
.addFilterBefore(new CustomCorsFilter(),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new AuthenticationFilter(authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new AuthorizationFilter(apiMatcher),
UsernamePasswordAuthenticationFilter.class);
}
apiMatcher
是开放某些REST端点。
这是我的 AuthorizationFilter
:
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, ServletException {
try {
String authorization = httpServletRequest.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Bearer ")) {
return getAuthentication(authorization.replace("Bearer ", ""));
}
} catch (ExecutionException e) {
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN,"The provided token was either not valid or is already expired!");
return null;
} catch (IOException | InterruptedException e) {
httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,"There was a problem verifying the supplied token!");
return null;
}
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized");
return null;
}
如果我在 attemptAuthentication
结束时不发送错误,我将能够访问不应打开的 REST 端点。此外,如果我只允许 GraphQL 端点,则不会发生任何授权,因此每个 @PreAuthorize
都会失败,即使我提供了有效的 JWT。
可能我的做法已经错了。如果是这种情况,请告诉我。
我使用两个安全配置解决了这个问题。一个用于我的 REST api,它已强制执行 authentication/authorization。另一个有一个可选的授权过滤器,它只在令牌存在时才使用它。
现在唯一的缺点是所有查询和突变默认情况下都对 public 开放,需要 @PreAuthorize
注释才能关闭。
我目前正在使用 Spring Boot Starter and GraphQL Java Tools 在我的 Spring 应用程序中使用 GraphQL。只要我授权 graphql 端点,它就可以与我的授权过滤器一起使用。现在我想打开 public 的某些突变或查询(因此不需要授权),这就是我绊倒的地方。如何打开 graphql 端点但仍然能够使用 Spring 安全性的 @PreAuthorize
注释进行方法级授权?换句话说:是否可以在端点上获得 "optional" 授权?
这是我的配置:
@Override
protected void configure(HttpSecurity http) throws Exception {
log.debug("configureHttpSecurity");
// Only authorize the request if it is NOT in the permitAllEndpoints AND matches API_ROOT_URL OR
// MESSAGING_ROOT_URL
List<RequestMatcher> requestMatchers = new ArrayList<>();
requestMatchers.add(new SkipPathRequestMatcher(permitAllEndpointList, API_ROOT_URL));
requestMatchers.add(new AntPathRequestMatcher(MESSAGING_ROOT_URL));
OrRequestMatcher apiMatcher = new OrRequestMatcher(requestMatchers);
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(permitAllEndpointList.toArray(new String[0]))
.permitAll()
.and()
.authorizeRequests()
.antMatchers(API_ROOT_URL, MESSAGING_ROOT_URL)
.authenticated()
.and()
.addFilterBefore(new CustomCorsFilter(),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new AuthenticationFilter(authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new AuthorizationFilter(apiMatcher),
UsernamePasswordAuthenticationFilter.class);
}
apiMatcher
是开放某些REST端点。
这是我的 AuthorizationFilter
:
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, ServletException {
try {
String authorization = httpServletRequest.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Bearer ")) {
return getAuthentication(authorization.replace("Bearer ", ""));
}
} catch (ExecutionException e) {
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN,"The provided token was either not valid or is already expired!");
return null;
} catch (IOException | InterruptedException e) {
httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,"There was a problem verifying the supplied token!");
return null;
}
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized");
return null;
}
如果我在 attemptAuthentication
结束时不发送错误,我将能够访问不应打开的 REST 端点。此外,如果我只允许 GraphQL 端点,则不会发生任何授权,因此每个 @PreAuthorize
都会失败,即使我提供了有效的 JWT。
可能我的做法已经错了。如果是这种情况,请告诉我。
我使用两个安全配置解决了这个问题。一个用于我的 REST api,它已强制执行 authentication/authorization。另一个有一个可选的授权过滤器,它只在令牌存在时才使用它。
现在唯一的缺点是所有查询和突变默认情况下都对 public 开放,需要 @PreAuthorize
注释才能关闭。