Last active
October 13, 2021 15:02
-
-
Save michalgebauer/5efb8d0d588b4ea3960759ed050ac277 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Component | |
public class ResourceResolver implements GraphQLQueryResolver { | |
public String securedResource() { | |
return "Secured resource"; | |
} | |
@PreAuthorize("hasRole('ROLE_ADMIN')") | |
public String securedResourceAdmin() { | |
return "Secured resource Admin"; | |
} | |
public String unsecuredResource() { | |
return "Unsecured resource"; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Component | |
public class ResourceResolver implements GraphQLQueryResolver { | |
// This method requires authenticated user by default | |
public String securedResource() { | |
return "Secured resource"; | |
} | |
// This method requires user with role ADMIN | |
@PreAuthorize("hasRole('ROLE_ADMIN')") | |
public String securedResourceAdmin() { | |
return "Secured resource Admin"; | |
} | |
// This method can be called by unauthenticated user | |
@Unsecured | |
public String unsecuredResource() { | |
return "Unsecured resource"; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@EnableWebSecurity | |
@EnableGlobalMethodSecurity(prePostEnabled = true) | |
public class SecurityConfig extends WebSecurityConfigurerAdapter { | |
@Override | |
protected void configure(HttpSecurity http) throws Exception { | |
http | |
.csrf().disable() | |
.authorizeRequests() | |
.antMatchers("/graphql").permitAll() | |
.antMatchers("/vendor/**").permitAll() | |
.antMatchers("/graphiql").permitAll() | |
.anyRequest().authenticated() | |
.and() | |
.formLogin(); | |
} | |
@Override | |
protected void configure(AuthenticationManagerBuilder auth) throws Exception { | |
auth | |
.inMemoryAuthentication() | |
.passwordEncoder(passwordEncoder()) | |
.withUser("mi3o").password("{noop}nbusr123").roles("USER").and() | |
.withUser("admin").password("{noop}nbusr123").roles("USER", "ADMIN"); | |
} | |
@Bean | |
public PasswordEncoder passwordEncoder() { | |
return PasswordEncoderFactories.createDelegatingPasswordEncoder(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Aspect | |
@Component | |
@Order(1) | |
public class SecurityQraphQLAspect { | |
/** | |
* All graphQLResolver methods can be called only by authenticated user. | |
* Exclusions are named in Pointcut expression. | |
*/ | |
@Before("allGraphQLResolverMethods() && isDefinedInApplication() && !isUnsecuredResourceMethod()") | |
public void doSecurityCheck() { | |
if (SecurityContextHolder.getContext() == null || | |
SecurityContextHolder.getContext().getAuthentication() == null || | |
!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || | |
AnonymousAuthenticationToken.class.isAssignableFrom(SecurityContextHolder.getContext().getAuthentication().getClass())) { | |
throw new AccessDeniedException("User not authenticated"); | |
} | |
} | |
/** | |
* Matches all beans that implement {@link com.coxautodev.graphql.tools.GraphQLResolver} | |
* note: {@code GraphQLMutationResolver}, {@code GraphQLQueryResolver} etc | |
* extend base GraphQLResolver interface | |
*/ | |
@Pointcut("target(com.coxautodev.graphql.tools.GraphQLResolver)") | |
private void allGraphQLResolverMethods() { | |
} | |
/** | |
* Matches all beans in com.mi3o.springgraphqlsecurity package | |
* resolvers must be in this package (subpackages) | |
*/ | |
@Pointcut("within(com.mi3o.springgraphqlsecurity..*)") | |
private void isDefinedInApplication() { | |
} | |
/** | |
* Exact method signature which will be excluded from security check | |
*/ | |
@Pointcut("execution(public java.lang.String com.mi3o.springgraphqlsecurity.resolver.ResourceResolver.unsecuredResource())") | |
private void isUnsecuredResourceMethod() { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Aspect | |
@Component | |
@Order(1) | |
public class SecurityQraphQLAspect { | |
/** | |
* All graphQLResolver methods can be called only by authenticated user. | |
* @Unsecured annotated methods are excluded | |
*/ | |
@Before("allGraphQLResolverMethods() && isDefinedInApplication() && !isMethodAnnotatedAsUnsecured()") | |
public void doSecurityCheck() { | |
// same as shown previously | |
} | |
/** | |
* Any method annotated with @Unsecured | |
*/ | |
@Pointcut("@annotation(com.mi3o.springgraphqlsecurity.config.Unsecured)") | |
private void isMethodAnnotatedAsUnsecured() { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@SpringBootApplication | |
@EnableAspectJAutoProxy | |
public class SpringGraphqlSecurityApplication { | |
public static void main(String[] args) { | |
SpringApplication.run(SpringGraphqlSecurityApplication.class, args); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@RunWith(SpringRunner.class) | |
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) | |
public class SpringGraphqlSecurityApplicationTests { | |
@Autowired | |
private ResourceResolver resourceResolver; | |
@Test | |
public void unsecured_resource_ok() { | |
resourceResolver.unsecuredResource(); | |
} | |
@Test(expected = AccessDeniedException.class) | |
public void secured_unauthorized_access_throws_exception() { | |
resourceResolver.securedResource(); | |
} | |
@Test | |
@WithMockUser(username = "mi3o") | |
public void secured_ok() { | |
resourceResolver.securedResource(); | |
} | |
@Test(expected = AccessDeniedException.class) | |
public void admin_unauthorized_access_throws_exception() { | |
resourceResolver.securedResourceAdmin(); | |
} | |
@WithMockUser(username = "mi3o") | |
@Test(expected = AccessDeniedException.class) | |
public void without_admin_role_throws_exception() { | |
resourceResolver.securedResourceAdmin(); | |
} | |
@WithMockUser(username = "admin", roles = "ADMIN") | |
@Test | |
public void admin_role_ok() { | |
resourceResolver.securedResourceAdmin(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Marking annotation that will switch off security check for given method. | |
* Works only for methods defined in GraphQL Resolvers | |
*/ | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.METHOD) | |
public @interface Unsecured { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment