3 namespace Drupal\Core\Access;
5 use Drupal\Component\Utility\Crypt;
6 use Drupal\Core\Render\BubbleableMetadata;
7 use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
8 use Symfony\Component\Routing\Route;
11 * Processes the outbound route to handle the CSRF token.
13 class RouteProcessorCsrf implements OutboundRouteProcessorInterface {
16 * The CSRF token generator.
18 * @var \Drupal\Core\Access\CsrfTokenGenerator
23 * Constructs a RouteProcessorCsrf object.
25 * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
26 * The CSRF token generator.
28 public function __construct(CsrfTokenGenerator $csrf_token) {
29 $this->csrfToken = $csrf_token;
35 public function processOutbound($route_name, Route $route, array &$parameters, BubbleableMetadata $bubbleable_metadata = NULL) {
36 if ($route->hasRequirement('_csrf_token')) {
37 $path = ltrim($route->getPath(), '/');
38 // Replace the path parameters with values from the parameters array.
39 foreach ($parameters as $param => $value) {
40 $path = str_replace("{{$param}}", $value, $path);
42 // Adding this to the parameters means it will get merged into the query
43 // string when the route is compiled.
44 if (!$bubbleable_metadata) {
45 $parameters['token'] = $this->csrfToken->get($path);
48 // Generate a placeholder and a render array to replace it.
49 $placeholder = Crypt::hashBase64($path);
50 $placeholder_render_array = [
51 '#lazy_builder' => ['route_processor_csrf:renderPlaceholderCsrfToken', [$path]],
54 // Instead of setting an actual CSRF token as the query string, we set
55 // the placeholder, which will be replaced at the very last moment. This
56 // ensures links with CSRF tokens don't break cacheability.
57 $parameters['token'] = $placeholder;
58 $bubbleable_metadata->addAttachments(['placeholders' => [$placeholder => $placeholder_render_array]]);
64 * #lazy_builder callback; gets a CSRF token for the given path.
67 * The path to get a CSRF token for.
70 * A renderable array representing the CSRF token.
72 public function renderPlaceholderCsrfToken($path) {
74 '#markup' => $this->csrfToken->get($path),
75 // Tokens are per session.