Grails 4.x中GraphQL集成Spring Security及视图角色控制实现
Hey there! Let's tackle your two questions about Spring Security integration with Grails 4.x GraphQL, using your provided Career entity as a reference.
First, make sure you have the core dependencies set up in your build.gradle:
dependencies { // Spring Security Core plugin for Grails 4.x implementation 'org.grails.plugins:spring-security-core:4.0.0' // Grails GraphQL plugin implementation 'org.grails.plugins:graphql:1.0.0' // Match version to your Grails 4.x setup }
Next, configure basic Spring Security rules in grails-app/conf/application.yml to secure the GraphQL endpoint:
grails: plugin: springsecurity: userLookup: userDomainClassName: 'com.yourpackage.User' # Update to your User domain authorityJoinClassName: 'com.yourpackage.UserRole' # Update to your join class authority: className: 'com.yourpackage.Role' # Update to your Role domain controllerAnnotations: staticRules: - pattern: '/graphql' access: ['isAuthenticated()'] # Require login for all GraphQL requests
Now, add role-based checks directly to your GraphQL data fetchers. Let's modify your Career entity example to restrict the careerByName query to users with the ROLE_ADMIN role:
import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.access.AccessDeniedException class Career { String name String description Integer positions static graphql = GraphQLMapping.build { query('careerByName', Career){ dataFetcher(new DataFetcher() { @Override Object get(DataFetchingEnvironment environment) throws Exception { // Get current authenticated user's roles def auth = SecurityContextHolder.context.authentication boolean hasAdminAccess = auth.authorities.any { it.authority == 'ROLE_ADMIN' } if (!hasAdminAccess) { throw new AccessDeniedException("You need admin privileges to access this query") } return Career.findByName(environment.getArgument('name')) } }) } } static constraints = { } static mapping = { } }
If you prefer using Spring Security annotations, create a Spring-managed data fetcher service and annotate the method:
// grails-app/services/CareerDataFetcherService.groovy import org.springframework.security.access.prepost.PreAuthorize import org.springframework.stereotype.Service import graphql.schema.DataFetcher import graphql.schema.DataFetchingEnvironment @Service class CareerDataFetcherService implements DataFetcher<Career> { @PreAuthorize("hasRole('ROLE_ADMIN')") @Override Career get(DataFetchingEnvironment environment) throws Exception { return Career.findByName(environment.getArgument('name')) } } // Update Career.groovy to use the service static graphql = GraphQLMapping.build { query('careerByName', Career) { dataFetcher(applicationContext.getBean(CareerDataFetcherService)) } }
Field-Level Access Control
If you want to restrict access to specific fields (e.g., only admins can see the positions field), add role checks directly in the field's data fetcher:
static graphql = GraphQLMapping.build { query('careerByName', Career){ dataFetcher(...) // Your existing query-level role check // Restrict positions field to admins only field('positions') { dataFetcher(new DataFetcher<Integer>() { @Override Integer get(DataFetchingEnvironment environment) throws Exception { def auth = SecurityContextHolder.context.authentication if (!auth.authorities.any { it.authority == 'ROLE_ADMIN' }) { return null // Or throw an AccessDeniedException } return environment.source.positions } }) } } }
Conditional Query/Field Visibility
To hide entire queries or fields from unauthorised users (so they don't appear in the GraphQL schema), you can conditionally define them based on the user's role:
static graphql = GraphQLMapping.build { def auth = SecurityContextHolder.context.authentication boolean isAdmin = auth?.authorities?.any { it.authority == 'ROLE_ADMIN' } // Only expose the careerByName query to admins if (isAdmin) { query('careerByName', Career){ dataFetcher(...) } } }
Note: This checks roles at schema build time, so it works best for static role-based visibility. For per-request dynamic checks, stick to data fetcher-level validation.
内容的提问来源于stack exchange,提问作者user2253081

