博客:八股文网站验证码解锁与JWT登录机制解析
一、八股文网站的验证码解锁功能实现原理
许多技术类博客或八股文网站通过"关注公众号后输入验证码解锁全文"的功能增加用户粘性。这种机制涉及前后端协作,以下是实现方式及相关问题的详细分析:
1. 实现方式
用户访问文章页面时,前端初始只加载部分内容,全文被隐藏或加密。后端控制解锁逻辑,流程如下:
- 关注公众号:用户关注后获取验证码。
- 输入验证码:前端将验证码发送至后端。
- 后端验证:后端校验验证码(通过公众号接口或预设值),验证通过后返回解锁令牌(token)。
- 前端存储与解锁:前端将token存储在本地(如localStorage或cookie),用以解锁全文。
若每个页面访问都需携带token请求权限,会导致:
- 用户体验差:每次跳转都需等待验证。
- 后端压力大:频繁请求增加服务器负担。
2. 优化体验:前端缓存解锁状态
为提升体验,常见做法是前端在首次验证成功后缓存全文或解锁状态,后续页面访问直接使用本地数据,避免重复请求。以Vue为例,具体实现如下:
Vue中的实现
假设使用Vue开发前端,可以通过Vuex(或Pinia)管理状态,结合localStorage存储token和解锁内容。以下是详细步骤:
- 首次验证 :
- 用户输入验证码,前端发送请求至后端。
- 后端返回token(包含过期时间戳,如JWT中的
exp)和全文内容。 - 前端将token和内容存入localStorage,并更新Vuex状态。
- 状态管理 :
- Vuex存储一个
isUnlocked标志和全文内容。 - 页面组件根据
isUnlocked决定显示部分还是全文。
- Vuex存储一个
- 时间限制与token验证 :
- 在Vue组件的
created或mounted钩子中,检查localStorage中的token。 - 解析token,提取过期时间戳(
exp),与当前时间戳(Date.now())比较。 - 若当前时间大于
exp,则删除localStorage中的token和内容,更新Vuex状态为未解锁,提示用户重新请求token。
- 在Vue组件的
示例代码(Vue + Vuex)
javascript
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
isUnlocked: false,
fullContent: ''
},
mutations: {
setUnlocked(state, { isUnlocked, content }) {
state.isUnlocked = isUnlocked;
state.fullContent = content;
}
},
actions: {
checkToken({ commit }) {
const token = localStorage.getItem('unlockToken');
if (token) {
const payload = JSON.parse(atob(token.split('.')[1])); // 解析JWT的Payload
const currentTime = Math.floor(Date.now() / 1000); // 当前时间戳(秒)
if (currentTime < payload.exp) {
const content = localStorage.getItem('fullContent');
commit('setUnlocked', { isUnlocked: true, content });
} else {
localStorage.removeItem('unlockToken');
localStorage.removeItem('fullContent');
commit('setUnlocked', { isUnlocked: false, content: '' });
}
}
},
unlockContent({ commit }, { token, content }) {
localStorage.setItem('unlockToken', token);
localStorage.setItem('fullContent', content);
commit('setUnlocked', { isUnlocked: true, content });
}
}
});
// App.vue
<template>
<div>
<div v-if="isUnlocked">{{ fullContent }}</div>
<div v-else>
<p>部分内容...</p>
<input v-model="code" placeholder="输入验证码" />
<button @click="submitCode">解锁</button>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
data() {
return { code: '' };
},
computed: mapState(['isUnlocked', 'fullContent']),
created() {
this.checkToken(); // 页面加载时检查token
},
methods: {
...mapActions(['checkToken', 'unlockContent']),
async submitCode() {
const res = await fetch('/api/unlock', {
method: 'POST',
body: JSON.stringify({ code: this.code })
});
const { token, content } = await res.json();
this.unlockContent({ token, content });
}
}
};
</script>
- 逻辑说明 :
- 每次页面加载时,
checkToken检查token是否过期。 - 若未过期,直接使用缓存内容;若过期,清除缓存,恢复未解锁状态。
- 用户提交验证码后,存储新token和内容,并更新状态。
- 每次页面加载时,
保证限定时间访问
通过token中的exp字段和前端时间戳比较,确保用户只能在限定时间内访问:
- 后端设置 :生成token时设置
exp(如1小时后过期)。 - 前端校验 :每次访问时比较
exp与当前时间,过期则删除权限。 - 安全性 :因token由后端签名,前端无法篡改
exp,保证时间限制有效。
3. LocalStorage与Cookie的区别
- LocalStorage:容量大(5-10MB),仅客户端访问,适合存储token和内容。
- Cookie:容量小(4KB),自动随请求发送,适合服务端验证但增加请求开销。 在此场景,localStorage更适合缓存解锁状态,减少后端压力。
4. JWT与无状态token
JWT通过签名实现无状态验证。若不使用Redis,可在Payload中嵌入exp,前端自行判断过期。篡改JWT会导致签名失效,除非密钥泄露。
二、JWT登录的原理与篡改问题
1. JWT登录原理
- 用户登录后,服务端生成JWT,前端存储并携带于请求中。
- 服务端验证签名和
exp,确认合法性。 - 前端可缓存权限状态,减少请求。
2. JWT被篡改的问题
- 篡改Payload:签名失效,验证失败。
- 密钥泄露:需确保密钥安全。
- 前端续约:无密钥无法伪造。
3. 应对措施
- 缓存优化:前端缓存状态,减少验证频率。
- 短有效期:搭配刷新机制。
- HTTPS:防止窃取。
总结
八股文网站的解锁功能通过token实现,Vue中可利用localStorage和Vuex缓存状态,通过exp与时间戳比较限制访问时间。JWT提供无状态认证,结合前端优化可提升体验并减轻后端压力。