3

我在设置 spring 安全性和禁用/启用对经过 jwt 身份验证的基于角色的用户的 graphql服务访问时遇到问题。所有其他REST端点都受到适当保护,JWT身份验证和基于角色的授权正常工作。

到目前为止我所拥有的:

在我的WebSecurityConfigurerAdapter课堂上,我有以下代码:

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.csrf().disable().cors()
                         .and()
                         .authorizeRequests().antMatchers(HttpMethod.OPTIONS, "**/student-service/auth/**").permitAll().antMatchers("**/student-service/auth/**").authenticated()
                         .and()
                         .authorizeRequests().antMatchers(HttpMethod.OPTIONS, "**/graphql/**").permitAll().antMatchers("**/graphql/**").authenticated()
                         .and()
                         .exceptionHandling()
            .authenticationEntryPoint(entryPoint).and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    http.headers().cacheControl();

}

graphql服务中,我有一个@PreAuthorize

@Component 
public class UserResolver implements GraphQLQueryResolver{
    
    @Autowired
    UserRepo repo;

    @PreAuthorize("hasAnyAuthority('ADMIN')")
    public User findUser(int id) {
        return User.builder()
                    .id(1)
                   .email("test@grr.la")
                   .password("123")
                   .username("John")
                   .bankAccount(BankAccount.builder()
                                            .id(1)
                                            .accountName("some account name")
                                            .accountNumber("some account number")
                                            .build())
                    .build();
    }
}

在启用 JWTlocalhost:8080/login并发送graphql查询后,使用上述配置和代码,我得到:

org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73) ~[spring-security-core-5.4.5.jar:5.4.5]
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238) ~[spring-security-core-5.4.5.jar:5.4.5]
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208) ~[spring-security-core-5.4.5.jar:5.4.5]
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:58) ~[spring-security-core-5.4.5.jar:5.4.5]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.5.jar:5.3.5]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.5.jar:5.3.5]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) ~[spring-aop-5.3.5.jar:5.3.5]

这是请求的样子Postman

在此处输入图像描述

GraphQL 查询:

query{
  findUser(id : 1){
    id
    email
  }
}

和回应:

    {
    "errors": [
        {
            "message": "Access is denied",
            "locations": [
                {
                    "line": 2,
                    "column": 1
                }
            ],
            "path": [
                "findUser"
            ],
            "extensions": {
                "type": "AccessDeniedException",
                "classification": "DataFetchingException"
            }
        }
    ],
    "data": {
        "findUser": null
    }
}

application.yml文件:

graphql:
  servlet:
    max-query-depth: 100
    exception-handlers-enabled: true
  playground:
    headers:
      Authorization: Bearer TOKEN 

query.graphqls文件:

type Query{
    
    findUser(id: ID): User
    
}

type User{
    
    id: ID!
    username: String
    password: String
    email: String
    bankAccount: BankAccount
}

type BankAccount {
    id: ID!
    accountName: String
    accountNumber: String
    
}
4

1 回答 1

1

我花了一天时间试图弄清楚这一点。在您的数据获取环境中,如果您调用

environment.getContext()

您应该取回一个GraphQLContext实例,该实例具有 HTTP 请求和具有授权的标头。对我来说,这本质上是一个空的HashMap,没有关于请求的详细信息。在四处挖掘并尝试了 AOP 更改的所有内容之后,我发现了来自 auth0 的建议,以创建一个实现GraphQLInvocation的类. 这是我的解决方案,它将 Spring Security 上下文的实例放入数据获取环境上下文对象中。我现在至少能够验证数据获取器,因为我有一个 Spring Security 上下文可以使用(具有授予的权限等)。我宁愿有一个与 Spring Security 集成的过滤器(我可以让 preAuthorize 方法像你正在做),但我现在正在滚动这个时间。

@Primary
@Component
@Internal
public class SecurityContextGraphQLInvocation implements GraphQLInvocation {

    private final GraphQL graphQL;
    private final AuthenticationManager authenticationManager;

    public SecurityContextGraphQLInvocation(GraphQL graphQL, AuthenticationManager authenticationManager) {
        this.graphQL = graphQL;
        this.authenticationManager = authenticationManager;
    }

    @Override
    public CompletableFuture<ExecutionResult> invoke(GraphQLInvocationData invocationData, WebRequest webRequest) {
        final String header = webRequest.getHeader("Authorization");
        SecurityContext securityContext;
        if (header == null || !header.startsWith("Bearer ")) {
            securityContext = new SecurityContextImpl();
        } else {
            String authToken = header.substring(7);
            JwtAuthenticationToken authRequest = new JwtAuthenticationToken(authToken);
            final var authentication = authenticationManager.authenticate(authRequest);
            securityContext = new SecurityContextImpl(authentication);
        }

        ExecutionInput executionInput = ExecutionInput.newExecutionInput()
                .query(invocationData.getQuery())
                .context(securityContext)
                .operationName(invocationData.getOperationName())
                .variables(invocationData.getVariables())
                .build();
        return graphQL.executeAsync(executionInput);
    }
}
于 2021-07-03T19:04:42.397 回答