diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index 20b9af390..b75420012 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -55,6 +55,7 @@ object Versions {
     const val bouncycastle = "1.64"
     const val mavenArtifactResolver = "1.7.3"
     const val mavenResolverProvider = "3.8.4"
+    const val zxing = "3.5.0"
 
     const val junit = "5.7.2"
 
@@ -157,3 +158,6 @@ const val `maven-resolver-impl` = "org.apache.maven.resolver:maven-resolver-impl
 const val `maven-resolver-connector-basic` = "org.apache.maven.resolver:maven-resolver-connector-basic:${Versions.mavenArtifactResolver}"
 const val `maven-resolver-transport-http` = "org.apache.maven.resolver:maven-resolver-transport-http:${Versions.mavenArtifactResolver}"
 const val `maven-resolver-provider` = "org.apache.maven:maven-resolver-provider:${Versions.mavenResolverProvider}"
+
+const val `zxing-javase` = "com.google.zxing:javase:${Versions.zxing}"
+const val `zxing-core` = "com.google.zxing:core:${Versions.zxing}"
diff --git a/mirai-core-api/src/jvmMain/kotlin/utils/SwingSolver.kt b/mirai-core-api/src/jvmMain/kotlin/utils/SwingSolver.kt
index b93ec1364..32d23a035 100644
--- a/mirai-core-api/src/jvmMain/kotlin/utils/SwingSolver.kt
+++ b/mirai-core-api/src/jvmMain/kotlin/utils/SwingSolver.kt
@@ -102,10 +102,16 @@ public object SwingSolver : LoginSolver() {
         val title = "Mirai UnsafeDeviceLoginVerify(${bot.id})"
         return SwingLoginSolver(
             title, "",
-            arrayOf(
+            mutableListOf<Any>(
                 "", HyperLinkLabel(url, "设备锁验证", title),
                 "URL", JTextField(url),
-            ),
+            ).also { components ->
+                val qr = PlatformImageUtil.generateQRCode(url, 300, 300)
+                if (qr != null) {
+                    components.add("")
+                    components.add(JLabel(ImageIcon(qr)))
+                }
+            }.toTypedArray(),
             hiddenInput = true,
             topComponent = JLabel(
                 """
diff --git a/mirai-core-utils-addition/src/androidMain/kotlin/PlatformImageUtilImpl.kt b/mirai-core-utils-addition/src/androidMain/kotlin/PlatformImageUtilImpl.kt
new file mode 100644
index 000000000..f73eea8bc
--- /dev/null
+++ b/mirai-core-utils-addition/src/androidMain/kotlin/PlatformImageUtilImpl.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019-2022 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.utils.addition
+
+import android.graphics.Bitmap
+import com.google.zxing.BarcodeFormat
+import com.google.zxing.WriterException
+import com.google.zxing.common.BitMatrix
+import com.google.zxing.qrcode.QRCodeWriter
+import net.mamoe.mirai.utils.PlatformImage
+import net.mamoe.mirai.utils.PlatformImageUtil
+
+public class PlatformImageUtilImpl : PlatformImageUtil {
+    override val available: Boolean get() = true
+
+    override fun generateQRCode(content: String, width: Int, height: Int): PlatformImage? {
+        val bitMatrix: BitMatrix = try {
+            QRCodeWriter().encode(
+                content,
+                BarcodeFormat.QR_CODE,
+                width,
+                height
+            )
+        } catch (e: WriterException) {
+            throw RuntimeException(e)
+        }
+        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
+        for (x in 0 until width) {
+            for (y in 0 until height) {
+                bitmap.setPixel(
+                    x, y, if (bitMatrix[x, y]) {
+                        0x000000
+                    } else {
+                        0xFFFFFF
+                    }
+                )
+            }
+        }
+        return bitmap
+    }
+}
\ No newline at end of file
diff --git a/mirai-core-utils-addition/src/jvmMain/kotlin/PlatformImageUtilImpl.kt b/mirai-core-utils-addition/src/jvmMain/kotlin/PlatformImageUtilImpl.kt
new file mode 100644
index 000000000..3877628a4
--- /dev/null
+++ b/mirai-core-utils-addition/src/jvmMain/kotlin/PlatformImageUtilImpl.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019-2022 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.utils.addition
+
+import com.google.zxing.BarcodeFormat
+import com.google.zxing.WriterException
+import com.google.zxing.client.j2se.MatrixToImageWriter
+import com.google.zxing.common.BitMatrix
+import com.google.zxing.qrcode.QRCodeWriter
+import net.mamoe.mirai.utils.PlatformImage
+import net.mamoe.mirai.utils.PlatformImageUtil
+
+public class PlatformImageUtilImpl : PlatformImageUtil {
+    override val available: Boolean get() = true
+
+    override fun generateQRCode(content: String, width: Int, height: Int): PlatformImage? {
+        val bitMatrix: BitMatrix = try {
+            QRCodeWriter().encode(
+                content,
+                BarcodeFormat.QR_CODE,
+                width,
+                height
+            )
+        } catch (e: WriterException) {
+            throw RuntimeException(e)
+        }
+
+        return MatrixToImageWriter.toBufferedImage(bitMatrix)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core-utils/src/androidMain/kotlin/Actuals.kt b/mirai-core-utils/src/androidMain/kotlin/Actuals.kt
index 8645198cf..c7bb8300b 100644
--- a/mirai-core-utils/src/androidMain/kotlin/Actuals.kt
+++ b/mirai-core-utils/src/androidMain/kotlin/Actuals.kt
@@ -12,11 +12,14 @@
 
 package net.mamoe.mirai.utils
 
+import android.graphics.Bitmap
 import android.util.Base64
 import java.util.*
 import kotlin.reflect.KClass
 import kotlin.reflect.full.createInstance
 
+public actual typealias PlatformImage = Bitmap
+
 
 public actual fun ByteArray.encodeBase64(): String {
     return Base64.encodeToString(this, Base64.DEFAULT)
diff --git a/mirai-core-utils/src/commonMain/kotlin/PlatformImage.kt b/mirai-core-utils/src/commonMain/kotlin/PlatformImage.kt
new file mode 100644
index 000000000..04b1506b1
--- /dev/null
+++ b/mirai-core-utils/src/commonMain/kotlin/PlatformImage.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019-2022 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+@file:JvmMultifileClass
+
+package net.mamoe.mirai.utils
+
+
+public expect class PlatformImage
+
+public interface PlatformImageUtil {
+    public val available: Boolean
+
+    /**
+     * @return `null` --- Unsupported
+     */
+    public fun generateQRCode(
+        content: String,
+        width: Int,
+        height: Int
+    ): PlatformImage?
+
+    public companion object INSTANCE : PlatformImageUtil by initService()
+}
+
+private fun initService(): PlatformImageUtil {
+    return loadServiceOrNull(
+        PlatformImageUtil::class,
+        "net.mamoe.mirai.utils.addition.PlatformImageUtilImpl"
+    ) ?: DummyPIU
+}
+
+private object DummyPIU : PlatformImageUtil {
+    override val available: Boolean get() = false
+    override fun generateQRCode(content: String, width: Int, height: Int): PlatformImage? = null
+}
\ No newline at end of file
diff --git a/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt b/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt
index 417c334f5..dcc13f06f 100644
--- a/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt
+++ b/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt
@@ -12,10 +12,12 @@
 
 package net.mamoe.mirai.utils
 
+import java.awt.image.BufferedImage
 import java.util.*
 import kotlin.reflect.KClass
 import kotlin.reflect.full.createInstance
 
+public actual typealias PlatformImage = BufferedImage
 
 public actual fun ByteArray.encodeBase64(): String {
     return Base64.getEncoder().encodeToString(this)