这一次使用纯粹使用前端来加密内容,优点是部署方便,不用折腾后端和数据库,缺点是加密不灵活,如果要修改密钥需要重新生成密文。
首先是加密代码,为了方便测试,同时加入了解密功能,你可以在本地打开这个html文件使用:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>加密内容查看器</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
margin: 0;
padding: 20px;
background: inherit;
}
.container {
width: 100%;
max-width: 100%;
padding: 20px;
box-sizing: border-box;
background: inherit;
}
.section {
margin-bottom: 30px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background: inherit;
}
textarea {
width: 100%;
height: 100px;
margin: 10px 0;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ddd;
border-radius: 4px;
background: inherit;
}
input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
font-size: 16px;
background: inherit;
}
button {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
.result {
margin-top: 20px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
word-break: break-all;
background: inherit;
}
.error {
color: #ff4444;
margin-top: 10px;
}
/* 响应式布局 */
@media screen and (max-width: 768px) {
body {
padding: 10px;
}
.container {
padding: 10px;
}
.section {
padding: 15px;
}
input {
width: 100%;
margin: 10px 0;
}
button {
width: 100%;
margin: 10px 0;
}
}
/* 超宽屏幕优化 */
@media screen and (min-width: 1200px) {
.container {
max-width: 1200px;
margin: 0 auto;
}
}
</style>
</head>
<body>
<div class="container">
<!-- 加密部分 -->
<div class="section">
<h3>加密内容</h3>
<div>
<textarea id="content-to-encrypt" placeholder="输入要加密的内容"></textarea>
</div>
<div>
<input type="password" id="encrypt-password" placeholder="设置加密密码">
<button onclick="encryptContent()">加密</button>
</div>
<div class="result" id="encrypted-result"></div>
</div>
<!-- 解密部分 -->
<div class="section">
<h3>解密内容</h3>
<div>
<textarea id="content-to-decrypt" placeholder="输入加密后的内容"></textarea>
</div>
<div>
<input type="password" id="decrypt-password" placeholder="输入解密密码">
<button onclick="decryptContent()">解密</button>
</div>
<div id="decrypt-error" class="error"></div>
<div class="result" id="decrypted-result"></div>
</div>
</div>
<script>
function encryptContent() {
const content = document.getElementById('content-to-encrypt').value;
const password = document.getElementById('encrypt-password').value;
if (!content || !password) {
alert('请输入内容和密码');
return;
}
try {
const encrypted = CryptoJS.AES.encrypt(content, password).toString();
document.getElementById('encrypted-result').textContent = encrypted;
document.getElementById('content-to-decrypt').value = encrypted;
} catch (e) {
alert('加密失败:' + e.message);
}
}
function decryptContent() {
const encryptedText = document.getElementById('content-to-decrypt').value;
const password = document.getElementById('decrypt-password').value;
const errorDiv = document.getElementById('decrypt-error');
const resultDiv = document.getElementById('decrypted-result');
if (!encryptedText || !password) {
errorDiv.textContent = '请输入加密内容和密码';
return;
}
try {
const decrypted = CryptoJS.AES.decrypt(encryptedText, password).toString(CryptoJS.enc.Utf8);
if (decrypted) {
resultDiv.textContent = decrypted;
errorDiv.textContent = '';
} else {
errorDiv.textContent = '解密失败:密码错误或内容格式不正确';
resultDiv.textContent = '';
}
} catch (e) {
errorDiv.textContent = '解密失败:' + e.message;
resultDiv.textContent = '';
}
}
document.getElementById('decrypt-password').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
decryptContent();
}
});
</script>
</body>
</html>
效果如下:
加密内容
解密内容
然后把加密后的密文放入前端中(替换位置在163行,支持markdown渲染):
<div class="encrypted-article-container">
<div id="passwordSection" class="ea-password-section">
<div class="ea-input-group">
<input type="password" id="passwordInput" class="ea-password-input" placeholder="请输入密码" autocomplete="off">
<button onclick="verifyPassword()" class="ea-button">验证密码</button>
</div>
<div id="errorMessage" class="ea-error-message"></div>
</div>
<div id="articleContent" class="ea-article-content">
<div id="articleBody" class="ea-markdown-content"></div>
</div>
<style>
.encrypted-article-container {
font-family: -apple-system, BlinkMacSystemFont, 'Microsoft YaHei', sans-serif;
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
}
.ea-password-section {
max-width: 600px;
margin: 20px auto;
padding: 20px;
text-align: center;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.ea-input-group {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
}
.ea-password-input {
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 4px;
width: 250px;
font-size: 16px;
background: #fff;
transition: border-color 0.3s;
}
.ea-password-input:focus {
outline: none;
border-color: #4CAF50;
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
}
.ea-button {
padding: 12px 24px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: transform 0.2s, filter 0.2s;
}
.ea-button:hover {
filter: brightness(110%);
transform: translateY(-1px);
}
.ea-button:active {
transform: translateY(0);
}
.ea-error-message {
color: #dc3545;
margin-top: 10px;
padding: 8px;
border-radius: 4px;
display: none;
animation: ea-fadeIn 0.3s ease;
}
.ea-article-content {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 20px;
opacity: 0;
display: none;
transition: opacity 0.5s ease;
}
.ea-article-content.visible {
opacity: 1;
}
.ea-markdown-content {
line-height: 1.8;
width: 100%;
max-width: 100%;
margin: 0 auto;
}
@keyframes ea-fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
@media screen and (max-width: 768px) {
.ea-input-group {
flex-direction: column;
align-items: center;
}
.ea-password-input,
.ea-button {
width: 100%;
max-width: 300px;
}
}
</style>
<script type="module">
import { marked } from 'https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js';
window.marked = marked;
marked.setOptions({
breaks: true,
gfm: true,
sanitize: true
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script>
class ArticleDecryptor {
constructor() {
this.setupEventListeners();
}
setupEventListeners() {
document.getElementById('passwordInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
this.verifyPassword();
}
});
}
showError(message) {
const errorElement = document.getElementById('errorMessage');
errorElement.textContent = message;
errorElement.style.display = 'block';
setTimeout(() => {
errorElement.style.display = 'none';
}, 3000);
if (message.includes('密码错误')) {
const passwordInput = document.getElementById('passwordInput');
passwordInput.value = '';
passwordInput.focus();
}
}
showArticle(content) {
const passwordSection = document.getElementById('passwordSection');
const articleContent = document.getElementById('articleContent');
const articleBody = document.getElementById('articleBody');
passwordSection.style.display = 'none';
try {
if (typeof window.marked === 'undefined') {
articleBody.textContent = content;
} else {
articleBody.innerHTML = window.marked.parse(content);
}
} catch (error) {
console.error('Markdown 渲染错误:', error);
articleBody.textContent = content;
}
articleContent.style.display = 'block';
setTimeout(() => {
articleContent.classList.add('visible');
}, 10);
}
verifyPassword() {
const password = document.getElementById('passwordInput').value.trim();
if (!password) {
this.showError('请输入密码');
return;
}
// 这里替换为实际的加密内容
const encryptedContent = ""
// 你的加密内容
try {
const decrypted = CryptoJS.AES.decrypt(encryptedContent, password).toString(CryptoJS.enc.Utf8);
if (decrypted) {
this.showArticle(decrypted);
} else {
this.showError('密码错误,请重试');
}
} catch (e) {
this.showError('密码错误,请重试');
}
}
}
// 初始化
window.decryptor = new ArticleDecryptor();
window.verifyPassword = () => window.decryptor.verifyPassword();
</script></div>
效果演示(密码2025):
评论区