《機器學習最強入門:基礎數學/機率/統計邁向AI真實數據專題實作-王者歸來》
作者:洪錦魁/洪锦魁
深智數位股份有限公司 2023
下載:https://libgen.is/book/index.php?md5=DE4C26EA99FE42EA1A2C5FF2B6824D9C
博客
-
Libgen中文新书速递:《機器學習最強入門:基礎數學/機率/統計邁向AI真實數據專題實作-王者歸來
-
深入浅出:WordPress插件开发基础
欢迎来到WordPress插件开发的世界!WordPress插件不仅能为您的网站添加各种功能,还能让您在广大的WordPress社区中分享您的创意和技术成果。本文将带您逐步了解WordPress插件的开发基础,从头文件要求到钩子的使用,帮助您快速入门并掌握插件开发的核心要素。
WordPress插件入门
一个WordPress插件其实就是一个带有特定头注释的PHP文件。头注释包含了插件的元数据,例如插件名称和作者等。我们强烈建议为插件创建一个专门的目录,以保持文件整齐有序,便于后续维护。
创建一个简单的插件
- 切换到WordPress站点的插件目录
cd wp-content/plugins
- 创建一个新目录
mkdir plugin-name cd plugin-name
- 创建一个PHP文件
vi plugin-name.php
注意:你可以使用任意你喜欢的文本编辑器来创建和编辑PHP文件。
- 添加插件头注释
<?php /* Plugin Name: YOUR PLUGIN NAME */
保存文件后,您可以在WordPress后台的插件列表中看到您创建的插件。
钩子:Action和Filter
WordPress钩子是插件开发的核心。通过钩子,您可以在特定的时机介入WordPress的代码执行流程,而无需编辑任何核心文件。
- Action钩子:允许您添加或修改WordPress的功能。
- Filter钩子:允许您修改用户提交的或展示给用户的内容。
使用基础钩子
在创建插件时,需要使用以下三个基础钩子:
- register_activation_hook()
插件激活时运行,用于设置插件的初始状态,如添加默认设置等。
register_activation_hook(__FILE__, 'my_plugin_activate'); function my_plugin_activate() { // 初始化代码 }
- register_deactivation_hook()
插件禁用时运行,用于清理插件数据。
register_deactivation_hook(__FILE__, 'my_plugin_deactivate'); function my_plugin_deactivate() { // 清理代码 }
- register_uninstall_hook()
插件卸载时运行,用于彻底删除插件数据。
register_uninstall_hook(__FILE__, 'my_plugin_uninstall'); function my_plugin_uninstall() { // 卸载代码 }
添加自定义钩子
您可以使用
do_action()
函数添加自定义钩子,其他开发者可以通过这些钩子扩展或修改您的插件。do_action('my_custom_hook');
移除挂载到钩子上的函数
使用
remove_action()
可以移除挂载到某个钩子上的函数。remove_action('my_custom_hook', 'my_function');
WordPress API的使用
WordPress提供了丰富的API,大大简化了插件开发的过程。例如:
- 选项API:用于将数据保存到数据库中。
- HTTP API:用于发送HTTP请求。
插件的发布
在分享插件之前,请选择适当的许可证。推荐使用GNU通用公共许可证(GPLv2+),以确保与WordPress核心许可证兼容。
插件头文件要求
头文件至少需要包含插件名称,并可选择性地包含以下部分:
- 插件名称:(必需)插件的名称。
- 插件URI:插件主页的URL。
- 描述:简短描述,不超过140个字符。
- 版本:例如1.0或1.0.3。
- 作者:插件作者的名字。
- 作者URI:作者的个人网址。
结语
通过本文的介绍,您应该已经对WordPress插件开发有了基本的了解。插件开发不仅能提升您网站的功能性,还能为广大WordPress用户提供便利。快来动手开发属于您的第一个插件吧!
如需更详细的教程和示例,欢迎访问WordPress插件开发教程手册。
-
WordPress 插件开发
学习 WordPress 插件开发是一个非常有益且实用的技能,可以帮助你扩展和定制 WordPress 网站的功能。以下是一个详细的指南,帮助你从零开始学习 WordPress 插件开发。
1. 基础知识准备
在开始插件开发之前,你需要具备一些基本知识:
- HTML/CSS:了解基本的网页结构和样式。
- JavaScript:掌握基本的编程逻辑和DOM操作。
- PHP:WordPress 插件主要使用 PHP 编写,需要了解 PHP 的基本语法和功能。
- MySQL:了解基本的数据库操作,因为 WordPress 使用 MySQL 作为数据库。
2. 设置开发环境
为插件开发设置一个合适的开发环境:
- 本地服务器:使用工具如 XAMPP、MAMP 或 Local by Flywheel 来设置一个本地开发服务器。
- 文本编辑器或 IDE:选择一个代码编辑器,如 Visual Studio Code、Sublime Text 或 PHPStorm。
- WordPress 安装:在本地服务器上安装最新版本的 WordPress。
3. 了解 WordPress 插件结构
一个简单的 WordPress 插件通常包含以下文件和文件夹:
- 插件主文件:通常命名为
plugin-name.php
,包含插件的主要功能代码。 - README 文件:包含插件的基本信息和说明。
- 其他文件:如 CSS、JavaScript、图片等资源文件。
4. 创建第一个插件
下面是创建一个简单插件的步骤:
4.1 创建插件目录和主文件
在
wp-content/plugins
目录下创建一个新文件夹,例如my-first-plugin
。在该文件夹中创建一个 PHP 文件,例如my-first-plugin.php
。4.2 插件头部信息
在
my-first-plugin.php
文件中添加插件的头部信息:<?php /* Plugin Name: My First Plugin Plugin URI: https://example.com/my-first-plugin Description: This is my first WordPress plugin. Version: 1.0 Author: Your Name Author URI: https://example.com License: GPL2 */
4.3 插件的基本功能
添加一些基本功能,如在 WordPress 后台显示一个简单的信息:
function my_first_plugin_admin_notice() { echo '<div class="notice notice-success is-dismissible"> <p>My First Plugin is activated!</p> </div>'; } add_action('admin_notices', 'my_first_plugin_admin_notice');
5. 学习 WordPress 钩子(Hooks)
WordPress 插件开发的核心是钩子(Hooks),它包括动作钩子(Action Hooks)和过滤器钩子(Filter Hooks)。通过钩子,你可以在特定的时间点执行代码:
- 动作钩子(Actions):允许你在 WordPress 的执行过程中插入自定义代码。
- 过滤器钩子(Filters):允许你修改 WordPress 数据或输出。
例子:
// 动作钩子 add_action('wp_footer', 'my_first_plugin_footer_message'); function my_first_plugin_footer_message() { echo '<p>Thank you for visiting my website!</p>'; } // 过滤器钩子 add_filter('the_content', 'my_first_plugin_content_filter'); function my_first_plugin_content_filter($content) { return $content . '<p>Custom content added by My First Plugin.</p>'; }
6. 学习 WordPress 插件 API
WordPress 提供了丰富的 API 供插件开发者使用:
- Settings API:管理插件设置页面。
- Shortcode API:创建短代码。
- Widgets API:创建自定义小工具。
- REST API:创建自定义 REST 端点。
7. 学习插件国际化和本地化
为了使你的插件可以被翻译成不同的语言,了解如何国际化和本地化插件是很重要的:
// 加载插件文本域 function my_first_plugin_load_textdomain() { load_plugin_textdomain( 'my-first-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); } add_action('plugins_loaded', 'my_first_plugin_load_textdomain');
8. 探索插件开发资源
- 官方文档:阅读 WordPress 插件开发手册。
- 教程和书籍:查找相关的在线教程和书籍,如《Professional WordPress Plugin Development》。
9. 配置插件设置页面
为插件添加一个设置页面是常见的需求。以下是如何使用 Settings API 创建一个简单的插件设置页面的步骤:
9.1 添加菜单项
首先,需要在 WordPress 管理菜单中添加一个新的菜单项:
function my_first_plugin_menu() { add_menu_page( 'My First Plugin Settings', // 页面标题 'My First Plugin', // 菜单标题 'manage_options', // 用户权限 'my-first-plugin', // 菜单别名 'my_first_plugin_settings_page', // 回调函数 'dashicons-admin-generic' // 图标 ); } add_action('admin_menu', 'my_first_plugin_menu');
9.2 创建设置页面
在回调函数
my_first_plugin_settings_page
中定义设置页面的内容:function my_first_plugin_settings_page() { ?> <div class="wrap"> <h1>My First Plugin Settings</h1> <form method="post" action="options.php"> <?php settings_fields('my_first_plugin_options_group'); do_settings_sections('my-first-plugin'); submit_button(); ?> </form> </div> <?php }
9.3 注册设置
使用 Settings API 注册插件设置:
function my_first_plugin_settings_init() { register_setting('my_first_plugin_options_group', 'my_first_plugin_option_name'); add_settings_section( 'my_first_plugin_settings_section', // ID 'My First Plugin Settings', // 标题 'my_first_plugin_settings_section_callback', // 回调函数 'my-first-plugin' // 页面 ); add_settings_field( 'my_first_plugin_field', // ID 'Sample Field', // 标签 'my_first_plugin_field_callback', // 回调函数 'my-first-plugin', // 页面 'my_first_plugin_settings_section' // 部分 ); } add_action('admin_init', 'my_first_plugin_settings_init'); function my_first_plugin_settings_section_callback() { echo 'Enter your settings below:'; } function my_first_plugin_field_callback() { $option = get_option('my_first_plugin_option_name'); echo '<input type="text" name="my_first_plugin_option_name" value="' . esc_attr($option) . '" />'; }
10. 创建短代码
短代码允许用户在文章和页面中插入自定义内容。以下是如何创建一个简单的短代码:
function my_first_plugin_shortcode($atts) { $atts = shortcode_atts( array( 'text' => 'Hello, World!', ), $atts, 'my_shortcode' ); return '<div class="my-shortcode">' . esc_html($atts['text']) . '</div>'; } add_shortcode('my_shortcode', 'my_first_plugin_shortcode');
11. 创建小工具
小工具可以在 WordPress 侧边栏等小工具区域显示内容。以下是如何创建一个简单的小工具:
“`php
class My_First_Plugin_Widget extends WP_Widget {
function construct() { parent::_construct( ‘my_first_plugin_widget’, // ID (‘My First Plugin Widget’, ‘text_domain’), // 名称
array(‘description’ => _(‘A simple widget’, ‘text_domain’)) // 描述
);
}public function widget($args, $instance) { echo $args['before_widget']; echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title']; echo '<p>' . esc_html($instance['text']) . '</p>'; echo $args['after_widget']; } public function form($instance) { $title = !empty($instance['title']) ? $instance['title'] : __('New title', 'text_domain'); $text = !empty($instance['text']) ? $instance['text'] : __('Hello, World!', 'text_domain'); ?> <p> <label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php _e('Title:'); ?></label> <input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title); ?>"> </p> <p> <label for="<?php echo esc_attr($this->get_field_id('text')); ?>"><?php _e('Text:'); ?></label>
11. 创建小工具(续)
11.1 完成小工具的表单和更新方法
在上面的代码基础上,继续完成小工具的表单和更新方法:
<textarea class="widefat" id="<?php echo esc_attr($this->get_field_id('text')); ?>" name="<?php echo esc_attr($this->get_field_name('text')); ?>"><?php echo esc_attr($text); ?></textarea> </p> <?php } public function update($new_instance, $old_instance) { $instance = array(); $instance['title'] = (!empty($new_instance['title'])) ? strip_tags($new_instance['title']) : ''; $instance['text'] = (!empty($new_instance['text'])) ? strip_tags($new_instance['text']) : ''; return $instance; } } // 注册小工具 function register_my_first_plugin_widget() { register_widget('My_First_Plugin_Widget'); } add_action('widgets_init', 'register_my_first_plugin_widget');
12. 使用 WordPress REST API
WordPress REST API 允许你创建和访问自定义端点,从而使你的插件能够与外部应用程序进行通信。
12.1 创建自定义 REST 端点
以下是如何创建一个简单的自定义 REST 端点:
function my_first_plugin_register_routes() { register_rest_route('my-first-plugin/v1', '/data/', array( 'methods' => 'GET', 'callback' => 'my_first_plugin_get_data', )); } add_action('rest_api_init', 'my_first_plugin_register_routes'); function my_first_plugin_get_data($request) { return new WP_REST_Response(array('message' => 'Hello, World!'), 200); }
13. 安全性和最佳实践
确保你的插件是安全的,并遵循最佳实践:
13.1 数据验证和清理
在处理用户输入时,确保对数据进行验证和清理:
// 验证和清理输入数据 function my_first_plugin_validate_data($data) { return sanitize_text_field($data); }
13.2 使用非ces (Nonces) 进行安全验证
在处理表单提交时,使用非ces 来防止跨站请求伪造 (CSRF) 攻击:
// 创建一个 nonce function my_first_plugin_create_nonce() { return wp_create_nonce('my_first_plugin_nonce_action'); } // 验证 nonce function my_first_plugin_verify_nonce($nonce) { return wp_verify_nonce($nonce, 'my_first_plugin_nonce_action'); }
13.3 避免直接文件访问
在插件的每个文件顶部添加以下代码,防止直接访问:
if (!defined('ABSPATH')) { exit; // 退出如果直接访问 }
14. 插件国际化和本地化(续)
确保你的插件可以被翻译成不同的语言:
14.1 创建语言文件
在插件目录下创建一个
languages
文件夹,并使用.pot
文件保存插件的翻译字符串。例如,创建一个my-first-plugin.pot
文件。14.2 加载语言文件
在插件初始化时加载语言文件:
function my_first_plugin_load_textdomain() { load_plugin_textdomain('my-first-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages'); } add_action('plugins_loaded', 'my_first_plugin_load_textdomain');
15. 发布和分发插件
当你完成插件开发后,可以考虑将插件发布到 WordPress 插件目录中。
15.1 准备插件
确保你的插件包含以下文件:
- 主插件文件:包含插件头部信息和主要功能代码。
- README 文件:详细描述插件的功能、安装方法和使用说明。
- 语言文件:包含
.pot
文件和翻译文件。 - 其他资源文件:如 CSS、JavaScript、图片等资源文件。
15.2 创建 README 文件
使用 Markdown 编写 README 文件,并确保它包含以下部分:
- Plugin Name: 插件名称
- Description: 插件描述
- Installation: 安装说明
- Frequently Asked Questions: 常见问题解答
- Changelog: 更新日志
15.3 提交插件
访问 WordPress 插件提交页面 并按照指南提交你的插件。一旦审核通过,你的插件将出现在 WordPress 插件目录中。
16. 进一步学习资源
要深入学习 WordPress 插件开发,以下资源非常有用:
16.1 官方文档
- WordPress Plugin Handbook: 官方插件开发手册,包含详细的开发指南和示例代码。
- WordPress Code Reference: 提供了 WordPress 核心函数、类、钩子的详细文档。
16.2 在线课程和教程
- Udemy: 提供了许多关于 WordPress 插件开发的在线课程。
- Lynda/LinkedIn Learning: 提供了高质量的 WordPress 开发视频教程。
16.3 开发者社区
- WordPress Stack Exchange: 专门针对 WordPress 开发问题的问答社区。
- WordPress.org 支持论坛: 官方支持论坛,可以在这里提问和回答问题。
16.4 博客和文章
- Tuts+: 提供了大量关于 WordPress 开发的文章和教程。
- Smashing Magazine: 定期发布关于 WordPress 的开发技巧和最佳实践的文章。
17. 高级插件开发技巧
17.1 面向对象编程 (OOP)
使用面向对象编程风格可以使你的插件代码更具可维护性和可扩展性。以下是一个简单的 OOP 插件示例:
class My_First_Plugin { public function __construct() { add_action('init', array($this, 'init')); } public function init() { // 初始化代码 } public function register_post_type() { $args = array( 'public' => true, 'label' => 'Custom Post', ); register_post_type('custom_post', $args); } } $my_first_plugin = new My_First_Plugin();
17.2 使用命名空间
使用命名空间可以避免类名和函数名冲突:
namespace MyFirstPlugin; class Main { public function __construct() { add_action('init', array($this, 'init')); } public function init() { // 初始化代码 } public function register_post_type() { $args = array( 'public' => true, 'label' => 'Custom Post', ); register_post_type('custom_post', $args); } } $main = new Main();
17.3 自动加载
使用自动加载器可以简化类文件的加载:
spl_autoload_register(function ($class) { $prefix = 'MyFirstPlugin\\'; $base_dir = __DIR__ . '/inc/'; $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { return; } $relative_class = substr($class, $len); $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; if (file_exists($file)) { require $file; } });
17.4 单例模式
使用单例模式确保插件的主类只实例化一次:
class My_First_Plugin { private static $instance = null; private function __construct() { add_action('init', array($this, 'init')); } public static function get_instance() { if (self::$instance == null) { self::$instance = new self(); } return self::$instance; } public function init() { // 初始化代码 } } My_First_Plugin::get_instance();
18. 性能优化
确保你的插件不会对网站性能产生负面影响:
18.1 使用缓存
利用 WordPress 内置的缓存功能:
function get_cached_data() { $cache_key = 'my_first_plugin_cached_data'; $data = wp_cache_get($cache_key); if ($data === false) { // 获取数据 $data = '...'; wp_cache_set($cache_key, $data, '', 3600); } return $data; }
19. 数据库操作
19.1 创建自定义数据库表
有时候,插件需要创建自定义数据库表来存储特定的数据。以下是创建自定义表的示例:
function my_first_plugin_create_table() { global $wpdb; $table_name = $wpdb->prefix . 'my_first_plugin_table'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, name tinytext NOT NULL, email text NOT NULL, date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, PRIMARY KEY (id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } register_activation_hook(__FILE__, 'my_first_plugin_create_table');
19.2 插入数据
function my_first_plugin_insert_data($name, $email) { global $wpdb; $table_name = $wpdb->prefix . 'my_first_plugin_table'; $wpdb->insert( $table_name, array( 'name' => $name, 'email' => $email, 'date' => current_time('mysql'), ) ); }
19.3 获取数据
function my_first_plugin_get_data() { global $wpdb; $table_name = $wpdb->prefix . 'my_first_plugin_table'; $results = $wpdb->get_results("SELECT * FROM $table_name", ARRAY_A); return $results; }
20. AJAX 操作
使用 AJAX 可以在不刷新页面的情况下与服务器进行交互。
20.1 在前端调用 AJAX
jQuery(document).ready(function($) { $('#my-button').click(function() { $.ajax({ url: my_first_plugin_ajax_object.ajax_url, type: 'POST', data: { action: 'my_first_plugin_action', data: 'some_data' }, success: function(response) { console.log(response); } }); }); });
20.2 在后端处理 AJAX 请求
function my_first_plugin_ajax_handler() { $data = $_POST['data']; // 处理数据 $response = array('message' => 'Success', 'data' => $data); wp_send_json_success($response); } add_action('wp_ajax_my_first_plugin_action', 'my_first_plugin_ajax_handler'); add_action('wp_ajax_nopriv_my_first_plugin_action', 'my_first_plugin_ajax_handler');
21. 使用自定义钩子和过滤器
创建自定义钩子和过滤器,使其他开发者可以扩展你的插件功能。
21.1 创建自定义动作钩子
function my_first_plugin_do_custom_action() { do_action('my_first_plugin_custom_action'); } function my_first_plugin_add_custom_action() { echo 'Custom Action Triggered!'; } add_action('my_first_plugin_custom_action', 'my_first_plugin_add_custom_action');
21.2 创建自定义过滤器
function my_first_plugin_apply_custom_filter($content) { return apply_filters('my_first_plugin_custom_filter', $content); } function my_first_plugin_modify_content($content) { return $content . ' - Modified by custom filter'; } add_filter('my_first_plugin_custom_filter', 'my_first_plugin_modify_content');
22. 使用第三方库
有时,你可能需要在插件中使用第三方库。你可以通过 Composer 来管理依赖关系。
22.1 安装 Composer
确保在你的开发环境中安装了 Composer。
22.2 创建
composer.json
文件在插件根目录下创建一个
composer.json
文件:{ "name": "yourname/yourplugin", "description": "A description of your plugin", "require": { "some/package": "^1.0" } }
22.3 安装依赖
在终端中运行以下命令:
composer install
22.4 在插件中加载 Composer 自动加载器
在插件的主文件中添加以下代码:
require_once __DIR__ . '/vendor/autoload.php';
23. 测试和调试(续)
23.1 使用 WP_DEBUG
在
wp-config.php
文件中启用调试模式:define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); @ini_set('display_errors', 0);
此设置会将错误记录到
wp-content/debug.log
文件中,而不是直接显示在页面上。23.2 使用 PHPStorm 和 Xdebug 进行调试
- 安装 Xdebug: 根据你的 PHP 版本和操作系统安装 Xdebug。
- 配置 php.ini: 添加以下行到你的
php.ini
文件:zend_extension="path/to/xdebug.so" xdebug.remote_enable=1 xdebug.remote_host=127.0.0.1 xdebug.remote_port=9000 xdebug.remote_handler=dbgp
- 配置 PHPStorm: 在 PHPStorm 中配置 Xdebug 远程调试。在 “Preferences” -> “Languages & Frameworks” -> “PHP” -> “Debug” 中设置 Xdebug 端口为 9000。
- 设置断点: 在 PHPStorm 中打开你的插件代码并设置断点。
- 启动调试: 启动 PHPStorm 的调试监听,然后在浏览器中访问你的插件页面。代码将在断点处暂停,你可以逐步调试。
24. 国际化与本地化
24.1 准备插件以进行翻译
使用
__()
和_e()
函数准备你的插件代码:echo __('Hello, World!', 'my-first-plugin'); _e('Hello, World!', 'my-first-plugin');
24.2 创建 POT 文件
使用
xgettext
或 Poedit 等工具生成 POT 文件:xgettext --from-code=UTF-8 -k__ -k_e -o languages/my-first-plugin.pot $(find . -name "*.php")
24.3 加载翻译文件
在插件初始化时加载翻译文件:
function my_first_plugin_load_textdomain() { load_plugin_textdomain('my-first-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages'); } add_action('plugins_loaded', 'my_first_plugin_load_textdomain');
25. 安全性
25.1 数据验证和清理
确保所有用户输入的数据都经过验证和清理:
$cleaned_data = sanitize_text_field($_POST['data']);
25.2 权限检查
在处理敏感操作之前检查用户权限:
if (!current_user_can('manage_options')) { wp_die(__('You do not have sufficient permissions to access this page.', 'my-first-plugin')); }
25.3 防止 SQL 注入
使用
$wpdb->prepare()
函数来防止 SQL 注入:global $wpdb; $sql = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}my_table WHERE id = %d", $id); $results = $wpdb->get_results($sql);
26. 创建设置页面
26.1 添加设置页面
function my_first_plugin_add_admin_menu() { add_options_page( __('My First Plugin Settings', 'my-first-plugin'), __('My First Plugin', 'my-first-plugin'), 'manage_options', 'my-first-plugin', 'my_first_plugin_options_page' ); } add_action('admin_menu', 'my_first_plugin_add_admin_menu');
26.2 创建设置页面内容
function my_first_plugin_options_page() { ?> <div class="wrap"> <h1><?php _e('My First Plugin Settings', 'my-first-plugin'); ?></h1> <form action="options.php" method="post"> <?php settings_fields('my_first_plugin_options'); do_settings_sections('my-first-plugin'); submit_button(); ?> </form> </div> <?php }
26.3 注册设置
“`php
function my_first_plugin_settings_init() {
register_setting(‘my_first_plugin_options’, ‘my_first_plugin_options’);add_settings_section( 'my_first_plugin_section', __('General Settings', 'my-first-plugin'), 'my_first_plugin_section_callback', 'my-first-plugin' ); add_settings_field( 'my_first_plugin_field_text', __('Sample Text Field', 'my-first-plugin'), 'my_first_plugin_field_text_callback', '
26. 创建设置页面(续)
26.3 注册设置(续)
add_settings_field( 'my_first_plugin_field_text', __('Sample Text Field', 'my-first-plugin'), 'my_first_plugin_field_text_callback', 'my-first-plugin', 'my_first_plugin_section' ); } add_action('admin_init', 'my_first_plugin_settings_init'); function my_first_plugin_section_callback() { echo __('Enter your settings below:', 'my-first-plugin'); } function my_first_plugin_field_text_callback() { $options = get_option('my_first_plugin_options'); ?> <input type="text" name="my_first_plugin_options[my_first_plugin_field_text]" value="<?php echo isset($options['my_first_plugin_field_text']) ? esc_attr($options['my_first_plugin_field_text']) : ''; ?>"> <?php }
27. 创建自定义小工具
27.1 定义小工具类
class My_First_Plugin_Widget extends WP_Widget { public function __construct() { parent::__construct( 'my_first_plugin_widget', // Base ID __('My First Plugin Widget', 'my-first-plugin'), // Name array('description' => __('A sample widget for showing text', 'my-first-plugin')) // Args ); } public function widget($args, $instance) { echo $args['before_widget']; if (!empty($instance['title'])) { echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title']; } echo __('Hello, World!', 'my-first-plugin'); echo $args['after_widget']; } public function form($instance) { $title = !empty($instance['title']) ? $instance['title'] : __('New title', 'my-first-plugin'); ?> <p> <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:', 'my-first-plugin'); ?></label> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>"> </p> <?php } public function update($new_instance, $old_instance) { $instance = array(); $instance['title'] = (!empty($new_instance['title'])) ? strip_tags($new_instance['title']) : ''; return $instance; } }
27.2 注册小工具
function my_first_plugin_register_widgets() { register_widget('My_First_Plugin_Widget'); } add_action('widgets_init', 'my_first_plugin_register_widgets');
28. 使用 REST API
28.1 创建自定义 REST API 路由
function my_first_plugin_register_rest_route() { register_rest_route('my-first-plugin/v1', '/data/', array( 'methods' => 'GET', 'callback' => 'my_first_plugin_get_data', )); } add_action('rest_api_init', 'my_first_plugin_register_rest_route'); function my_first_plugin_get_data($request) { $data = array('message' => 'Hello, World!'); return new WP_REST_Response($data, 200); }
28.2 使用 REST API
在前端使用 JavaScript 访问自定义 REST API 路由:
fetch('/wp-json/my-first-plugin/v1/data/') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));
29. 自定义短代码
29.1 注册短代码
function my_first_plugin_shortcode($atts = [], $content = null) { $atts = array_change_key_case((array)$atts, CASE_LOWER); $a = shortcode_atts([ 'title' => 'Default Title', ], $atts); return '<div class="my-first-plugin">' . esc_html($a['title']) . '</div>'; } add_shortcode('my_first_plugin', 'my_first_plugin_shortcode');
29.2 使用短代码
在文章或页面中使用短代码:
[my_first_plugin title="Hello, World!"]
30. 单元测试(续)
30.1 设置 PHPUnit(续)
在
tests
目录中创建bootstrap.php
文件以初始化测试环境:<?php $_tests_dir = getenv('WP_TESTS_DIR'); if (!$_tests_dir) { $_tests_dir = rtrim(sys_get_temp_dir(), '/\\') . '/wordpress-tests-lib'; } require_once $_tests_dir . '/includes/functions.php'; function _manually_load_plugin() { require dirname(__DIR__) . '/my-first-plugin.php'; } tests_add_filter('muplugins_loaded', '_manually_load_plugin'); require $_tests_dir . '/includes/bootstrap.php';
在
tests
目录中创建test-sample.php
文件以编写第一个测试:<?php class SampleTest extends WP_UnitTestCase { function test_sample() { $this->assertTrue(true); } }
30.2 运行测试
使用以下命令运行 PHPUnit 测试:
phpunit --bootstrap tests/bootstrap.php tests/test-sample.php
31. 使用自定义表单
31.1 创建自定义表单
在插件中使用 HTML 和 PHP 创建自定义表单:
function my_first_plugin_form() { ?> <form action="<?php echo esc_url(admin_url('admin-post.php')); ?>" method="post"> <input type="hidden" name="action" value="my_first_plugin_form_action"> <?php wp_nonce_field('my_first_plugin_form_nonce', 'my_first_plugin_nonce'); ?> <label for="name"><?php _e('Name:', 'my-first-plugin'); ?></label> <input type="text" name="name" id="name" required> <input type="submit" value="<?php _e('Submit', 'my-first-plugin'); ?>"> </form> <?php }
31.2 处理表单提交
在插件中处理表单提交:
function my_first_plugin_form_handler() { if (!isset($_POST['my_first_plugin_nonce']) || !wp_verify_nonce($_POST['my_first_plugin_nonce'], 'my_first_plugin_form_nonce')) { wp_die(__('Nonce verification failed', 'my-first-plugin')); } if (!current_user_can('manage_options')) { wp_die(__('You do not have sufficient permissions to access this page.', 'my-first-plugin')); } $name = sanitize_text_field($_POST['name']); // 处理表单数据,例如保存到数据库 wp_redirect(admin_url('options-general.php?page=my-first-plugin&message=success')); exit; } add_action('admin_post_my_first_plugin_form_action', 'my_first_plugin_form_handler');
32. 使用 AJAX
32.1 注册 AJAX 动作
function my_first_plugin_ajax_handler() { check_ajax_referer('my_first_plugin_nonce', 'security'); if (!current_user_can('manage_options')) { wp_send_json_error(__('You do not have sufficient permissions to access this page.', 'my-first-plugin')); } $response = array('message' => __('Hello, World!', 'my-first-plugin')); wp_send_json_success($response); } add_action('wp_ajax_my_first_plugin_action', 'my_first_plugin_ajax_handler');
32.2 在前端使用 AJAX
在前端脚本中使用 AJAX 进行请求:
jQuery(document).ready(function($) { $('#my-button').on('click', function() { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'my_first_plugin_action', security: my_first_plugin_vars.nonce }, success: function(response) { if (response.success) { alert(response.data.message); } else { alert('Error: ' + response.data); } } }); }); });
在插件中注册和本地化脚本:
function my_first_plugin_enqueue_scripts() { wp_enqueue_script('my-first-plugin-script', plugins_url('js/my-first-plugin.js', __FILE__), array('jquery'), null, true); wp_localize_script('my-first-plugin-script', 'my_first_plugin_vars', array( 'nonce' => wp_create_nonce('my_first_plugin_nonce') )); } add_action('admin_enqueue_scripts', 'my_first_plugin_enqueue_scripts');
33. 创建自定义数据库表
33.1 激活时创建表
在插件激活时创建自定义数据库表:
function my_first_plugin_create_db_table() { global $wpdb; $table_name = $wpdb->prefix . 'my_first_plugin_table'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, name tinytext NOT NULL, value text NOT NULL, PRIMARY KEY (id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } register_activation_hook(__FILE__, 'my_first_plugin_create_db_table');
33.2 插入数据
在插件中插入数据到自定义表:
function my_first_plugin_insert_data($name, $value) { global $wpdb; $table_name = $wpdb->prefix . 'my_first_plugin_table'; $wpdb->insert( $table_name, array( 'name' => $name, 'value' => $value, ) ); }
33.3 查询数据
从自定义表中查询数据:
function my_first_plugin_get_data() { global $wpdb; $table_name = $wpdb->prefix . 'my_first_plugin_table'; $results = $wpdb->get_results("SELECT * FROM $table_name", OBJECT); return $results; }
34. 本地化
34.1 加载插件的翻译文件
在插件中加载翻译文件:
function my_first_plugin_load_textdomain() { load_plugin_textdomain('my-first-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages'); } add_action('plugins_loaded', 'my_first_plugin_load_textdomain');
34.2 创建翻译文件
使用 Poedit 或其他翻译工具创建
.po
和.mo
文件,并将它们放在languages
目录中。例如,创建my-first-plugin-zh_CN.po
和my-first-plugin-zh_CN.mo
文件。35. 安全性
35.1 数据验证和清理
在处理用户输入时,确保数据的验证和清理:
$name = sanitize_text_field($_POST['name']);
35.2 非可信数据的输出
在输出非可信数据时,使用适当的 WordPress 函数进行转义:
echo esc_html($name);
35.3 使用非ces
使用 Nonces 来验证请求的合法性:
if (!isset($_POST['my_first_plugin_nonce']) || !wp_verify_nonce($_POST['my_first_plugin_nonce'], 'my_first_plugin_form_nonce')) { wp_die(__('Nonce verification failed', 'my-first-plugin')); }
36. 优化性能
36.1 减少数据库查询
缓存频繁使用的数据以减少数据库查询次数:
$data = wp_cache_get('my_first_plugin_data'); if ($data === false) { $data = my_first_plugin_get_data(); wp_cache_set('my_first_plugin_data', $data); }
36.2 异步处理
使用异步方式处理耗时操作:
function my_first_plugin_enqueue_async_script($url) { if (strpos($url, '#async') === false) { return $url; } else if (is_admin()) { return str_replace('#async', '', $url); } else { return str_replace('#async', '', $url) . "' async='async"; } } add_filter('clean_url', 'my_first_plugin_enqueue_async_script', 11, 1);
37. 与第三方服务集成
37.1 使用 API 密钥
存储和使用 API 密钥:
$api_key = get_option('my_first_plugin_api_key'); $response = wp_remote_get("https://api.example.com/data?api_key={$api_key}");
37.2 处理 API 响应
处理第三方 API 的响应:
if (is_wp_error($response)) { $error_message = $response->get_error_message(); echo "Something went wrong: $error_message"; } else { $body = wp_remote_retrieve_body($response); $data = json_decode($body); }
38. 版本控制
38.1 使用 Git
使用 Git 进行版本控制是现代开发的最佳实践。以下是一些基本步骤来初始化和使用 Git:
- 初始化 Git 仓库:
git init
- 添加
.gitignore
文件: 创建一个.gitignore
文件,以忽略不需要版本控制的文件和目录。例如:/vendor/ /node_modules/ /wp-content/uploads/ .env
- 添加并提交文件:
git add . git commit -m "Initial commit"
- 推送到远程仓库:
git remote add origin <remote-repository-URL> git push -u origin master
38.2 使用 GitHub Actions 进行 CI/CD
GitHub Actions 可以帮助你自动化测试、构建和部署插件。
- 创建 GitHub Actions 工作流: 在你的仓库中创建
.github/workflows/main.yml
文件:name: CI on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ubuntu-lateststeps: - name: Checkout code uses: actions/checkout@v2 - name: Set up PHP uses: shivammathur/setup-php@v2 with: php-version: '7.4' - name: Install dependencies run: composer install - name: Run tests run: vendor/bin/phpunit</code></pre></li>
39. 钩子和过滤器
39.1 自定义钩子
创建自定义钩子,让其他开发者可以扩展你的插件功能:
do_action('my_first_plugin_custom_action', $arg1, $arg2);
使用自定义钩子:
add_action('my_first_plugin_custom_action', 'my_custom_function', 10, 2); function my_custom_function($arg1, $arg2) { // 处理自定义钩子 }
39.2 自定义过滤器
创建自定义过滤器以便于修改数据:
$value = apply_filters('my_first_plugin_custom_filter', $value);
使用自定义过滤器:
add_filter('my_first_plugin_custom_filter', 'my_custom_filter_function'); function my_custom_filter_function($value) { // 修改并返回数据 return $value . ' modified'; }
40. REST API
40.1 注册自定义 REST API 路由
使用 WordPress REST API 注册自定义路由:
function my_first_plugin_register_rest_route() { register_rest_route('my-first-plugin/v1', '/data', array( 'methods' => 'GET', 'callback' => 'my_first_plugin_rest_callback', )); } add_action('rest_api_init', 'my_first_plugin_register_rest_route');
40.2 处理 REST API 请求
处理 REST API 请求并返回响应:
function my_first_plugin_rest_callback($request) { $data = array( 'message' => __('Hello, World!', 'my-first-plugin'), ); return new WP_REST_Response($data, 200); }
41. Gutenberg 块
41.1 创建自定义 Gutenberg 块
继续创建和注册自定义 Gutenberg 块。
- 创建编辑器脚本(续): 在
src
目录中继续创建index.js
文件的内容:import { registerBlockType } from '@wordpress/blocks'; import { useBlockProps, RichText } from '@wordpress/block-editor'; registerBlockType('my-first-plugin/my-custom-block', { edit({ attributes, setAttributes }) { const blockProps = useBlockProps(); return ( <div {...blockProps}> <RichText tagName="p" onChange={(content) => setAttributes({ content })} value={attributes.content} placeholder="Write your content here..." /> </div> ); }, save({ attributes }) { const blockProps = useBlockProps.save(); return ( <div {...blockProps}> <RichText.Content tagName="p" value={attributes.content} /> </div> ); }, attributes: { content: { type: 'string', source: 'html', selector: 'p' } } });
- 编译脚本: 更新
package.json
以添加构建脚本:{ "scripts": { "build": "wp-scripts build", "start": "wp-scripts start" }, "devDependencies": { "@wordpress/scripts": "^23.0.0" } }
然后运行构建命令:npm run build
- 加载脚本: 在插件的 PHP 文件中加载编译后的 JavaScript 文件:
function my_first_plugin_register_block() { wp_register_script( 'my-first-plugin-editor-script', plugins_url('build/index.js', __FILE__), array('wp-blocks', 'wp-element', 'wp-editor') );register_block_type('my-first-plugin/my-custom-block', array( 'editor_script' => 'my-first-plugin-editor-script', ));} add_action('init', 'my_first_plugin_register_block');
42. 使用 WP-CLI
WP-CLI 是一个强大的命令行工具,可以用于管理 WordPress 安装,包括插件的开发和调试。
42.1 创建自定义 WP-CLI 命令
- 注册自定义命令: 在插件的主文件中注册一个自定义 WP-CLI 命令:
if (defined('WP_CLI') && WP_CLI) { WP_CLI::add_command('my_first_plugin', 'My_First_Plugin_CLI_Command'); } class My_First_Plugin_CLI_Command { public function hello($args, $assoc_args) { WP_CLI::success('Hello, World!'); } }
- 运行命令: 在终端中运行自定义 WP-CLI 命令:
wp my_first_plugin hello
43. 单元测试
43.1 使用 PHPUnit 进行单元测试
- 安装 PHPUnit: 使用 Composer 安装 PHPUnit:
composer require --dev phpunit/phpunit
- 设置测试环境: 创建
phpunit.xml.dist
文件以配置测试环境:<phpunit bootstrap="tests/bootstrap.php"> <testsuites> <testsuite name="My First Plugin Test Suite"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
- 编写测试: 在
tests
目录中创建测试文件,例如test-sample.php
:class SampleTest extends WP_UnitTestCase { public function test_sample() { $this->assertTrue(true); } }
- 运行测试: 在终端中运行 PHPUnit:
vendor/bin/phpunit
44. 代码质量和静态分析
44.1 使用 PHPCS 检查代码规范
- 配置 PHPCS: 创建
phpcs.xml.dist
文件,以定义代码检查规则:<?xml version="1.0"?> <ruleset name="My First Plugin"> <description>WordPress Plugin Coding Standards</description> <rule ref="WordPress-Core"/> <rule ref="WordPress-Docs"/> <rule ref="WordPress-Extra"/> <file>./</file> <exclude-pattern>*/vendor/*</exclude-pattern> </ruleset>
- 运行 PHPCS: 在终端中运行 PHP_CodeSniffer 以检查代码规范:
vendor/bin/phpcs
44.2 使用 PHPStan 进行静态分析
- 安装 PHPStan: 使用 Composer 安装 PHPStan:
composer require --dev phpstan/phpstan
- 配置 PHPStan: 创建
phpstan.neon
文件:includes: - vendor/phpstan/phpstan/conf/bleedingEdge.neon parameters: level: max paths: - %currentWorkingDirectory%/src excludePaths: - %currentWorkingDirectory%/vendor
- 运行 PHPStan: 在终端中运行 PHPStan 以进行静态分析:
vendor/bin/phpstan analyse
45. 安全性
45.1 数据验证和清理
为了防止安全漏洞,必须对所有用户输入进行验证和清理。
- 验证输入: 使用
sanitize_*
系列函数来清理输入数据:$email = sanitize_email($_POST['email']); $url = esc_url($_POST['url']);
- 非ces验证: 使用
wp_verify_nonce
和check_admin_referer
来验证非ces:if (!wp_verify_nonce($_POST['_wpnonce'], 'my_action')) { die('Security check failed'); }
45.2 SQL 注入防护
使用
prepare
方法来防止 SQL 注入:global $wpdb; $wpdb->query($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_title = %s", $title));
46. 国际化和本地化
46.1 准备插件进行翻译
- 加载文本域: 在插件主文件中加载文本域:
function my_first_plugin_load_textdomain() { load_plugin_textdomain('my-first-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages'); } add_action('plugins_loaded', 'my_first_plugin_load_textdomain');
- 使用翻译函数: 使用
__
,_e
,_n
等函数进行国际化:$message = __('Hello, World!', 'my-first-plugin'); _e('Welcome to my plugin!', 'my-first-plugin');
- 生成 POT 文件: 使用
wp i18n
工具来生成 POT 文件:npx wp i18n make-pot . languages/my-first-plugin.pot
47. 优化性能
47.1 缓存
- 使用 Transients API: 缓存临时数据,以减少数据库查询:
$data = get_transient('my_first_plugin_data'); if ($data === false) { $data = costly_database_query(); set_transient('my_first_plugin_data', $data, 12 * HOUR_IN_SECONDS); }
- 使用对象缓存: 使用
wp_cache_set
和wp_cache_get
进行对象缓存:wp_cache_set('my_cache_key', $data); $cached_data = wp_cache_get('my_cache_key');
47.2 优化数据库查询
确保数据库查询高效,避免不必要的查询和数据加载:
global $wpdb; $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_status = %s", 'publish'));
48. 高级插件架构
48.1 使用面向对象编程(OOP)
通过面向对象编程(OOP),可以使插件的代码更加模块化、可维护和可扩展。
- 创建基础类: 创建一个基础类来管理插件的初始化和加载:
class MyFirstPlugin { protected static $instance = null;public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } private function __construct() { $this->setup_hooks(); } private function setup_hooks() { add_action('init', array($this, 'init')); } public function init() { // 初始化代码 }} MyFirstPlugin::get_instance();
- 模块化插件功能: 将插件的不同功能模块化,以便更好地管理和扩展:
class MyFirstPlugin_Admin { public function __construct() { add_action('admin_menu', array($this, 'add_admin_menu')); }public function add_admin_menu() { add_menu_page('My First Plugin', 'My First Plugin', 'manage_options', 'my-first-plugin', array($this, 'admin_page')); } public function admin_page() { echo '<h1>My First Plugin Settings</h1>'; }} if (is_admin()) { new MyFirstPlugin_Admin(); }
48.2 使用依赖注入(DI)
依赖注入(DI)是一种设计模式,它可以使类的依赖更显式,并且更容易进行单元测试。
- 创建依赖注入容器: 使用一个简单的 DI 容器来管理类的实例:
class DIContainer { protected $instances = array();public function set($name, $instance) { $this->instances[$name] = $instance; } public function get($name) { return $this->instances[$name]; }} $container = new DIContainer();
- 使用 DI 容器: 注册和使用依赖:
$container->set('admin', new MyFirstPlugin_Admin()); $admin = $container->get('admin');
49. REST API
49.1 创建自定义 REST API 端点
WordPress 提供了一个内置的 REST API,可以用来创建自定义端点。
- 注册自定义端点: 使用
register_rest_route
函数注册一个自定义 REST API 端点:function my_first_plugin_register_api_routes() { register_rest_route('my-first-plugin/v1', '/data', array( 'methods' => 'GET', 'callback' => 'my_first_plugin_get_data', )); } add_action('rest_api_init', 'my_first_plugin_register_api_routes'); function my_first_plugin_get_data($request) { return new WP_REST_Response(array('data' => 'Hello, World!'), 200); }
- 测试端点: 访问
http://your-site/wp-json/my-first-plugin/v1/data
以测试自定义端点。
50. 多站点支持
50.1 多站点激活和停用
```php foreach ($blog_ids as $blog_id) { switch_to_blog($blog_id); my_first_plugin_single_deactivate(); restore_current_blog(); } } else { my_first_plugin_single_deactivate(); } } register_deactivation_hook(__FILE__, 'my_first_plugin_deactivate'); ```
50.2 处理多站点特定功能
- 存储全局设置: 在多站点环境中,可以使用
get_site_option
和update_site_option
来存储和检索全局设置:function my_first_plugin_get_global_setting($key) { return get_site_option($key); } function my_first_plugin_set_global_setting($key, $value) { update_site_option($key, $value); }
- 跨站点数据同步: 在多站点网络中,可以通过
switch_to_blog
和restore_current_blog
函数在不同站点之间切换,以同步数据:function my_first_plugin_sync_data_across_sites($data) { global $wpdb; $blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach ($blog_ids as $blog_id) { switch_to_blog($blog_id); update_option('my_first_plugin_data', $data); restore_current_blog(); } }
51. 单元测试
51.1 使用 PHPUnit 进行测试
- 安装 PHPUnit: 使用 Composer 安装 PHPUnit:
composer require --dev phpunit/phpunit
- 配置 PHPUnit: 创建
phpunit.xml.dist
文件:<phpunit bootstrap="tests/bootstrap.php"> <testsuites> <testsuite name="My Plugin Test Suite"> <directory>tests/</directory> </testsuite> </testsuites> </phpunit>
- 编写测试用例: 创建测试用例文件,例如
tests/test-sample.php
:class SampleTest extends WP_UnitTestCase { public function test_sample() { $this->assertTrue(true); } }
- 运行测试: 运行 PHPUnit 以执行测试:
vendor/bin/phpunit
52. 钩子和过滤器
52.1 创建自定义钩子
- 创建自定义动作钩子: 在插件代码中创建一个自定义动作钩子:
do_action('my_first_plugin_custom_action', $arg1, $arg2);
- 创建自定义过滤器钩子: 在插件代码中创建一个自定义过滤器钩子:
$value = apply_filters('my_first_plugin_custom_filter', $value, $arg1, $arg2);
52.2 使用钩子和过滤器
- 添加动作钩子: 使用
add_action
函数来挂载自定义动作钩子:add_action('my_first_plugin_custom_action', 'my_custom_action_function', 10, 2); function my_custom_action_function($arg1, $arg2) { // 自定义动作处理逻辑 }
- 添加过滤器钩子: 使用
add_filter
函数来挂载自定义过滤器钩子:add_filter('my_first_plugin_custom_filter', 'my_custom_filter_function', 10, 3); function my_custom_filter_function($value, $arg1, $arg2) { // 自定义过滤逻辑 return $value; }
-
无用户名登录的 WebAuthn 实现
无用户名登录是一种通过 WebAuthn 进行身份验证的改进方法,其目标是进一步简化用户的登录过程,消除输入用户名的需求。这一特性主要依赖于 Resident Key(驻留密钥)功能,允许认证器在本地存储私钥,从而实现用户身份的无缝识别。下面是这一过程的详细解释:
为什么普通的 WebAuthn 不能实现无用户名登录?
在传统的 WebAuthn 流程中,依赖方(如网站)在验证用户身份时需要提供凭证 ID(Credential ID)给认证器,认证器依赖该凭证 ID 计算出相应的私钥。通常,认证器并不存储私钥,而是通过 Key Warp 等技术加密私钥并将其包含在凭证 ID 中。这意味着认证器可以无限制地生成公私钥对,而无需维护庞大的存储空间。
然而,这也导致了一个问题:在登录时,依赖方必须通过用户名找到对应的凭证 ID,并将其发送给认证器。这就要求用户在验证时必须输入用户名。
Resident Key(驻留密钥)解决方案
Resident Key 功能允许认证器在本地永久存储私钥,从而消除对凭证 ID 的依赖。通过这种方式,认证器可以直接根据依赖方 ID 找到对应的私钥进行身份验证,无需用户输入用户名。
无用户名登录的具体流程
注册时(启用 Resident Key):
- 依赖方请求新建凭证:
- 依赖方请求认证器生成一对公私钥,并要求启用 Resident Key。
- 认证器生成密钥对:
- 认证器生成一对公私钥,并将私钥存储在永久内存中,与依赖方 ID 和用户 ID 绑定。
- 发送公钥给依赖方:
- 认证器将公钥发送给依赖方,依赖方将公钥与用户 ID 绑定并存储。
验证时(无用户名登录):
- 依赖方请求验证:
- 依赖方请求验证用户身份,只需提供依赖方 ID。
- 用户选择认证器:
- 用户选择用于验证的认证器。
- 认证器查找私钥:
- 认证器根据依赖方 ID 查找对应的私钥。如果有多个对应的私钥,认证器会提示用户选择使用哪个身份信息进行登录。
- 认证器签名挑战:
- 认证器使用找到的私钥签名依赖方发送的挑战(challenge),并将签名结果和用户 ID 返回给依赖方。
- 依赖方验证签名:
- 依赖方根据返回的用户 ID 查找对应的公钥,验证签名的正确性。如果签名有效,则允许用户登录。
无用户名登录的示意图
依赖方 客户端 认证器 |-- 验证请求(依赖方 ID) -->| | | |-- 用户选择认证器 ------>| | | |-- 查找私钥 | | |-- 签名挑战 | |<-- 返回签名和用户 ID --| |<-- 验证签名(公钥验证) --| | |-- 登录成功(如验证通过) -->| |
重要注意事项
- 存储限制:认证器能永久存储的私钥数量是有限的,因此 Resident Key 功能应仅在真正需要无用户名登录时启用。
- 兼容性:目前尚无统一的方法检测认证器是否支持 Resident Key 功能,因此在无用户名验证失败时,应回退至常规的 WebAuthn 验证流程,即向用户询问用户名。
- 安全性:驻留密钥功能要求认证器支持用户身份的安全管理,包括对用户 ID 的安全存储和私钥的安全签名操作。
结论
Resident Key 功能为 WebAuthn 提供了无用户名登录的可能性,进一步简化了用户的登录体验。在未来,随着更多认证器对 Resident Key 的支持及其在实际应用中的普及,无用户名登录有望成为一种常见的身份验证方法。然而,在实施这一特性时,需要注意认证器的存储限制和兼容性问题,以确保用户体验的平滑过渡。
- 依赖方请求新建凭证:
-
CBOR (Concise Binary Object Representation)
CBOR,全称是简明二进制对象表示(Concise Binary Object Representation),是一种编码方式,常用于物联网(IoT)领域。它的设计目标是提供一种体积更小、更高效的二进制格式,类似于 JSON,但更适合资源受限的环境,如物联网设备。
CBOR 的特点
- 紧凑性:CBOR 的编码格式比 JSON 更紧凑,减少了数据传输的体积和存储空间。
- 高效性:由于其二进制格式,解析和生成 CBOR 数据通常比处理文本格式的 JSON 更高效。
- 自描述性:CBOR 编码的数据包含类型信息,解析时无需额外的模式(schema)。
- 广泛支持:大部分编程语言都有相应的 CBOR 编码和解码库,可以方便地处理 CBOR 数据。
CBOR 与 JSON 的比较
特性 CBOR JSON 格式 二进制 文本 数据体积 较小 较大 解析效率 较高 较低 自描述性 是 是 适用场景 物联网、嵌入式系统、网络协议等 Web 服务、配置文件等 示例
以下是一个简单的 JSON 对象及其对应的 CBOR 编码表示:
JSON 示例:
{ "name": "Alice", "age": 30, "is_student": false }
CBOR 编码表示:
A3 # map(3) 64 # text(4) 6E616D65 # "name" 65 # text(5) 416C696365 # "Alice" 63 # text(3) 616765 # "age" 18 1E # unsigned(30) 6A # text(10) 69735F73747564656E74 # "is_student" F4 # false
CBOR 库
大部分编程语言都有相应的 CBOR 库,可以方便地进行编码和解码操作。以下是一些常见语言的 CBOR 库:
- Python:
cbor2
,cbor
- JavaScript:
cbor
- Go:
github.com/fxamacker/cbor
- Java:
com.fasterxml.jackson.dataformat.cbor
- C/C++:
libcbor
使用示例
Python 示例:
import cbor2 # 编码 JSON 对象为 CBOR data = { "name": "Alice", "age": 30, "is_student": False } encoded = cbor2.dumps(data) print(encoded) # 输出 CBOR 二进制数据 # 解码 CBOR 为 JSON 对象 decoded = cbor2.loads(encoded) print(decoded) # 输出 {'name': 'Alice', 'age': 30, 'is_student': False}
结论
CBOR 提供了一种紧凑、高效的二进制编码格式,适用于资源受限的环境,如物联网设备。通过使用现有的 CBOR 库,可以轻松地在各种编程语言中进行 CBOR 数据的编码和解码操作,在提高数据传输效率的同时,保持了对各种复杂数据结构的支持。
-
Server-Sent Events(服务器发送事件)和WebSocket(网络套接字)
Server-Sent Events(服务器发送事件)和WebSocket(网络套接字)是两种常用的实时通信协议,用于在应用程序中进行快速高效的数据传输。它们在实时体验方面的期望随着时间的推移不断增长,随着技术的改进和对可能性的理解而不断提高。本文将比较这两种流行的实时协议——WebSockets和Server-Sent Event(SSE)API。您将了解到它们各自的功能、优缺点以及何时使用它们。
WebSockets是建立在设备的TCP/IP协议栈之上的一种轻量级传输层,提供了服务器和浏览器之间全双工、低延迟、事件驱动的连接。这对于实时应用程序非常理想,因为在初始的HTTP握手之后,单个WebSocket连接可以处理一个会话的所有消息,无需进一步的握手。当会话结束时,连接应该作为清理的一部分关闭。[2]
WebSockets的优点包括:
- 使用自定义的ws协议传输消息,工作在比HTTP更低的层级上。
- 连接是双向的,因此WebSockets非常适用于需要从服务器读取和写入数据的应用程序,例如聊天应用程序或多人游戏。
- 它可以复杂地从头开始实现WebSocket,但有许多库可用于简化此过程。
- 基于事件的,无需轮询即可获取消息,有助于减少延迟。
- RFC 6455 – WebSocket协议于2011年发布在IETF网站上,现在所有主流浏览器都支持它。[2]
WebSockets的缺点包括:
- 防火墙阻塞:一些企业防火墙在处理WebSockets时可能会出现问题(特别是SophosXG防火墙、WatchGuard和McAfee Web Gateway)。
- 没有内置的重新连接支持:当WebSocket连接关闭时(例如由于网络问题),客户端不会尝试重新连接到服务器,这意味着您需要编写额外的代码来轮询服务器,在可用时重新建立连接。或者,您可以使用Server-Sent Events或具有重新连接支持的库,如Socket.IO。[2]
Server-Sent Events(SSE)基于Server-Sent DOM Events(服务器发送的DOM事件)。浏览器可以使用EventSource接口订阅服务器生成的事件流,每当发生新事件时,就会接收到更新。EventSource接受来自特定URL的HTTP事件流连接,并在检索到可用数据时保持连接打开。Server-Sent Events是一种标准,描述了服务器在建立初始客户端连接后如何保持数据传输到客户端。它提供了一种内存高效的XHR流实现。与原始的XHR连接不同,原始XHR连接在连接断开之前会缓冲整个接收到的响应,而SSE连接可以在不累积所有消息的情况下丢弃已处理的消息。[3]
Server-Sent Events的优点包括:
- 可以在不支持它的浏览器中使用JavaScript进行polyfill。这对于向后兼容性非常有用,因为您可以依赖现有的实现而不必编写替代实现。
- 内置的重新连接支持:Server-Sent Event连接在连接丢失后会重新建立连接,这意味着需要编写的代码更少,以实现基本的行为。
- 不会被防火墙阻塞:SSE在进行数据包检查的企业防火墙中没有问题,这对于在企业环境中支持应用程序非常重要。
Server-Sent Events(SSE)是一种基于服务器发送的DOM事件的协议。浏览器可以通过EventSource接口订阅由服务器生成的事件流,每当发生新事件时,浏览器就会接收到更新。SSE使用XHR流传输消息,连接是单向的,适用于只需要从服务器读取数据的应用程序,例如实时股票或新闻滚动。
WebSocket是建立在设备的TCP/IP协议栈之上的一种轻量级传输层,提供了全双工、低延迟、事件驱动的服务器与浏览器之间的连接。WebSocket适用于需要从服务器读取和写入数据的应用程序,例如聊天应用程序或多人游戏。
下面是Server-Sent Events和WebSocket的一些比较:
Server-Sent Events:
- 使用XHR流传输消息,连接是单向的。
- 不需要复杂的实现,但相关的库较少。
- 事件驱动,无需轮询即可拦截消息。
- 支持自动重新连接,当连接丢失时会重新建立连接。
- 可以通过JavaScript进行polyfill,以支持不支持SSE的浏览器。
- 不受企业防火墙的阻塞,适用于企业环境。
WebSocket:
- 使用自定义的ws协议传输消息,工作在比HTTP更低的层级。
- 连接是双向的,适用于需要从服务器读取和写入数据的应用程序。
- 实现WebSocket可能较为复杂,但有许多库可用于简化实现。
- 事件驱动,无需轮询即可拦截消息。
- 不受企业防火墙的阻塞,但某些企业防火墙可能无法处理WebSocket。
- 没有内置的重新连接支持,需要额外的代码来重新建立连接。
根据具体的应用场景和需求,可以选择使用Server-Sent Events或WebSocket。如果只需要从服务器读取数据,并且需要自动重新连接的功能,可以选择Server-Sent Events。如果需要双向通信,并且需要更高级的功能和更大的灵活性,可以选择WebSocket。
Learn more:
-
在WordPress网站中启用Passkey认证
要在WordPress网站中启用Passkey认证,你可以按照以下步骤操作:
- 安装插件:首先,你需要安装一个支持WebAuthn和Passkey认证的WordPress插件。
WP-WebAuthn
是一个可以在WordPress站点上启用WebAuthn的插件。 - 配置插件:安装插件后,你需要进入WordPress管理后台的“设置”部分,找到
WP-WebAuthn
插件的设置页面,并按照插件的文档说明进行配置。 - 启用HTTPS:由于WebAuthn和Passkey认证需要安全的连接,确保你的WordPress网站使用HTTPS连接。如果尚未启用,请配置SSL证书以启用HTTPS。
- 用户注册身份验证器:用户需要在他们的个人资料页面注册身份验证器。这可能涉及到使用生物识别技术,如指纹或面部识别,或者是通过USB设备、NFC等方式。
- 测试认证流程:配置完成后,尝试注册和使用Passkey进行登录,以确保一切工作正常。
- 用户教育:由于Passkey是一种较新的技术,你可能需要向你的用户解释如何使用它,以及它如何增强账户的安全性。
- 技术支持:如果你遇到任何问题,可以查看插件的文档,或者在插件的GitHub页面上寻求帮助。
- 保持更新:定期检查插件更新,以确保你的Passkey认证功能保持最新和安全。
请注意,启用Passkey认证可能需要一定的技术知识,特别是关于服务器配置和插件安装的部分。如果你不熟悉这些操作,可能需要寻求专业的技术支持。
WP-WebAuthn 是一个为你的 WordPress 站点启用 WebAuthn 的插件。只需下载并安装,你就进入了 Web 身份验证的未来。
WP-WebAuthn 也支持无用户名验证。
这个插件有 4 个内置短代码和 4 个对应的内置 Gutenberg 区块,因此你可以将注册表单等组件添加到前端页面。
WebAuthn(Web Authentication)是一个开放的无密码标准,由万维网联盟(W3C)和FIDO联盟共同制定,旨在为用户提供一种更安全、更便捷的网络认证方式。WebAuthn 允许用户使用生物识别技术、智能卡或移动设备等作为认证手段,而不是仅仅依赖传统的用户名和密码。
截至2023年,WebAuthn 规范的主要版本是 Level 1,即 WebAuthn Level 1,它在2019年3月成为W3C的官方推荐标准。WebAuthn Level 1 定义了客户端和服务器之间进行无密码认证的流程,包括注册(注册新的认证器)和认证(使用已有的认证器进行认证)两个主要过程。
关于 WebAuthn Level 2 的规范,截至2023年,并没有官方的 Level 2 版本发布。通常,技术规范的更新会通过补丁或小版本迭代来进行,而不是直接跳到 Level 2。WebAuthn 的发展和更新可能会通过一系列的改进提案(如 RFCs)来进行,这些提案会逐步集成到核心规范中。
为了获取最新的WebAuthn规范信息,建议访问W3C官方网站或FIDO联盟的相关资源,查阅最新的文档和公告。
走进 WebAuthn 的世界:无密码时代的身份认证
随着互联网的发展,传统密码认证方式的安全性越来越让人担忧。幸运的是,WebAuthn 技术的出现让我们看到了希望。WebAuthn 是一种新的身份认证方式,它让我们可以抛弃繁琐的密码,通过更加安全和便捷的方式来进行身份验证。在本文中,我们将一起走进 WebAuthn 的世界,了解它的角色、过程和关键概念。
WebAuthn 认证的四大角色
在 WebAuthn 的认证流程中,有四个重要的角色:
- 依赖方 (Relying Party, RP):
- 这是提供服务的那一方,比如一个网站。
- 依赖方负责提供注册和登录的接口。
- 用户 (User):
- 也就是你,准备登录网站的那个人。
- 用户是整个流程的主体,通过生物识别或其他方式进行身份验证。
- 认证器 (Authenticator):
- 认证器可以是 USB Key、设备内置的指纹扫描器、虹膜扫描器、面部识别装置等。
- 认证器在使用过程中替代了传统的密码。
- 用户代理 (User Agent):
- 通常是浏览器或操作系统。
- 用户代理负责与认证器交互,帮助完成认证流程。
认证过程:注册和验证
WebAuthn 的认证过程主要分为两个阶段:
- 注册仪式 (Registration Ceremony):
- 这是用户将认证器添加到他们的账户中的过程。
- 用户在此过程中注册他们的认证器,使其可以在未来进行身份验证。
- 验证仪式 (Authentication Ceremony):
- 用户通过已注册的认证器进行身份验证。
- 这是用户登录时的过程,通过之前注册的认证器来验证身份。
认证过程中的关键内容
在 WebAuthn 的认证过程中,有一些重要的内容需要理解:
- 挑战 (Challenge):
- 通常是一串随机字符串。
- 由依赖方生成并发送给用户,以防止重放攻击。
- 公钥凭证 (Public Key Credential):
- 由认证器生成的凭证,技术上代替了密码。
- 包含公钥和其他必要的信息,用于验证用户身份。
- 证明 (Attestation):
- 注册时认证器产生的验证数据。
- 用于证明认证器的可靠性和安全性。
- 断言 (Assertion):
- 验证时认证器产生的验证数据。
- 用于证明用户是注册时的同一个用户。
区分证明和断言
在 WebAuthn 中,“证明 (Attestation)”和“断言 (Assertion)”是两个容易混淆的概念,但它们有着不同的用途:
- 证明 (Attestation):在注册过程中产生,用于证明认证器的安全性和真实性。
- 断言 (Assertion):在登录过程中产生,用于证明用户的身份。
总结
WebAuthn 技术为我们带来了更加安全、便捷的身份认证方式。通过了解依赖方、用户、认证器和用户代理这四大角色,以及注册和验证两个过程中的关键内容,我们可以更好地理解和应用 WebAuthn。随着这种无密码身份认证方式的普及,我们的网络生活将变得更加安全和舒适。
让我们一起迎接无密码时代的到来,享受更加安全和便捷的网络体验吧!
非对称加密与 WebAuthn 的安全性
在数字时代,密码曾经是我们保护在线身份和隐私的主要工具。然而,随着技术的进步和黑客手段的多样化,传统的密码认证方式变得越来越不可靠。WebAuthn 的出现让我们看到了新的希望,它是一种基于非对称加密的身份认证技术,旨在提供更加安全和便捷的认证体验。
非对称加密的基础
要理解 WebAuthn 的安全性,我们首先需要了解非对称加密的基本概念。非对称加密使用一对密钥,即公钥和私钥。这对密钥有以下特点:
- 公钥 (Public Key):可以公开给任何人,用于加密信息。
- 私钥 (Private Key):必须严格保密,用于解密信息。
公钥和私钥是互相关联但几乎无法互相推导的。在这种加密体系中,使用私钥加密的信息只能由对应的公钥解密,反之亦然。
对称加密的局限性
为了更好地理解非对称加密的优势,我们先来看一个对称加密的例子:
假设小明和小红互相写信,但见不到对方。小红想确认给自己写信的人真的是小明,于是他们商量出一套方案:
- 小明和小红在身份验证的情况下商量一个统一的密码和密钥。
- 一段时间后,小红要求小明验证身份时,发送一段文本给小明。
- 小明用商量好的密码和密钥加密文本后发回给小红。
- 小红使用相同的密码和密钥解密文本,如果得到的文本和之前发出的一致,就能确定对方是小明。
这种方法的最大问题在于密钥的交换。如果两人在开始时不能见面,那么他们必须通过某种方式以明文交换密码,一旦密码在传输过程中被窃取,这个认证方法就失效了。
非对称加密解决信任问题
非对称加密完美地解决了密钥泄露的问题。我们再来看小明和小红的例子,这次他们使用非对称加密:
- 在小明的身份已经验证的情况下,小明生成一对公私钥,将公钥发送给小红,私钥自己保管,同时商量好统一的密码。
- 一段时间后,小红要求小明验证身份时,发送一段文本给小明。
- 小明使用商量好的密码和自己的私钥加密文本,发送给小红。
- 小红使用相同的密码和小明的公钥解密文本,如果得到的文本和之前发出的一致,就能确定对方是小明。
在这个过程中,私钥从未离开过小明的手中,也没有经过传输,几乎没有泄露的风险。小红可以通过解密文本确认对方的身份,而无需担心密钥被窃取。
WebAuthn 的安全性
WebAuthn 采用了非对称加密的基本原理来确保用户身份的安全性。具体而言,WebAuthn 的认证过程包括以下步骤:
- 注册 (Registration):
- 用户在依赖方(如网站)注册时,认证器生成一对公私钥,并将公钥发送给依赖方。
- 私钥保存在认证器中,从不离开设备。
- 验证 (Authentication):
- 用户登录时,依赖方生成一个随机的挑战 (Challenge) 并发送给用户。
- 认证器使用私钥对挑战进行签名,并将签名发送回依赖方。
- 依赖方使用事先保存的公钥验证签名,如果验证通过,则表明用户的身份是合法的。
通过这种方式,WebAuthn 确保了认证过程的安全性,因为私钥永远不会被传输或暴露,即使网络通信被截获,攻击者也无法伪造用户的身份。
结论
WebAuthn 的核心在于利用非对称加密技术,通过公钥和私钥的配合,确保认证器生成的凭证是用户的认证器,而非第三方伪造的。这种机制大大提高了身份验证的安全性,解决了传统密码认证方式的诸多问题,让我们的数字生活更加安全和便捷。通过 WebAuthn,我们正迈向一个无密码的安全新时代。
非对称加密与 WebAuthn 的安全性
在数字时代,密码曾经是我们保护在线身份和隐私的主要工具。然而,随着技术的进步和黑客手段的多样化,传统的密码认证方式变得越来越不可靠。WebAuthn 的出现让我们看到了新的希望,它是一种基于非对称加密的身份认证技术,旨在提供更加安全和便捷的认证体验。
非对称加密的基础
要理解 WebAuthn 的安全性,我们首先需要了解非对称加密的基本概念。非对称加密使用一对密钥,即公钥和私钥。这对密钥有以下特点:
- 公钥 (Public Key):可以公开给任何人,用于加密信息。
- 私钥 (Private Key):必须严格保密,用于解密信息。
公钥和私钥是互相关联但几乎无法互相推导的。在这种加密体系中,使用私钥加密的信息只能由对应的公钥解密,反之亦然。
对称加密的局限性
为了更好地理解非对称加密的优势,我们先来看一个对称加密的例子:
假设小明和小红互相写信,但见不到对方。小红想确认给自己写信的人真的是小明,于是他们商量出一套方案:
- 小明和小红在身份验证的情况下商量一个统一的密码和密钥。
- 一段时间后,小红要求小明验证身份时,发送一段文本给小明。
- 小明用商量好的密码和密钥加密文本后发回给小红。
- 小红使用相同的密码和密钥解密文本,如果得到的文本和之前发出的一致,就能确定对方是小明。
这种方法的最大问题在于密钥的交换。如果两人在开始时不能见面,那么他们必须通过某种方式以明文交换密码,一旦密码在传输过程中被窃取,这个认证方法就失效了。
非对称加密解决信任问题
非对称加密完美地解决了密钥泄露的问题。我们再来看小明和小红的例子,这次他们使用非对称加密:
- 在小明的身份已经验证的情况下,小明生成一对公私钥,将公钥发送给小红,私钥自己保管,同时商量好统一的密码。
- 一段时间后,小红要求小明验证身份时,发送一段文本给小明。
- 小明使用商量好的密码和自己的私钥加密文本,发送给小红。
- 小红使用相同的密码和小明的公钥解密文本,如果得到的文本和之前发出的一致,就能确定对方是小明。
在这个过程中,私钥从未离开过小明的手中,也没有经过传输,几乎没有泄露的风险。小红可以通过解密文本确认对方的身份,而无需担心密钥被窃取。
WebAuthn 的安全性
WebAuthn 采用了非对称加密的基本原理来确保用户身份的安全性。具体而言,WebAuthn 的认证过程包括以下步骤:
- 注册 (Registration):
- 用户在依赖方(如网站)注册时,认证器生成一对公私钥,并将公钥发送给依赖方。
- 私钥保存在认证器中,从不离开设备。
- 验证 (Authentication):
- 用户登录时,依赖方生成一个随机的挑战 (Challenge) 并发送给用户。
- 认证器使用私钥对挑战进行签名,并将签名发送回依赖方。
- 依赖方使用事先保存的公钥验证签名,如果验证通过,则表明用户的身份是合法的。
通过这种方式,WebAuthn 确保了认证过程的安全性,因为私钥永远不会被传输或暴露,即使网络通信被截获,攻击者也无法伪造用户的身份。
结论
WebAuthn 的核心在于利用非对称加密技术,通过公钥和私钥的配合,确保认证器生成的凭证是用户的认证器,而非第三方伪造的。这种机制大大提高了身份验证的安全性,解决了传统密码认证方式的诸多问题,让我们的数字生活更加安全和便捷。通过 WebAuthn,我们正迈向一个无密码的安全新时代。
WebAuthn 注册流程
WebAuthn 的注册流程旨在建立用户、认证器和依赖方(如网站)之间的信任关系。通过这个流程,认证器生成公私钥对,公钥被传递给依赖方以便后续认证使用。以下是详细的注册流程:
- 用户发起注册请求:
- 用户在依赖方的注册页面上发起注册请求。依赖方会生成一个注册挑战(challenge),并返回给用户的客户端(通常是浏览器)。
- 客户端与认证器交互:
- 客户端将注册挑战发送给用户的认证器(如安全密钥、手机的生物识别系统等)。
- 认证器生成密钥对:
- 认证器生成一对公私钥。私钥保存在认证器中,永不离开设备。
- 同时,认证器生成一个包含公钥和其他元数据(如认证器的标识、使用的加密算法等)的凭证。
- 用户验证:
- 认证器要求用户进行验证(如指纹扫描、面部识别或输入 PIN 码),以确保是合法用户在进行注册操作。
- 返回注册响应:
- 认证器将公钥和其他相关数据(如认证器 ID、签名等)封装在注册响应中,并返回给客户端。
- 客户端发送注册响应给依赖方:
- 客户端将注册响应发送回依赖方服务器。
- 依赖方验证并存储公钥:
- 依赖方验证注册响应(例如,检查认证器签名是否有效)。
- 验证通过后,依赖方会将公钥和其他相关信息与用户账户关联存储起来,以便后续的验证使用。
WebAuthn 注册流程示意图
用户 依赖方 客户端 认证器 |-- 注册请求 -->| | | | |-- 注册挑战 ------->| | | | |-- 注册挑战发送给认证器 -->| | | | |-- 生成密钥对 | | | |-- 用户验证 | | |<-- 返回注册响应 ---------| | |<-- 注册响应 -------| | | |-- 验证并存储公钥 -->| |
具体细节
- 注册挑战(challenge):这是一个随机生成的字符串,用于防止重放攻击。每次注册请求都会生成一个新的挑战。
- 元数据(metadata):包括认证器的标识、支持的加密算法等。这些信息帮助依赖方了解如何验证响应。
- 签名(signature):认证器用私钥对注册响应中的数据进行签名,依赖方用公钥验证签名的有效性。
结论
WebAuthn 的注册流程通过非对称加密技术,确保了在注册过程中公私钥的安全生成和存储,防止密钥泄露或被篡改。同时,通过挑战-应答模型和用户验证步骤,WebAuthn 提供了强大的安全保障,为后续的身份验证打下了坚实的基础。接下来,我们可以详细探讨 WebAuthn 的验证流程。
WebAuthn 验证流程
WebAuthn 验证流程旨在确保用户的身份真实性,通过基于挑战-应答的模型,使用之前注册时存储的公钥验证用户。以下是详细的验证流程:
- 用户发起验证请求:
- 用户在依赖方(如网站)上发起登录请求。依赖方生成一个验证挑战(challenge),并返回给用户的客户端(通常是浏览器)。
- 依赖方发送挑战:
- 依赖方将挑战发送给用户的客户端。
- 客户端与认证器交互:
- 客户端将验证挑战、依赖方信息(如 Relying Party ID)和客户端信息(如 origin)发送给用户的认证器。
- 认证器请求用户动作:
- 认证器提示用户进行验证(如指纹扫描、面部识别或输入 PIN 码)。
- 用户验证通过后,认证器找到对应的私钥,并使用私钥对挑战进行签名,生成一个签名断言(assertion)。
- 认证器返回签名断言:
- 认证器将签名断言连同认证器的元数据(如认证器 ID、计数器值等)返回给客户端。
- 客户端发送签名断言给依赖方:
- 客户端将签名断言发送回依赖方服务器。
- 依赖方验证签名断言:
- 依赖方使用之前存储的公钥,验证签名断言的有效性。
- 依赖方检查计数器值是否合理,以防止重放攻击。
- 如果签名有效且计数器值合理,依赖方确认用户身份验证通过。
WebAuthn 验证流程示意图
用户 依赖方 客户端 认证器 |-- 验证请求 -->| | | | |-- 验证挑战 ------->| | | | |-- 验证挑战发送给认证器 -->| | | | |-- 用户验证 | | | |-- 签名挑战 | | |<-- 返回签名断言 ---------| | |<-- 签名断言 -------| | | |-- 验证签名断言 -->| | |<-- 验证成功 --| | |
具体细节
- 验证挑战(challenge):这是一个随机生成的字符串,用于防止重放攻击。每次验证请求都会生成一个新的挑战。
- 依赖方信息(Relying Party ID):这是依赖方的标识,用于确保签名是由正确的私钥生成的。
- 客户端信息(origin):包括客户端的原点信息(URI),用于确保请求的合法性。
- 签名断言(assertion):认证器用私钥对挑战进行签名生成的断言,包含签名数据和其他元数据。
- 计数器值:认证器维护的计数器,用于防止重放攻击。每次成功验证后计数器都会递增。
结论
WebAuthn 的验证流程通过挑战-应答模型确保了用户身份的真实性。通过非对称加密技术,认证器生成的私钥永不离开设备,极大地提升了安全性。同时,通过用户验证步骤和计数器机制,WebAuthn 提供了强大的防重放攻击能力。这个流程与注册流程相辅相成,确保了从注册到验证的整体安全性和可靠性。
https://github.com/topics/webauthn
- 安装插件:首先,你需要安装一个支持WebAuthn和Passkey认证的WordPress插件。
-
AI搜索:通向未来的关键一步
引言:AI搜索的崛起
近年来,AI搜索已经逐渐成为科技领域的一大热点。从Perplexity的新一轮融资,到ChatGPT将其首页变为搜索框,再到国内秘塔AI搜索和360AI搜索的崛起,这一切都预示着AI搜索正在成为新的行业共识。此外,不少企业也纷纷表示要加入这一领域的竞争,显示出AI搜索的巨大市场潜力和吸引力。
搜索的市场格局
搜索技术的发展经历了从狭义的搜索引擎到广义的内容发现的转变。狭义的搜索,如百度搜索和浏览器地址栏,已进入平台期,而广义的搜索,包括内容平台内的搜索功能,正处于上升阶段。随着优质内容的分散,用户的搜索需求也日益增长,推动了AI搜索技术的发展。
AI搜索的本质
AI搜索的核心优势在于其能够提供超越传统搜索的内容理解和用户体验。AI搜索不仅仅是关于提升搜索结果的相关性,更关键的是通过深度学习和自然语言处理技术,理解用户的真实意图,并提供更准确、个性化的搜索结果。
用户使用搜索的真实目的
用户使用搜索工具的最终目的,往往不仅仅是为了找到一个网址或一个答案,而是为了解决实际问题或获取具体的资源。例如,用户可能需要找到特定的信息进行学习研究,或者寻找特定的视频内容进行观看。AI搜索通过更好的理解用户需求,能够提供更符合用户期待的搜索体验。
AI搜索的切入点和未来方向
AI搜索需要找到与传统搜索不同的切入点,这通常意味着在特定的垂直领域或新的使用场景下,发挥AI的独特优势。例如,可以在学术研究或医疗信息查询等领域,通过AI搜索提供更专业、更深入的搜索服务。
AI搜索与内容平台的关系
成功的AI搜索引擎将是那些能够与内容平台紧密结合,共同构建强大内容生态系统的引擎。例如,通过与内容创作者和平台合作,AI搜索可以更有效地聚合和推荐内容,从而为用户提供更加丰富和精准的搜索结果。
结论:AI搜索的战略意义
AI搜索不仅是技术的革新,更是对用户搜索体验的全面革命。随着技术的不断进步和市场的逐渐成熟,AI搜索将成为连接用户需求与信息世界的关键桥梁。对于企业来说,投入AI搜索技术,开发更智能、更个性化的搜索解决方案,将是抓住未来市场机遇的关键。
通过对AI搜索的深入理解和应用,我们可以预见一个更加智能和连接的信息时代的到来。
-
AI搜索全解析
AI搜索,这个听起来颇具科技感的词汇,最近在科技圈里掀起了不小的波澜。从Perplexity获得新融资,到ChatGPT将首页改为搜索框,再到国内AI搜索领域的新星——秘塔AI搜索和360AI搜索的崛起,AI搜索似乎正逐渐成为新的行业共识。在这样的背景下,许多公司也开始摩拳擦掌,准备在AI搜索领域大展拳脚。
AI搜索的市场格局
首先,让我们来梳理一下搜索市场的格局。传统搜索,无论是百度的主页还是浏览器的地址栏,其市场已经进入一个相对稳定的平台期。随着优质内容逐渐被各大App如抖音、小红书、知乎等分割,传统搜索的体验虽然成熟,但面临着内容质量下降的挑战。
然而,广义上的搜索,包括App内的搜索条以及对模型内部知识的搜索,其实正在上升期。用户数和搜索频次都在增加,显示出搜索需求的持续增长。
AI搜索的本质
AI搜索的核心在于“智能”,而不仅仅是“搜索”。这意味着,AI搜索需要基于大型语言模型(LLM)和其他工具构建的架构,提供更为精准和个性化的搜索结果。AI搜索的目标是更好地理解用户的Query(查询),并提供端到端的解决方案。
用户迁移的切入点
要让用户从传统搜索迁移到AI搜索,需要提供显著的新体验。这不仅仅是在搜索结果上做出微小改进,而是要找到新的使用场景和垂直领域,如学术、医疗、法律等,为用户提供真正有价值的新体验。
AI搜索与内容平台的关系
AI搜索与内容平台之间存在密切的依赖关系。长期来看,拥有高质量内容的社区在搜索领域更有可能胜出。例如,百度通过构建知道、百科等内容产品,试图控制内容并构建竞争壁垒。而在移动互联网时代,用户的搜索行为已经逐渐转移到各个App中,这对传统搜索引擎构成了挑战。
AI搜索的具体应用
一些AI搜索产品已经开始探索具体的应用场景,如:
- 思维导图:帮助用户条理化地收集资料,提高研究效率。
- 学术搜索和播客搜索:通过提高输入内容的质量,提升搜索结果的质量,特别适合学术研究和深度内容消费。
- 一键生成PPT:利用AI搜索结果直接生成演示文稿,提高工作效率。
- 资讯、视频、图片搜索:满足用户对传统搜索引擎功能的期待。
搜索市场的机遇与挑战
尽管AI搜索来势汹汹,但传统搜索依然有其稳固的市场和价值。搜索引擎依然是互联网流量的重要入口,具有巨大的商业价值。同时,随着技术的发展,新的搜索玩家有机会通过创新的场景和体验来挑战现有的市场格局。
从AI搜索到AGI
AI搜索是通往更高级的通用人工智能(AGI)的必经之路。通过更好地理解用户的Query,AI搜索将逐步攻克更多复杂场景,满足用户从信息搜索到内容创作的全方位需求。
在这个过程中,AI搜索不仅需要技术上的突破,更需要对用户需求深刻的洞察和创新的产品思维。随着AI技术的不断进步,我们有理由相信,AI搜索将为用户带来更加丰富和智能的搜索体验。