diff --git a/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/BasicPixivSession.java b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/BasicPixivSession.java
new file mode 100644
index 0000000..84f74a6
--- /dev/null
+++ b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/BasicPixivSession.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 LamGC
+ *
+ * ContentGrabbingJi is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * ContentGrabbingJi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package net.lamgc.pixiv;
+
+import com.google.common.net.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+
+import java.io.IOException;
+
+/**
+ * 基本的 Pixiv 会话对象.
+ * @author LamGC
+ */
+public class BasicPixivSession extends PixivSession {
+
+ private final static String PIXIV_LOGOUT_URL = "https://www.pixiv.net/logout.php";
+ private final HttpClient httpClient;
+ private boolean login = true;
+
+ /**
+ * 构造一个会话对象.
+ * @param cookieStore 会话 Cookies.
+ * @param httpClient 用于请求登出的 HttpClient 对象.
+ */
+ public BasicPixivSession(CookieStore cookieStore, HttpClient httpClient) {
+ super(cookieStore);
+ this.httpClient = httpClient;
+ }
+
+ @Override
+ public boolean logOut() throws IOException {
+ HttpGet request = new HttpGet(PIXIV_LOGOUT_URL);
+ StringBuilder builder = new StringBuilder();
+ getCookies().getCookies().forEach(cookie ->
+ builder.append(cookie.getName()).append("=").append(cookie.getValue()).append("; "));
+ request.addHeader(HttpHeaders.COOKIE, builder.toString());
+
+ HttpResponse response = httpClient.execute(request);
+ final int httpRedirectCode = 302;
+ if (response.getStatusLine().getStatusCode() == httpRedirectCode) {
+ login = false;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isLogin() {
+ return login;
+ }
+
+}
diff --git a/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivLogin.java b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivLogin.java
new file mode 100644
index 0000000..023dd2c
--- /dev/null
+++ b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivLogin.java
@@ -0,0 +1,16 @@
+package net.lamgc.pixiv;
+
+/**
+ * @author LamGC
+ */
+public interface PixivLogin {
+
+ /**
+ * 执行登录操作.
+ * @return 返回登录后的会话对象
+ * @see PixivSession
+ * @throws PixivLoginException 当无法完成登录时抛出.
+ */
+ PixivSession login() throws PixivLoginException;
+
+}
diff --git a/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivLoginException.java b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivLoginException.java
new file mode 100644
index 0000000..9f462ab
--- /dev/null
+++ b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivLoginException.java
@@ -0,0 +1,17 @@
+package net.lamgc.pixiv;
+
+/**
+ * Pixiv 登录异常.
+ * @author LamGC
+ */
+public class PixivLoginException extends Exception {
+
+ public PixivLoginException(String message) {
+ super(message);
+ }
+
+ public PixivLoginException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivSession.java b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivSession.java
new file mode 100644
index 0000000..525335c
--- /dev/null
+++ b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/PixivSession.java
@@ -0,0 +1,43 @@
+package net.lamgc.pixiv;
+
+import org.apache.http.client.CookieStore;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Pixiv 会话.
+ * @author LamGC
+ */
+public abstract class PixivSession {
+
+ private final CookieStore cookieStore;
+
+ protected PixivSession(CookieStore cookieStore) {
+ this.cookieStore = Objects.requireNonNull(cookieStore);
+ }
+
+ /**
+ * 登出当前会话.
+ *
+ *
登出后该会话 Cookies 将会失效.
+ * @return 如果登出成功, 返回 true.
+ * @throws IOException 当尝试登出发生异常时抛出.
+ */
+ public abstract boolean logOut() throws IOException;
+
+ /**
+ * 检查是否已登录, 或者说会话是否有效.
+ * @return 如果会话已登出(失效), 返回 false.
+ */
+ public abstract boolean isLogin();
+
+ /**
+ * 获取 CookieStore 对象.
+ * @return 返回 CookieStore.
+ */
+ public CookieStore getCookies() {
+ return cookieStore;
+ }
+
+}
diff --git a/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/login/ObjectInputStreamLogin.java b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/login/ObjectInputStreamLogin.java
new file mode 100644
index 0000000..6312385
--- /dev/null
+++ b/ContentGrabbingJi-pixiv/src/main/java/net/lamgc/pixiv/login/ObjectInputStreamLogin.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 LamGC
+ *
+ * ContentGrabbingJi is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * ContentGrabbingJi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package net.lamgc.pixiv.login;
+
+import net.lamgc.pixiv.*;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.HttpClient;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * 读入对象进行登录.
+ * @author LamGC
+ */
+public class ObjectInputStreamLogin implements PixivLogin {
+
+ private final HttpClient httpClient;
+ private final ObjectInputStream inputStream;
+ private final AtomicReference sessionCache = new AtomicReference<>();
+
+ public ObjectInputStreamLogin(HttpClient httpClient, InputStream cookieInput) throws IOException {
+ this.httpClient = httpClient;
+ this.inputStream = new ObjectInputStream(cookieInput);
+ }
+
+
+ @Override
+ public synchronized PixivSession login() throws PixivLoginException {
+ if (sessionCache.get() == null) {
+ CookieStore cookieStore;
+ try {
+ cookieStore = (CookieStore) inputStream.readObject();
+ } catch (Exception e) {
+ throw new PixivLoginException("An exception occurred while trying to read in a Java serialized object", e);
+ }
+ PixivSession session = new BasicPixivSession(cookieStore, this.httpClient);
+ sessionCache.set(session);
+ return session;
+ } else {
+ return sessionCache.get();
+ }
+
+ }
+}