ساختن یک VPN ایمن در Android با WireGuard: یک راهنمای کامل

👋 سلام همه ،
در این راهنما ، ما چگونه می آموزیم که ادغام شود محافظ به شما اندرویدی برنامه با استفاده از Jetpack آهنگسازی وت کلاتلینبشر طیف گسترده ای از پروتکل های VPN برای ادغام با اندروید در بازار موجود است و ما انتخاب کردیم محافظ (یک پروتکل VPN مدرن) که به دلیل سادگی ، سرعت و ویژگی های امنیتی قوی شناخته شده است. در پایان این آموزش ، شما یک اجرای Wireguard VPN را خواهید داشت که می توانید در برنامه خود ادغام شوید.
اگر تازه وارد VPN ها هستید یا با اصطلاحات مرتبط ناآشنا هستید ، لطفاً این وبلاگ را قبل از ادامه مطلب بخوانید:
اگر قبلاً با VPN ها آشنا هستید یا وبلاگ را قبلاً خوانده اید ، بیایید شروع کنیم !!!
فهرست مطالب
قبل از اجرای VPN ، پروتکل های مختلف VPN را مورد بررسی قرار دادم و Wireguard را مناسب ترین انتخاب برای توسعه موبایل کردم. سادگی ، سرعت و ویژگی های امنیتی قوی آن از پروتکل های سنتی مانند OpenVPN و IPSec مشخص است. Wireguard حتی بهتر است زیرا بسیاری از افراد از آن پشتیبانی می کنند ، و نمونه های کد رایگان زیادی به صورت آنلاین وجود دارد. این باعث می شود اضافه کردن WireGuard به یک برنامه Android آسانتر شود.
# پیش نیازهای اساسی برای تنظیم پروژه شما
قبل از شروع توسعه ، اطمینان از نصب همه ابزارهای لازم و به روز بودن آنها ضروری است.
اطمینان حاصل کنید که موارد زیر را قبل از شروع آماده کرده اید:
استودیوی اندرویدی – آخرین نسخه پایدار را نصب کنید.
سرور VPN – برای تولید سرور به سرور نیاز دارید wg.conf
پرونده برای Wireguard.
# تست نحوه عملکرد VPN در دستگاه ها
برای آزمایش نحوه کار VPN روی دستگاه شما (لپ تاپ ، موبایل و غیره) ، می توانید برنامه های موجود را از فروشگاه بارگیری کنید. در اینجا برخی از بهترین برنامه ها آورده شده است:
شما به یک فایل پیکربندی نیاز دارید (به عنوان مثال. client.conf
) برای اجرای VPN روی دستگاه. با پسر ارائه دهنده سرور خود تماس بگیرید یا به صورت شخصی ایجاد کنید.
در اینجا چند پرونده نمونه برای نشان دادن نحوه پیکربندی به نظر می رسد پرونده: client.conf
# تنظیم پروژه
آیا شما آماده شیرجه زدن به قسمت برنامه نویسی هستید؟ بیایید شروع کنیم!
وابستگی به پروژه اضافه کنید
dependencies {
// Add WireGuard dependency
implementation("com.wireguard.android:tunnel:1.0.20210211")
// Add desugaring library for Java 8+ API support
implementation("com.android.tools:desugar_jdk_libs:2.1.5")
}
پیکربندی سرویس VPN در AndroidManifest.xml
قبل از استفاده از ماژول WireGuard ، خود را به روز کنید AndroidManifest.xml
پرونده
مجوزهای مورد نیاز را اضافه کنید:
این مجوزها به برنامه اجازه می دهد تا به اینترنت دسترسی پیدا کند و وضعیت شبکه را بررسی کند:
خدمات VPN را ثبت کنید:
موارد زیر را در داخل اضافه کنید
برای فعال کردن سرویس VPN WireGuard:
به منطق برنامه نویسی شیرجه بزنید
- یک کلاس ایجاد کنید
WireGuardTunnel.kt
در پروژه شما این کلاس به عنوان یک بسته بندی برای یک تونل Wireguard عمل می کند و به شما امکان می دهد وضعیت آن را به طور کارآمد پیگیری و مدیریت کنید.
import com.wireguard.android.backend.Tunnel
typealias StateChangeCallback = (Tunnel.State) -> Unit
class WireGuardTunnel(
private var name: String,
private val onStateChanged: StateChangeCallback? = null
) : Tunnel {
private var state: Tunnel.State = Tunnel.State.DOWN
override fun getName() = name
override fun onStateChange(newState: Tunnel.State) {
state = newState
onStateChanged?.invoke(newState)
}
fun getState(): Tunnel.State = state
}
- برای مدیریت جزئیات سرور VPN ، موارد زیر را اضافه کنید
ServerInfo
کلاس داده به پروژه شما:
import com.google.gson.annotations.SerializedName
data class ServerInfo(
// Interface details
@SerializedName("address") val interfaceAddress: String?,
@SerializedName("dns") val interfaceDns: String?,
@SerializedName("private_key") val interfacePrivateKey: String?,
// Peer details
@SerializedName("public_key") val peerPublicKey: String?,
@SerializedName("preshared_key") val peerPresharedKey: String?,
@SerializedName("allowed_ips") val peerAllowedIPs: String?,
@SerializedName("endpoint") val peerEndpoint: String?,
@SerializedName("persistent_keep_alive") val peerPersistentKeepalive: String?
)
- برای ردیابی حالتهای مختلف اتصال VPN ، کلاس enum vpnstatus زیر را اضافه کنید:
enum class VPNStatus {
PREPARE, // VPN is getting ready
CONNECTING, // Establishing a connection
CONNECTED, // VPN is active and running
DISCONNECTING, // Disconnecting from the VPN
DISCONNECTED, // VPN is not connected
NO_CONNECTION, // No available VPN connection
REFRESHING // Refreshing VPN status
}
- برای رسیدگی به عملیات VPN ، موارد زیر را اضافه کنید
WireguardManager
کلاس. بعداً آن را با روش های بیشتر گسترش خواهیم داد.
import android.app.Activity
import android.content.Context
import com.wireguard.android.backend.Backend
import kotlinx.coroutines.*
class WireguardManager(private val context: Context, private val activity: Activity?) {
private val scope = CoroutineScope(Job() + Dispatchers.Main.immediate)
private var backend: Backend? = null
private var tunnelName: String = "wg_default"
private var config: Config? = null
private var tunnel: WireGuardTunnel? = null
private val futureBackend = CompletableDeferred()
private val TAG = "WireguardManager"
companion object {
private var state: VPNStatus = VPNStatus.NO_CONNECTION
}
}
- روش اولیه را در
WireguardManager
کلاس:
class WireguardManager(private val context: Context, private val activity: Activity?) {
// Remaining code
init {
scope.launch(Dispatchers.IO) {
try {
cachedTunnelData = SharedPreferenceHelper.getVpnData()
backend = GoBackend(context)
futureBackend.complete(backend!!)
activity?.let { GoBackend.VpnService.prepare(it) }
} catch (e: Throwable) {
Log.e(TAG, "ERROR: Exception during WireguardManager initialization: ${e.localizedMessage}")
Log.e(TAG, Log.getStackTraceString(e))
}
}
}
}
class WireguardManager(private val context: Context, private val activity: Activity?) {
// Remaining code
/**
* Generates WireGuard configuration from the provided ServerInfo.
* This creates a wg-quick compatible configuration for the VPN tunnel.
*/
private fun getConfigData(tunnelData: ServerInfo): Config {
val wgQuickConfig = """
[Interface]
Address = ${tunnelData.interfaceAddress ?: ""}
DNS = ${tunnelData.interfaceDns ?: ""}
PrivateKey = ${tunnelData.interfacePrivateKey ?: ""}
[Peer]
PublicKey = ${tunnelData.peerPublicKey ?: ""}
PresharedKey = ${tunnelData.peerPresharedKey ?: ""}
AllowedIPs = ${tunnelData.peerAllowedIPs ?: ""}
Endpoint = ${tunnelData.peerEndpoint ?: ""}
PersistentKeepalive = ${tunnelData.peerPersistentKeepalive ?: ""}
""".trimIndent()
val inputStream = ByteArrayInputStream(wgQuickConfig.toByteArray())
return Config.parse(inputStream)
}
/**
* Checks if any VPN connection is currently active on the device.
* Returns `true` if a VPN connection is detected, otherwise `false`.
*/
val isVpnActive: Boolean
get() {
return try {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
connectivityManager.activeNetwork ?: return false
} else {
// For Android < 6.0, use the old method
val networkInfo = connectivityManager.activeNetworkInfo
return networkInfo != null && networkInfo.type == ConnectivityManager.TYPE_VPN
}
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
// Check if any VPN is active
networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) == true
} catch (e: Exception) {
Log.e(TAG, "isVpnActive - ERROR - ${e.localizedMessage}", e)
false
}
}
/**
* Retrieves an existing WireGuard tunnel or creates a new one if none exists.
* The `callback` function listens for state changes in the tunnel.
*/
private fun getTunnel(name: String, callback: StateChangeCallback? = null): WireGuardTunnel {
if (tunnel == null) {
tunnel = WireGuardTunnel(name, callback)
}
return tunnel as WireGuardTunnel
}
}
- روش هایی را برای به روزرسانی وضعیت اضافه کنید:
class WireguardManager(private val context: Context, private val activity: Activity?) {
// Remaining code
/**
* Updates the VPN status based on the tunnel's state.
* Runs on the main thread to ensure UI updates happen smoothly.
*/
private fun updateStageFromState(state: Tunnel.State) {
scope.launch(Dispatchers.Main) {
when (state) {
Tunnel.State.UP -> updateStage(VPNStatus.CONNECTED) // VPN is active
Tunnel.State.DOWN -> updateStage(VPNStatus.DISCONNECTED) // VPN is disconnected
else -> updateStage(VPNStatus.NO_CONNECTION) // No active VPN connection
}
}
}
/**
* Sets the VPN status and saves it in shared preferences.
* Ensures status updates run on the main thread.
*/
private fun updateStage(stage: VPNStatus?) {
scope.launch(Dispatchers.Main) {
val updatedStage = stage ?: VPNStatus.NO_CONNECTION
state = updatedStage
// Store VPN status in SharedPreferences if required
}
}
/**
* Returns the VPN status based on the tunnel's state.
*/
fun getStatus(): VPNStatus {
return state
}
}
- روش هایی را برای شروع سرویس VPN اضافه کنید:
class WireguardManager(private val context: Context, private val activity: Activity?) {
// Remaining code
/**
* Starts the VPN connection process.
* Initializes the tunnel and attempts to connect.
*/
fun start(tunnelData: ServerInfo) {
initialize(tunnelName)
connect(tunnelData)
}
/**
* Initializes the tunnel with a given name.
* Ensures the tunnel name is valid before proceeding.
*/
private fun initialize(localizedDescription: String) {
if (Tunnel.isNameInvalid(localizedDescription)) {
Log.e(TAG, "Invalid Tunnel Name: $localizedDescription")
return
}
tunnelName = localizedDescription
}
/**
* Connects to the VPN using the provided tunnel configuration.
* Updates VPN status at different stages of the connection process.
*/
private fun connect(tunnelData: ServerInfo) {
scope.launch(Dispatchers.IO) {
try {
updateStage(VPNStatus.PREPARE) // Preparing VPN connection
// Generate WireGuard configuration
config = getConfigData(tunnelData)
updateStage(VPNStatus.CONNECTING) // Attempting to connect
// Retrieve or create the WireGuard tunnel
val tunnel = getTunnel(tunnelName) { state ->
scope.launch {
Log.i(TAG, "onStateChange - $state")
updateStageFromState(state)
}
}
// Activate the VPN connection
futureBackend.await().setState(tunnel, Tunnel.State.UP, config)
scope.launch(Dispatchers.Main) {
updateStage(VPNStatus.CONNECTED) // VPN is successfully connected
// Store VPN status in SharedPreferences if required
}
Log.i(TAG, "Connect - success!")
} catch (e: Throwable) {
updateStage(VPNStatus.NO_CONNECTION) // Failed to establish a connection
Log.e(TAG, "Connect - ERROR - ${e.message}")
}
}
}
}
- روش هایی را برای قطع سرویس VPN اضافه کنید:
class WireguardManager(private val context: Context, private val activity: Activity?) {
// Remaining code
/**
* Stops the VPN connection by calling the disconnect method.
*/
fun stop() {
disconnect()
}
/**
* Disconnects the active VPN tunnel.
* - If no tunnel is running, logs an error.
* - Updates VPN status before and after disconnection.
* - Handles reconnection if cached tunnel data exists.
*/
private fun disconnect() {
scope.launch(Dispatchers.IO) {
try {
// Check if any tunnel is currently running
if (futureBackend.await().runningTunnelNames.isEmpty()) {
throw Exception("Tunnel is not running")
}
updateStage(VPNStatus.DISCONNECTING)
// Retrieve the active tunnel and monitor state changes
val tunnel = getTunnel(tunnelName) { state ->
scope.launch {
Log.i(TAG, "onStateChange - $state")
updateStageFromState(state)
}
}
// Set the tunnel state to DOWN to disconnect
futureBackend.await().setState(tunnel, Tunnel.State.DOWN, config)
// Update VPN status and shared preferences on the main thread
scope.launch(Dispatchers.Main) {
updateStage(VPNStatus.DISCONNECTED)
// Store VPN status in SharedPreferences if required
}
Log.i(TAG, "Disconnect - success!")
} catch (e: Throwable) {
Log.e(TAG, "Disconnect - ERROR - ${e.message}")
}
}
}
}
- اکنون یک کلاس مدل View ایجاد کنید
VPNViewModel
، مسئول رسیدگی به حالت VPN ، شروع/متوقف کردن اتصال و نظارت بر وضعیت VPN به طور کارآمد.
class VPNViewModel(application: Application) : AndroidViewModel(application) {
private val context by lazy { application.applicationContext }
private var wireguardManager: WireguardManager? = null
// VPN connection status as a StateFlow
private val _vpnState = MutableStateFlow(VPNStatus.NO_CONNECTION)
val vpnState: StateFlow = _vpnState.asStateFlow()
// Whether VPN is currently active
private val _isVpnActive = MutableStateFlow(false)
val isVpnActive: StateFlow = _isVpnActive.asStateFlow()
/**
* Initializes the WireGuard manager and starts monitoring VPN state changes.
* This method should be called when the ViewModel is created.
*/
fun initVPN(activity: Activity) {
wireguardManager = WireguardManager(context, activity)
// Observe VPN state changes in a coroutine
viewModelScope.launch {
while (isActive) {
delay(500) // Check every 500ms (half a second)
// Restore last known VPN state from SharedPreferences if needed and assign in fallback
_vpnState.value = wireguardManager?.getStatus()
?: VPNStatus.NO_CONNECTION
_isVpnActive.value = wireguardManager?.isVpnActive ?: false
}
}
}
/**
* Starts the VPN connection with the given tunnel data.
*/
fun startVPN(tunnelData: ServerInfo) {
viewModelScope.launch {
wireguardManager?.start(tunnelData)
}
}
/**
* Stops the active VPN connection.
*/
fun stopVPN() {
viewModelScope.launch {
wireguardManager?.stop()
}
}
}
- مدیریت اتصالات VPN با ViewModel در JetPack آهنگسازی
// Handling VPN Permission Request
val vpnPermissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// Permission granted, now connect
viewModel.startVPN(tunnelData)
} else {
// Permission denied, show an error or update UI
}
}
// Starting a VPN Connection
coroutineScope.launch(Dispatchers.IO) {
val intent = GoBackend.VpnService.prepare(activity)
delay(100) // Small delay to prevent UI lag
if (intent != null) {
vpnPermissionLauncher.launch(intent) // Request permission if needed
} else {
viewModel.startVPN(tunnelData) // No permission needed, start directly
}
}
// Stopping a VPN Connection
coroutineScope.launch(Dispatchers.IO) {
delay(100) // Simulate network request
withContext(Dispatchers.Main) {
viewModel.stopVPN() // Stop the VPN connection
}
}
# بیایید بپیچیم
مدیریت اتصالات VPN در یک برنامه Android نیاز به رسیدگی به مجوزها ، شروع اتصالات و مدیریت قطع ارتباطات دارد. با استفاده از آهنگسازی JetPack و ViewModel ، می توانیم ضمن نگه داشتن کد سازماندهی و مدیریت آسان ، برنامه را آسان کنیم.
اگر این وبلاگ را مفید یا سؤال دیگری پیدا کردید ، دوست داریم از شما بشنویم. احساس کردن رایگان برای رسیدن به وت ما را دنبال کنید در سیستم عامل های رسانه های اجتماعی ما برای راهنمایی ها و آموزش های بیشتر در مورد پست های فناوری گرا.
برنامه نویسی مبارک!