spring-security GraphQL和Spring安全性使用@PreAuthorize?

hsvhsicv  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(290)

我在为graphql服务设置Spring Security 和禁用/启用对jwt身份验证的基于角色的用户的访问时遇到问题。所有其他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();
    }
}

localhost:8080/login上获取JWT并发送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

}
qaxu7uf2

qaxu7uf21#

我花了一天的时间试图弄明白这一点。

environment.getContext()

您应该返回一个 GraphQLContext 的示例,它包含HTTP请求和带有授权的头。对我来说,这实际上是一个空的 HashMap,没有关于请求的任何细节。在挖掘并尝试AOP更改的所有内容之后,我发现auth0建议创建一个实现 GraphQLInvocation 的类。下面是我的解决方案,它将SpringSecurity上下文的一个示例放置到数据获取环境上下文对象中。(具有授权等)。我更希望有一个与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);
    }
}

相关问题