惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
月光博客
月光博客
J
Java Code Geeks
Jina AI
Jina AI
罗磊的独立博客
宝玉的分享
宝玉的分享
S
SegmentFault 最新的问题
D
DataBreaches.Net
博客园 - 叶小钗
腾讯CDC
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Google DeepMind News
Google DeepMind News
阮一峰的网络日志
阮一峰的网络日志
B
Blog
V
Visual Studio Blog
雷峰网
雷峰网
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报

任霏博客

我将关闭服务器:AI彻底掐死了奄奄一息的个人博客 - 博客文章 - 任霏的个人博客网站 Vibe Coding 实现本地模型 Token 自由 IntelliJ IDEA + LM Studio + LM Link + Continue - 博客文章 - 任霏的个人博客网站 1Password涨价后,别急着退订1Password,这个操作能帮你省25% - 博客文章 - 任霏的个人博客网站 我,吃饱了撑的注册了个域名,Cloudflare账号没了,不建议将域名放在Cloudflare - 博客文章 - 任霏的个人博客网站 临时邮箱:保护隐私与免骚扰的新方式 - 博客文章 - 任霏的个人博客网站 价值4100万美元SOL被盗SwissBorg在Solana上遭遇安全事件超200万枚ETH排队退出质押 - 博客文章 - 任霏的个人博客网站 注意 Web3 钱包遭遇 NPM 超大规模供应链攻击投毒事件 - 博客文章 - 任霏的个人博客网站 我受到以太坊ERC-20假代币地址投毒攻击记录一下大家谨防上当受骗 - 博客文章 - 任霏的个人博客网站 在2025年使用显卡 NVIDIA RTX 2080 Ti 挖矿收益记录和分析还能不能挖矿 - 博客文章 - 任霏的个人博客网站 分享我是如何成功戒烟的经验(包含失败的经验) - 博客文章 - 任霏的个人博客网站 在 OpenWRT 中配置 PassWall2 插件的教程记录 - 博客文章 - 任霏的个人博客网站 Office Professional Plus 2019 VL 版下载与 KMS 激活 - 博客文章 - 任霏的个人博客网站 最近几天我的 CDN 流量受到来自电信[山东烟台]、[江苏扬州]两地家庭宽带的攻击 - 博客文章 - 任霏的个人博客网站 自建AI服务器使用PVE配置显卡直通虚拟机安装驱动、CUDA和cuDNN运行LLM大模型进行AI炼丹 - 博客文章 - 任霏的个人博客网站 各代英特尔Intel芯片组主板适配兼容的CPU和DDR内存数据统计 - 博客文章 - 任霏的个人博客网站 GitLab Global 国际站将在60天内删除中国大陆、香港、澳门地区的账号 - 博客文章 - 任霏的个人博客网站 Github Copilot Free 开放免费版所有人均可使用 OpenAI GPT-4o、Anthropic Claude 3.5 AI 代码生成服务 - 博客文章 - 任霏的个人博客网站 Cloudflare 更新了订阅协议明确禁止优选IP和搭建梯子的行为 - 博客文章 - 任霏的个人博客网站 Linux(systemd)手动离线安装二进制(binary)MairaDB数据库指定版本 - 博客文章 - 任霏的个人博客网站 流程引擎 Flowable/Activiti 无法启动报错:liquibase - Waiting for changelog lock.... - 博客文章 - 任霏的个人博客网站 Spring Boot 全局异常捕获 ControllerAdvice 无法捕获 过滤器(Filter)和拦截器(Interceptor)中的异常 - 博客文章 - 任霏的个人博客网站 Freenom 收回了全部免费域名(.tk/.cf/.gq/.ga/.ml) - 博客文章 - 任霏的个人博客网站 Alibaba Druid 数据库连接池 takeLast() AQS 死锁导致程序无响应 - 博客文章 - 任霏的个人博客网站 你的网站加入 HSTS preload 预加载列表了吗 - 博客文章 - 任霏的个人博客网站 我的博客网站接入使用 Cloudflare 的架构分享 - 博客文章 - 任霏的个人博客网站 在 Ubuntu 上的 Nginx 高并发配置实践 - 博客文章 - 任霏的个人博客网站 技术分析黑客敲诈勒索站长的新手法百度对此也无能为力 - 博客文章 - 任霏的个人博客网站 百度站长平台快速收录权限和sitemap提交权限被全部收回 - 博客文章 - 任霏的个人博客网站 极狐 GitLab 免费时代结束不升级付费账号将禁止登陆 - 博客文章 - 任霏的个人博客网站 免费.ml域名10年委托合同到期被马里共和国收回域名经营权 - 博客文章 - 任霏的个人博客网站
Java Spring Boot 项目与 Discuz! 论坛同步登录登出 会员打通...
任霏 · 2020-12-25 · via 任霏博客

我的主站 Spring Boot 与 Discuz! 论坛进行了打通,实现了同步的登录登出,并可以将 Discuz! 论坛中会员的数据回显到我的主站中,并且我对之前开源的 discuz-ucenter-api-for-java 进行了部分修改,以适应当下 Spring Boot 的风格。

首先鸣谢开源

与 Discuz! 打通一般分为通过 API 通讯和数据库通讯,这次介绍的是通过 API 通讯,这就不得不提一下 discuz-ucenter-api-for-java,首先要感谢原作者:梁平 (no_ten@163.com)将他的代码开源,这让我们节省了很多时间。

重新修改开源代码

在我下载源码后我发现这个还是2009年的代码,满满的复古风,而且原作者是根据PHP的代码翻译过去的,所以里面很多习惯并不是Java的习惯,关键还要配置 servlet,也不支持Maven构建。这在现在的 Spring Boot 项目中是格格不入,所以我对源码进行了修改并二次发布。

为了适应 Maven 构建的 Spring Boot 风格,项目地址:https://github.com/renfei/discuz-ucenter-api-for-java,我做了以下修改:

  • 将项目包发布到 Maven 中央仓库
  • 基于现在Java的JDK8,移除了原作者的Base64实现,使用JDK8提供的java.util.Base64,所以最低需要JDK8来运行。
  • 将方法名下划线的风格改为驼峰命名风格,例如将「uc_user_delete()」改为「ucUserDelete()」。
  • 将PHP风格的变量名改为普通Java用的变量名,例如将「String $module」改为「String $module」。
  • 将使用配置文件的方式改为实例化时构造方法传参的方式
  • 将配置 servlet 改为自行定义 Controller 并处理 HttpServletRequest 和 HttpServletResponse
  • 部分中文乱码的问题进行了修复

在开始集成前,我们先要对Discuz的UCenter进行配置,拿到接口地址、通讯Key、APPID。先到Discuz的后台UCenter这里,新增一个应用:

新增UCenter应用

然后填写配置,应用类型选择“其他”,再个应用起个名字,应用主的URL地址就填你的地址,通信秘钥自己随便设置个密码,最下方选择开启同步登录和接受通知:

配置UCenter应用

Spring Boot 集成 discuz-ucenter-api-for-java

我使用的是 Maven 构建的 Spring Boot 项目,所以需要先修改 pom.xml 将 discuz-ucenter-api-for-java 引入项目中:

<dependency>
  <groupId>net.renfei</groupId>
  <artifactId>discuz-ucenter-api-for-java</artifactId>
  <version>1.0.6</version>
</dependency>

然后我个人认为分为两大部分,一部分是作为客户端进行接收来自Discuz的UCenter的消息;一部分是主动向Discuz的UCenter发送消息,我们分开来说。

接收来自Discuz的UCenter的消息

先创建一个Controller,然后创建一个处理HttpServletRequest和HttpServletResponse的方法,给一个UCenter请求的地址@RequestMapping(“/api/uc.php”),实例化一个客户端net.renfei.discuz.ucenter.api.UCClient和net.renfei.discuz.ucenter.client.Client,然后把HttpServletRequest交给net.renfei.discuz.ucenter.api.UCClient.doAnswer()去处理,最后将结果写入HttpServletResponse,如果UCenter配置正确,就应该可以在UCenter看到通讯正常了。具体使用如下案例:

@Controller
public class UCenterController {
    @ResponseBody
    @RequestMapping("/api/uc.php")
    public void uc(HttpServletRequest request, HttpServletResponse response) throws IOException {
        UCClient ucClient = new UCClient();
        Client client = new Client("http://localhost:8080/uc_server", null, "123456789", "3","");
        String result = ucClient.doAnswer(client, request, response);
        response.getWriter().print(result);
    }
}

主动向Discuz的UCenter发送消息

注册

Client client = new Client("http://localhost/uc_server", null, "key", "2","");
String string = client.ucUserRegister("username","password","email");

先登陆再同步登陆

Client client = new Client("http://localhost/uc_server", null, "key", "2","");
// 登陆
String result = client.ucUserLogin(uid);
LinkedList<String> rs = XMLHelper.ucUnserialize(result);
if(rs.size() > 0){
    int uid = Integer.parseInt(rs.get(0));
    String username = rs.get(1);
    String password = rs.get(2);
    String email = rs.get(3);
    if(uid > 0) {
        //同步登陆
        String string = client.ucUserSynlogin(uid);
        //本地登陆代码
        //TODO ... ....
    } else if(uid == -1) {
        System.out.println("用户不存在,或者被删除");
    } else if(uid == -2) {
        System.out.println("密码错");
    } else {
        System.out.println("未定义");
    }
}else{
    System.out.println("Login failed");
    System.out.println(result);
}

登陆

Client client = new Client("http://localhost/uc_server", null, "key", "2","");
String string = client.ucUserLogin("username","password");

同步登陆

Client client = new Client("http://localhost/uc_server", null, "key", "2","");
int UID = 21; //此处是用户的UID
String string = client.ucUserSynlogin(uid);

常见问题

同步登陆接口成功但没有登陆状态

  • 症状表现:调用 net.renfei.discuz.ucenter.client.Client#ucUserSynlogin 成功,拿到了JavaScript代码,请求JS地址也成功,但是访问Discuz就是没有登陆状态。查看请求JS地址的Response头信息,Set-Cookie中没有xxxx_2132_auth的Cookie。
  • 发现场景:首先调用 net.renfei.discuz.ucenter.client.Client#ucUserRegister 进行用户注册,然后调用 net.renfei.discuz.ucenter.client.Client#ucUserLogin 用户登陆获取 uid,再调用 net.renfei.discuz.ucenter.client.Client#ucUserSynlogin 进行同步登陆获取 JavaScript代码,浏览器请求JS地址,全部都是成功状态,但是访问Discuz就是没有登陆状态。
  • 问题根源:当调用 net.renfei.discuz.ucenter.client.Client#ucUserRegister 进行用户注册后,用户信息会在 UCenter 中注册插入 pre_ucenter_members 表中,但不会自动插入 Discuz 论坛的 pre_common_member 表中,所以调用 net.renfei.discuz.ucenter.client.Client#ucUserSynlogin 进行同步登陆成功,访问 Discuz 的时候因为 Discuz 中没有这个用户的信息,所以不会有登陆状态。
  • 解决方案:第一种:注册用户的逻辑直接连接数据库,将用户相关的数据插入 UCenter 和 Discuz 的用户相关表中,涉及到的表需要查文档这里不再赘述。第二种:修改 UCenter 的代码,在插入 UCenter 用户表的同时,向 Discuz 用户表中也同步插入用户的信息。
  • 最后总结:UCenter 只是中间桥梁,打通各个应用的用户账号,但它不会通知各个应用有了新用户,Discuz 自带的登陆页面登陆的时候发现自己没有这个用户就会从 UCenter 拉取这个用户信息插入自己的用户表,所以你通过 UCenter 注册接口注册用户以后,如果这个用户从未登陆过 Discuz,那么 Discuz 中是没有这个用户的信息的,如果你想统一登陆验证,那么就需要你手动到 Discuz 的用户表中插入相关信息,其中涉及到多个表,请查阅官方文档。

更多信息请阅读源代码,此处不再一一演示。您可以提出issues或者到我的社区论坛一起讨论:https://bbs.renfei.net/forum-44-1.html