| 标题 | https://github.com/201206030/ novel 3.5.0 Missing Authorization |
|---|
| 描述 | require purchase entitlement before returning VIP chapter content- #52
## Summary
`BookServiceImpl.getBookContentAbout(chapterId)`, reachable via the public `GET /api/front/book/content/{chapterId}` endpoint, returns the full chapter body unconditionally regardless of the chapter's `isVip` flag. Authentication on this endpoint is optional (`TokenParseInterceptor` only parses a JWT if one is present, never requires it), so any caller, including an anonymous one, can read paid chapter content for free.
`isVip` is only ever used for display purposes elsewhere in this class (badging a chapter as VIP in list/metadata responses), never consulted for an access decision. `user_consume_log`, the purchase-record table, has zero service-layer callers anywhere in the codebase: the backend implements no payment-gating logic for VIP chapters at all, despite `isVip` existing specifically to mark them and the project's own feature list advertising recharge/subscription ("充值订阅").
## Details
```java
@Override
public RestResp<BookContentAboutRespDto> getBookContentAbout(Long chapterId) {
log.debug("userId:{}", UserHolder.getUserId());
// 查询章节信息
BookChapterRespDto bookChapter = bookChapterCacheManager.getChapter(chapterId);
// 查询章节内容
String content = bookContentCacheManager.getBookContent(chapterId);
...
return RestResp.ok(BookContentAboutRespDto.builder()
.bookInfo(bookInfo)
.chapterInfo(bookChapter)
.bookContent(content)
.build());
}
```
No check of `bookChapter.getIsVip()`, no check of `UserHolder.getUserId()` (which stays `null` for any request with no `Authorization` header), and no check of any purchase record precedes returning `content`.
## Proof of Concept
1. As any author (or via direct DB insert for testing), publish a book with a chapter marked `is_vip = 1` and non-empty content.
2. As a completely anonymous caller, with no `Authorization` header at all:
```bash
curl -s http://localhost:8080/api/front/book/content/<chapterId>
```
3. The response is `200 OK` with the full VIP chapter text in `data.bookContent`, despite no token, no purchase, and no subscription of any kind.
4. With the fix applied, the same anonymous request instead returns:
```json
{"code":"A0301","message":"访问未授权"}
```
5. Repeating step 2 as an *authenticated* user who has never purchased the chapter (a valid JWT, no `user_consume_log` row for this `chapterId`) produces the same `A0301` rejection. Only a request from a user with a matching `user_consume_log` row (`user_id`, `product_type = 0`, `product_id = <chapterId>`) receives the content.
## Impact
Any deployment of this project that marks chapters as paid (`is_vip = 1`) has no server-side enforcement of that restriction at all: the entire revenue-gating feature is bypassable by anyone, with or without an account, via a single unauthenticated GET request.
## Fix
`getBookContentAbout` now requires, for a VIP chapter, that the caller is authenticated and has a matching `user_consume_log` row for that specific chapter. Since no code path currently writes such a row, this correctly denies every caller for now, while recognizing entitlement the moment a purchase flow (or an administrator) starts writing these rows.
## Testing
Live-validated end to end against real MySQL + Redis (no project test suite exists for this service layer, same as the prior #51 review of this repo): planted a VIP chapter's content, confirmed an anonymous request returned it in full before the fix and was rejected (`A0301`) after; confirmed an authenticated-but-non-purchasing user is also rejected; confirmed inserting a matching `user_consume_log` row grants access; confirmed a non-VIP chapter is unaffected throughout. |
|---|
| 来源 | ⚠️ https://github.com/201206030/novel/pull/52 |
|---|
| 用户 | carfeii (UID 97470) |
|---|
| 提交 | 2026-10-14 14時55分 (23 日前) |
|---|
| 管理 | 2026-11-06 10時38分 (23 days later) |
|---|
| 状态 | 已接受 |
|---|
| VulDB条目 | 431793 [xxyopen/201206030 novel 直到 3.5.0 Book Content Service BookServiceImpl.java BookServiceImpl.getBookContentAbout chapterId 权限提升] |
|---|
| 积分 | 20 |
|---|