从PHP后端转Java后端代码 实现获取当前登录信息 - 教程

面对一个原本由 PHP 编写的后端系统,现在需要在 Java 后端中获取当前用户的登录状态(即“是否已登录”以及“登录用户是谁”),这是一个典型的跨语言、跨系统身份认证集成问题。

思路

方案一:共享 Session 存储

核心思想:让 PHP 和 Java 共享同一个 Session 存储后端(如 Redis、Memcached、数据库等),Java 通过读取该存储中的 Session 数据判断登录状态。

实现步骤:

  1. 确认 PHP 的 Session 存储方式
    • 如果 PHP 使用默认文件存储,则需修改为 Redis 或数据库。
    • 如果已经是 Redis/Memcached,那就直接对接。
  2. 统一 Session ID 传递方式
    • 通常通过 Cookie(如 PHPSESSID)传递。
    • Java 需要读取这个 Cookie,并用同样的 key 去查询 Session 存储。
  3. 解析 PHP 序列化的 Session 数据
    • PHP 默认使用 serialize() 序列化 Session,Java 需要能反序列化(可用第三方库如 php-serialize)。
    • 或者让 PHP 改用 JSON 存储(需修改 PHP 配置或自定义 Session handler)。
  4. Java 读取并验证
    • 根据 Session ID 从 Redis 中取出数据。
    • 解析出用户 ID、登录时间等信息。
    • 判断是否有效(如未过期、未注销)。

优点:

  • 无需改动前端。
  • 登录状态实时同步。

缺点:

  • 需要修改 PHP 的 Session 存储逻辑(如果还不是共享存储)。
  • 需处理 PHP 序列化格式兼容性问题。

方案二:通过 PHP 提供鉴权接口

核心思想:让 PHP 后端暴露一个轻量级的“验证登录状态”的 API(如 /api/check-login),Java 后端在需要时调用该接口。

实现步骤:

  1. 在 PHP 中新增一个接口,例如:php

    编辑

    // /api/check-login
    session_start();
    if(isset($_SESSION['user_id'])){
    echojson_encode(['logged_in'=>true,'user_id'=>$_SESSION['user_id']]);
    }else{
    echojson_encode(['logged_in'=>false]);
    }
  2. Java 后端在请求中携带原始 Cookie(尤其是 PHPSESSID),向该接口发起 HTTP 请求(可使用 RestTemplate、OkHttp 等)。

  3. 根据返回结果判断用户是否登录。

优点:

  • 对 Java 系统侵入小。
  • 不需要理解 PHP Session 格式。
  • 安全性由 PHP 控制。

缺点:

  • 每次验证都需要一次额外的 HTTP 调用(性能开销)。
  • 需确保 Java 能正确转发 Cookie(特别是跨域/子域场景)。

可优化:在 Java 端做短时缓存(如 5 秒),避免频繁调用。

方案三:Token 化改造

核心思想:将登录流程改为基于 Token(如 JWT),PHP 登录成功后生成 Token 并返回给前端,前端后续请求携带 Token,Java 和 PHP 都能独立验证。

实现步骤:

  1. 修改 PHP 登录逻辑:登录成功后生成 JWT(包含 user_id、exp 等),写入 Cookie 或返回给前端。
  2. 前端所有请求携带该 Token(Header 或 Cookie)。
  3. Java 后端使用公钥(或共享密钥)验证 JWT 签名,解析用户信息。

优点:

  • 彻底解耦 PHP 和 Java 的会话管理。
  • 便于未来微服务扩展。
  • 无状态,适合分布式。

缺点:

  • 需要改造现有登录流程。
  • Token 无法主动失效(除非引入黑名单机制)。
  • 甲方可能不愿改动现有 PHP 逻辑。

方案四:数据库共享用户状态

核心思想:PHP 登录/登出时在数据库中标记用户状态(如 last_login_timeis_logged_in 字段),Java 查询该表判断。

问题:

  • 无法准确判断“当前会话”是否有效(比如用户关闭浏览器但未登出)。
  • 多设备登录时状态混乱。
  • 不符合 HTTP 无状态原则。

⚠️ 一般不推荐,仅作备选。

实践步骤

为了实现改造最少原则选择 方案2 增加接口进行校验

思路如下

前端浏览器
    │
    │ (携带 Token)
    ▼
Java 后端接口(如 /api/java/getProfile)
    │
    │ 提取 Header: Token→ "PHPSESSID=abc123"
    │
    ▼
Java 内部调用 → 
                      ↑
                      └── 请求头带上 Token: PHPSESSID=abc123
    │
    │ PHP 收到后:
    │   - 根据 abc123 找到本地 session 文件
    │   - 读取 $_SESSION['user_id'] 等信息
    │
    ▼
PHP 返回 JSON:
{
  "logged_in": true,
  "user_id": 10086,
  "username": "alice"
}
    │
    ▼
Java 解析结果 → 确认当前请求用户是 user_id=10086
    │
    ▼
Java 继续执行自己的业务逻辑(如查数据库、返回数据等)

1. PHP 端:鉴权接口 (check_login.php)

这个接口非常简单,它只需要启动 Session 并返回当前用户的唯一标识(如 user_id)。

PHP

 'success',
        'user_id' => $_SESSION['user_id'],
        'username' => $_SESSION['username'] ?? ''
    ]);
} else {
    http_response_code(401); // 未登录
    echo json_encode([
        'status' => 'error',
        'message' => 'Unauthorized'
    ]);
}

2. Java 端:配置与工具类实现

A. 配置文件 (application-dev.yml)

在配置文件中定义 PHP 接口的地址。

YAML

php:
  auth:
    check-url: 
B. 核心逻辑:获取当前登录用户的封装类

我们需要利用 RestTemplate 来发送请求,并手动将前端传给 Java 的 Cookie 放入 Header 中传给 PHP。

Java

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@Component
@Slf4j
public class PhpSessionProxy {
    @Value("${php.auth.check-url}")
    private String phpCheckUrl;
    private final RestTemplate restTemplate = new RestTemplate();
    /**
     * 核心方法:获取当前 PHP 登录用户的信息
     */
    public Map getCurrentUser() {
        // 1. 从当前上下文中获取前端发给 Java 的 Request
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) return null;
        HttpServletRequest request = attributes.getRequest();
        // 2. 提取所有的 Cookie
        String cookieHeader = request.getHeader(HttpHeaders.COOKIE);
        if (cookieHeader == null || cookieHeader.isEmpty()) {
            log.warn("请求中未发现 Cookie,用户未登录");
            return null;
        }
        try {
            // 3. 构造请求头,将 Cookie 透传给 PHP
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.COOKIE, cookieHeader);
            HttpEntity entity = new HttpEntity<>(headers);
            // 4. 调用 PHP 接口
            ResponseEntity response = restTemplate.exchange(
                    phpCheckUrl,
                    HttpMethod.GET,
                    entity,
                    Map.class
            );
            if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
                return (Map) response.getBody();
            }
        } catch (Exception e) {
            log.error("PHP 鉴权接口调用失败: {}", e.getMessage());
        }
        return null;
    }
}

3. Java 端:在业务 Controller 中使用

Java

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    private final PhpSessionProxy phpSessionProxy;
    public OrderController(PhpSessionProxy phpSessionProxy) {
        this.phpSessionProxy = phpSessionProxy;
    }
    @GetMapping("/list")
    public ResponseEntity getMyOrders() {
        // 调用封装好的方法
        Map userInfo = phpSessionProxy.getCurrentUser();
        if (userInfo == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("请先在 PHP 端登录");
        }
        // 拿到 PHP 传过来的 user_id 进行 Java 侧的业务逻辑
        Object userId = userInfo.get("user_id");
        return ResponseEntity.ok("当前登录用户 ID: " + userId + ",这是来自 Java 后端的订单数据");
    }
}

优化

将其封装为静态方法

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@Slf4j
@Component
public class PhpAuthUtil {
    private static String checkUrl;
    @Value("${php.auth.check-url}")
    private String configUrl;
    @PostConstruct
    public void init() {
        checkUrl = this.configUrl;
    }
    /**
     * 静态方法:通过 Header 传参调用 PHP 鉴权
     */
    public static Map getCurrentUser() {
        try {
            // 1. 获取当前 Java 接收到的请求对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) return null;
            HttpServletRequest request = attributes.getRequest();
            // 2. 获取前端传给 Java 的 Token (假设前端传的也是 Token)
            String tokenValue = request.getHeader("Token");
            if (StrUtil.isBlank(tokenValue)) {
                log.warn("请求头中缺失 Token");
                return null;
            }
            // 3. 【核心步骤】使用 Hutool 发起带自定义 Header 的请求
            // .header("Token", tokenValue) 就会生成 Token: xxx 这种格式
            String result = HttpRequest.get(checkUrl)
                    .header("Token", tokenValue)
                    .timeout(2000) // 设置 2 秒超时,防止 PHP 卡死拖垮 Java
                    .execute()
                    .body();
            if (StrUtil.isBlank(result)) return null;
            log.info("PHP 接口返回: {}", result);
            // 4. 解析并返回
            return JSONUtil.parseObj(result.trim());
        } catch (Exception e) {
            log.error("PHP 鉴权调用异常: {}", e.getMessage());
            return null;
        }
    }
}

调用

@GetMapping("/api/info")
public Result getInfo() {
    Map user = PhpAuthUtil.getCurrentUser();
    if (user == null) {
        return Result.error("未登录");
    }
    return Result.ok(user.get("user_id"));
}

到此任务结束。

posted on 2026-02-18 15:45  ljbguanli  阅读(9)  评论(0)    收藏  举报