diff --git a/core/modules/book/book.services.yml b/core/modules/book/book.services.yml
index 740252e85b..fc3f50a808 100644
--- a/core/modules/book/book.services.yml
+++ b/core/modules/book/book.services.yml
@@ -15,7 +15,7 @@ services:
     arguments: ['@entity_type.manager', '@book.manager']
   book.outline_storage:
     class: Drupal\book\BookOutlineStorage
-    arguments: ['@database']
+    arguments: ['@database', '@current_user']
     tags:
       - { name: backend_overridable }
   access_check.book.removable:
diff --git a/core/modules/book/src/BookOutlineStorage.php b/core/modules/book/src/BookOutlineStorage.php
index 2224bb669e..a67791ef2d 100644
--- a/core/modules/book/src/BookOutlineStorage.php
+++ b/core/modules/book/src/BookOutlineStorage.php
@@ -3,6 +3,7 @@
 namespace Drupal\book;
 
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Session\AccountInterface;
 
 /**
  * Defines a storage class for books outline.
@@ -16,18 +17,39 @@ class BookOutlineStorage implements BookOutlineStorageInterface {
    */
   protected $connection;
 
+  /**
+   * Current user service.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
   /**
    * Constructs a BookOutlineStorage object.
    */
-  public function __construct(Connection $connection) {
+  public function __construct(Connection $connection, AccountInterface $currentUser = NULL) {
     $this->connection = $connection;
+    if (!$currentUser) {
+      @trigger_error('The current_user service must be passed to ' . __NAMESPACE__ . '\BookOutlineStorage::__construct(). It was added in drupal:8.9.0 and will be required before drupal:10.0.0.', E_USER_DEPRECATED);
+      $currentUser = \Drupal::service('current_user');
+    }
+    $this->currentUser = $currentUser;
   }
 
   /**
    * {@inheritdoc}
    */
   public function getBooks() {
-    return $this->connection->query("SELECT DISTINCT(bid) FROM {book}")->fetchCol();
+    $currentUserId = $this->currentUser->id();
+    if ($this->currentUser->hasPermission('view any unpublished content')) {
+      return $this->connection->query("SELECT DISTINCT(bid) FROM {book}")->fetchCol();
+    }
+    elseif ($this->currentUser->hasPermission('view own unpublished content')) {
+      return $this->connection->query("SELECT DISTINCT(bid) FROM {book} b INNER JOIN {node_field_data} f ON b.nid = f.nid WHERE b.pid = 0 AND (f.uid = $currentUserId OR f.status = 1)")->fetchCol();
+    }
+    else {
+      return $this->connection->query("SELECT DISTINCT(bid) FROM {book} b INNER JOIN {node_field_data} f ON b.nid = f.nid WHERE f.status = 1 AND b.pid = 0")->fetchCol();
+    }
   }
 
   /**
diff --git a/core/modules/book/tests/src/Functional/BookTest.php b/core/modules/book/tests/src/Functional/BookTest.php
index d34ae43563..9c807f8b08 100644
--- a/core/modules/book/tests/src/Functional/BookTest.php
+++ b/core/modules/book/tests/src/Functional/BookTest.php
@@ -22,6 +22,7 @@ class BookTest extends BrowserTestBase {
    * @var array
    */
   protected static $modules = [
+    'content_moderation',
     'book',
     'block',
     'node_access_test',
@@ -71,6 +72,7 @@ protected function setUp(): void {
       'create book content',
       'edit own book content',
       'add content to books',
+      'view own unpublished content',
     ]);
     $this->webUser = $this->drupalCreateUser([
       'access printer-friendly version',
@@ -91,6 +93,7 @@ protected function setUp(): void {
       'node test view',
       'administer content types',
       'administer site configuration',
+      'view any unpublished content',
     ]);
   }
 
@@ -543,6 +546,39 @@ public function testBookListing() {
     $this->drupalGet('book');
 
     $this->assertText($this->book->label(), 'The book title is displayed on the book listing page.');
+
+    // Unpublish the top book page and confirm that the created book title is
+    // not displayed for anonymous.
+    $this->book->setUnpublished();
+    $this->book->save();
+
+    $this->drupalGet('book');
+    $this->assertSession()->responseNotContains($this->book->label());
+
+    // Publish the top book page and unpublish a page in the book and confirm
+    // that the created book title is displayed for anonymous.
+    $this->book->setPublished();
+    $this->book->save();
+    $nodes[0]->setUnpublished();
+    $nodes[0]->save();
+
+    $this->drupalGet('book');
+    $this->assertSession()->responseContains($this->book->label());
+
+    // Unpublish the top book page and confirm that the created book title is
+    // displayed for user which has 'view own unpublished content' permission.
+    $this->drupalLogin($this->bookAuthor);
+    $this->book->setUnpublished();
+    $this->book->save();
+
+    $this->drupalGet('book');
+    $this->assertSession()->responseContains($this->book->label());
+
+    // Confirm that the created book title is displayed for user which has
+    // 'view any unpublished content' permission.
+    $this->drupalLogin($this->adminUser);
+    $this->drupalGet('book');
+    $this->assertSession()->responseContains($this->book->label());
   }
 
   /**
