该帖为站主方便配置而写,目前仅支持(lxzpan.cn)此域名使用,暂未开放使用。

1. 申请接入

获取以下信息:

- `site_id`: 网站唯一标识(如:`lxzpan`)

- `site_secret`: 网站密钥(用于API验证)

- 允许的回调域名

2. 配置登录按钮

将旗下网站的登录按钮链接修改为:

https://login.lxzpan.cn/?callback_url={回调地址}&site_id={网站标识}

**参数说明:**

| 参数 | 必填 | 说明 |

|--------------|------|---------------------------------------|

| callback_url | 是 | 登录成功后的回调地址,需URL编码 |

| site_id | 是 | 网站唯一标识 |

示例:

<!-- PHP示例 -->

<a href="https://login.lxzpan.cn/?callback_url=<?php echo urlencode('https://lxzpan.cn/auth/callback'); ?>&site_id=lxzpan">

    登录

</a>
<!-- JavaScript示例 -->

<a href="https://login.lxzpan.cn/?callback_url=" + encodeURIComponent('https://lxzpan.cn/auth/callback') + "&site_id=lxzpan">

    登录

</a>

3. 处理回调

用户登录成功后,会携带 `ticket` 参数回调到指定地址:

https://your-site.com/auth/callback?ticket=xxxxxxxxxxxxxxxx

4. 验证票据获取用户信息

在回调页面中,使用 `ticket` 换取用户信息:

PHP 示例

<?php

// config.php

define('SSO_SITE_ID', 'lxzpan');

define('SSO_SITE_SECRET', 'your_secret_key');

define('SSO_API_URL', 'https://login.lxzpan.cn/api.php');

// callback.php

$ticket = $_GET['ticket'] ?? '';

if (empty($ticket)) {

    die('缺少票据参数');

}

// 调用API验证票据

$data = [

    'ticket' => $ticket,

    'site_id' => SSO_SITE_ID,

    'site_secret' => SSO_SITE_SECRET

];

$ch = curl_init(SSO_API_URL . '?action=verify_ticket');

curl_setopt($ch, CURLOPT_POST, 1);

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);

$response = curl_exec($ch);

curl_close($ch);

$result = json_decode($response, true);

if ($result['success']) {

    // 登录成功

    $user = $result['user'];

    $token = $result['token'];

    // 存储到Session

    $_SESSION['user'] = $user;

    $_SESSION['token'] = $token;

    // 跳转到首页

    header('Location: /');

    exit;

} else {

    // 登录失败

    die('登录失败: ' . $result['message']);

}

?>

JavaScript/Node.js 示例

javascript

// 回调页面处理

async function handleCallback() {

    const urlParams = new URLSearchParams(window.location.search);

    const ticket = urlParams.get('ticket');

    if (!ticket) {

        console.error('缺少票据参数');

        return;

    }

    try {

        const response = await fetch('https://login.lxzpan.cn/api.php?action=verify_ticket', {

            method: 'POST',

            headers: {

                'Content-Type': 'application/json'

            },

            body: JSON.stringify({

                ticket: ticket,

                site_id: 'lxzpan',

                site_secret: 'your_secret_key'

            })

        });

        const result = await response.json();

        if (result.success) {

            // 存储用户信息

            localStorage.setItem('user', JSON.stringify(result.user));

            localStorage.setItem('token', result.token);

            // 跳转到首页

            window.location.href = '/';

        } else {

            console.error('登录失败:', result.message);

        }

    } catch (error) {

        console.error('请求失败:', error);

    }

}

handleCallback();

Python 示例

import requests

from flask import Flask, request, redirect, session

SSO_SITE_ID = 'lxzpan'

SSO_SITE_SECRET = 'your_secret_key'

SSO_API_URL = 'https://login.lxzpan.cn/api.php'

@app.route('/auth/callback')

def auth_callback():

    ticket = request.args.get('ticket')

    if not ticket:

        return '缺少票据参数', 400

    # 验证票据

    response = requests.post(

        f'{SSO_API_URL}?action=verify_ticket',

        json={

            'ticket': ticket,

            'site_id': SSO_SITE_ID,

            'site_secret': SSO_SITE_SECRET

        }

    )

    result = response.json()

    if result['success']:

        # 登录成功

        session['user'] = result['user']

        session['token'] = result['token']

        return redirect('/')

    else:

        return f"登录失败: {result['message']}", 400

API 接口文档

1. 用户登录

POST /api.php?action=login

**请求参数:**

| 参数 | 类型 | 必填 | 说明 |

|------|------|------|------|

| username | string | 是 | 用户名/邮箱/手机号 |

| password | string | 是 | 密码 |

| callback_url | string | 否 | 回调地址(SSO时使用) |

| site_id | string | 否 | 网站标识(SSO时使用) |

响应示例:

```json

{

    "success": true,

    "message": "登录成功",

    "token": "eyJ0b2tlbiI6...",

    "user": {

        "id": 1,

        "username": "testuser",

        "email": "test@example.com"

    },

    "ticket": "a1b2c3d4e5f6...",

    "callback_url": "https://lxzpan.cn/auth/callback"

}

2. 验证票据

POST /api.php?action=verify_ticket

**请求参数:**

| 参数 | 类型 | 必填 | 说明 |

|------|------|------|------|

| ticket | string | 是 | SSO票据 |

| site_id | string | 是 | 网站标识 |

| site_secret | string | 是 | 网站密钥 |

响应示例:

```json

{

    "success": true,

    "user": {

        "id": 1,

        "username": "testuser",

        "email": "test@example.com",

        "avatar": "https://...",

        "created_at": "2024-01-01 00:00:00"

    },

    "token": "eyJ0b2tlbiI6..."

}

3. 验证 Token

POST /api.php?action=verify_token

请求头:

Authorization: Bearer {token}

响应示例:

```json

{

    "success": true,

    "user": {

        "id": 1,

        "username": "testuser",

        "email": "test@example.com",

        "avatar": "https://...",

        "created_at": "2024-01-01 00:00:00"

    }

}

4. 刷新 Token

POST /api.php?action=refresh_token

请求头:

Authorization: Bearer {token}

响应示例:

```json

{

    "success": true,

    "token": "eyJ0b2tlbiI6..."

}

5. 登出

POST /api.php?action=logout

响应示例:

json

{

    "success": true,

    "message": "登出成功"

}

完整接入示例

PHP 完整示例

<?php

// sso_config.php

class SSOConfig {

    const SITE_ID = 'lxzpan';

    const SITE_SECRET = 'your_secret_key';

    const LOGIN_URL = 'https://login.lxzpan.cn/';

    const API_URL = 'https://login.lxzpan.cn/api.php';

}

// sso_helper.php

class SSOHelper {

    // 获取登录链接

    public static function getLoginUrl($callbackUrl) {

        return SSOConfig::LOGIN_URL . '?callback_url=' . urlencode($callbackUrl)

               . '&site_id=' . SSOConfig::SITE_ID;

    }

    // 验证票据

    public static function verifyTicket($ticket) {

        $data = [

            'ticket' => $ticket,

            'site_id' => SSOConfig::SITE_ID,

            'site_secret' => SSOConfig::SITE_SECRET

        ];

        $ch = curl_init(SSOConfig::API_URL . '?action=verify_ticket');

        curl_setopt($ch, CURLOPT_POST, 1);

        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);

        $response = curl_exec($ch);

        curl_close($ch);

        return json_decode($response, true);

    }

    // 验证Token

    public static function verifyToken($token) {

        $ch = curl_init(SSOConfig::API_URL . '?action=verify_token');

        curl_setopt($ch, CURLOPT_POST, 1);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        curl_setopt($ch, CURLOPT_HTTPHEADER, [

            'Content-Type: application/json',

            'Authorization: Bearer ' . $token

        ]);

        $response = curl_exec($ch);

        curl_close($ch);

        return json_decode($response, true);

    }

}

// 使用示例

session_start();

// 1. 在登录页面

$loginUrl = SSOHelper::getLoginUrl('https://lxzpan.cn/auth/callback');

echo '<a href="' . $loginUrl . '">登录</a>';

// 2. 在回调页面

if (isset($_GET['ticket'])) {

    $result = SSOHelper::verifyTicket($_GET['ticket']);

    if ($result['success']) {

        $_SESSION['user'] = $result['user'];

        $_SESSION['token'] = $result['token'];

        header('Location: /');

        exit;

    }

}

// 3. 验证当前用户是否登录

if (isset($_SESSION['token'])) {

    $result = SSOHelper::verifyToken($_SESSION['token']);

    if (!$result['success']) {

        // Token过期,需要重新登录

        unset($_SESSION['user']);

        unset($_SESSION['token']);

    }

}

?>

### JavaScript 完整示例

## 安全注意事项

// sso-config.js

const SSO_CONFIG = {

    SITE_ID: 'lxzpan',

    SITE_SECRET: 'your_secret_key',

    LOGIN_URL: 'https://login.lxzpan.cn/',

    API_URL: 'https://login.lxzpan.cn/api.php'

};

// sso-helper.js

class SSOHelper {

    // 获取登录链接

    static getLoginUrl(callbackUrl) {

        return `${SSO_CONFIG.LOGIN_URL}?callback_url=${encodeURIComponent(callbackUrl)}&site_id=${SSO_CONFIG.SITE_ID}`;

    }

    // 验证票据

    static async verifyTicket(ticket) {

        const response = await fetch(`${SSO_CONFIG.API_URL}?action=verify_ticket`, {

            method: 'POST',

            headers: {

                'Content-Type': 'application/json'

            },

            body: JSON.stringify({

                ticket: ticket,

                site_id: SSO_CONFIG.SITE_ID,

                site_secret: SSO_CONFIG.SITE_SECRET

            })

        });

        return await response.json();

    }

    // 验证Token

    static async verifyToken(token) {

        const response = await fetch(`${SSO_CONFIG.API_URL}?action=verify_token`, {

            method: 'POST',

            headers: {

                'Content-Type': 'application/json',

                'Authorization': `Bearer ${token}`

            }

        });

        return await response.json();

    }

    // 检查登录状态

    static async checkLogin() {

        const token = localStorage.getItem('token');

        if (!token) return false;

        const result = await this.verifyToken(token);

        if (result.success) {

            localStorage.setItem('user', JSON.stringify(result.user));

            return true;

        } else {

            localStorage.removeItem('token');

            localStorage.removeItem('user');

            return false;

        }

    }

    // 获取当前用户

    static getCurrentUser() {

        const user = localStorage.getItem('user');

        return user ? JSON.parse(user) : null;

    }

    // 登出

    static logout() {

        localStorage.removeItem('token');

        localStorage.removeItem('user');

        window.location.href = this.getLoginUrl(window.location.href);

    }

}

// 使用示例

// 1. 登录按钮

const loginBtn = document.getElementById('login-btn');

loginBtn.href = SSOHelper.getLoginUrl(window.location.origin + '/callback.html');

// 2. 回调页面处理

async function handleCallback() {

    const urlParams = new URLSearchParams(window.location.search);

    const ticket = urlParams.get('ticket');

    if (ticket) {

        const result = await SSOHelper.verifyTicket(ticket);

        if (result.success) {

            localStorage.setItem('token', result.token);

            localStorage.setItem('user', JSON.stringify(result.user));

            window.location.href = '/';

        } else {

            alert('登录失败: ' + result.message);

        }

    }

}

// 3. 页面加载时检查登录状态

document.addEventListener('DOMContentLoaded', async () => {

    const isLoggedIn = await SSOHelper.checkLogin();

    if (!isLoggedIn && window.location.pathname !== '/login.html') {

        // 未登录,跳转到登录页

        window.location.href = SSOHelper.getLoginUrl(window.location.href);

    }

});

1. 密钥保密: `site_secret` 必须严格保密,不要在前端代码中暴露

2. HTTPS: 所有通信必须使用 HTTPS

3. 票据有效期: SSO票据有效期为5分钟,且只能使用一次

4. Token存储: 建议将Token存储在 httpOnly Cookie 中,避免XSS攻击

5. 回调域名: 确保回调域名已在登录中心白名单中注册

常见问题

Q: 如何获取 site_id 和 site_secret?

A: 联系管理员申请接入,提供你的网站域名和名称。

Q: 票据验证失败怎么办?

A: 检查以下几点:

票据是否已过期(5分钟有效期)

票据是否已被使用(只能使用一次)

site_id 和 site_secret 是否正确

Q: 如何实现自动登录?

A: 在页面加载时调用 `verify_token` 接口验证本地存储的token,如果有效则保持登录状态。

Q: 如何退出登录?

A: 清除本地存储的token和用户信息,并可选择调用 `logout` 接口。