#微信支付前端暑期实习

97 阅读20分钟

微信支付前端暑期实习

项目

  • 项目就不说了,聊了半小时

算法:

  • 版本号比较plus(给一堆版本号数字,从大到小排列版本号)

要在 JavaScript 中实现版本号比较并从大到小排列版本号,可以使用以下代码:

function compareVersions(v1, v2) {
    const v1Parts = v1.split('.').map(Number);
    const v2Parts = v2.split('.').map(Number);

    const length = Math.max(v1Parts.length, v2Parts.length);

    for (let i = 0; i < length; i++) {
        const v1Part = v1Parts[i] || 0;
        const v2Part = v2Parts[i] || 0;

        if (v1Part > v2Part) {
            return -1;
        }
        if (v1Part < v2Part) {
            return 1;
        }
    }

    return 0;
}

function sortVersions(versions) {
    return versions.sort(compareVersions);
}

// 示例版本号数组
const versions = [
    "1.0.0",
    "1.0.1",
    "1.0.10",
    "1.1.0",
    "2.0.0",
    "1.0.0-alpha",
    "1.0.0-beta",
    "1.0.0-rc.1"
];

// 排序版本号
const sortedVersions = sortVersions(versions);

console.log(sortedVersions);

代码说明

  1. compareVersions 函数:这个函数用于比较两个版本号。它将版本号字符串拆分成数组,然后逐个比较每个部分的数值。如果某个部分的数值较大,则返回 -1(表示 v1 比 v2 大),反之返回 1。如果所有部分都相等,则返回 0。

  2. sortVersions 函数:这个函数接收一个版本号数组,并使用 Array.prototype.sort 方法对其进行排序。排序时使用 compareVersions 函数进行比较。

  3. 示例版本号数组:提供了一些示例版本号,包括常见的语义化版本号和预发布版本号。

  4. 排序并输出结果:调用 sortVersions 函数对版本号数组进行排序,并输出排序后的结果。

注意事项

  • 该实现假设版本号是语义化版本号(例如 1.0.02.1.0 等)。
  • 如果版本号包含预发布标签(例如 1.0.0-alpha1.0.0-beta),需要额外处理预发布标签的排序逻辑。上述代码未处理预发布标签的排序。

如果需要处理预发布标签,可以扩展 compareVersions 函数,增加对预发布标签的比较逻辑。

  • 实现一个stack类,实现括号匹配
  • O(n),并且不用额外空间去找到数组中出现两次的数字

八股:

  • HTTPS握手,问的巨深

HTTPS(HyperText Transfer Protocol Secure)握手是指客户端(通常是浏览器)和服务器之间建立安全连接的过程。这个过程主要包括以下几个步骤:

  1. 客户端Hello

    • 客户端向服务器发送一个“Hello”消息,其中包含客户端支持的SSL/TLS版本、加密算法、压缩方法以及一个随机数。
  2. 服务器Hello

    • 服务器收到客户端的“Hello”消息后,回复一个“Hello”消息,其中包含服务器选择的SSL/TLS版本、加密算法、压缩方法以及一个随机数。
  3. 服务器证书

    • 服务器向客户端发送其数字证书。这个证书包含服务器的公钥,并由受信任的证书颁发机构(CA)签名。
  4. 服务器Hello Done

    • 服务器发送一个“Hello Done”消息,表示服务器端的Hello消息部分已经完成。
  5. 客户端密钥交换

    • 客户端生成一个预主密钥(pre-master secret),并使用服务器的公钥对其进行加密,然后将加密后的预主密钥发送给服务器。
  6. 客户端完成

    • 客户端发送一个“Change Cipher Spec”消息,通知服务器后续的消息将使用协商好的加密算法和密钥。
    • 客户端发送一个“Finished”消息,包含所有握手消息的摘要,以供服务器验证。
  7. 服务器完成

    • 服务器收到客户端的预主密钥后,使用自己的私钥解密得到预主密钥。
    • 服务器生成主密钥(master secret),并使用它来生成会话密钥。
    • 服务器发送一个“Change Cipher Spec”消息,通知客户端后续的消息将使用协商好的加密算法和密钥。
    • 服务器发送一个“Finished”消息,包含所有握手消息的摘要,以供客户端验证。
  8. 安全连接建立

    • 如果客户端和服务器都成功验证了对方的“Finished”消息,握手过程完成,安全连接建立。此时,客户端和服务器可以使用对称加密算法进行加密通信。

以下是一个简化的HTTPS握手流程图:

客户端                        服务器
  |  ClientHello            -->  |
  |                           <--  ServerHello
  |                           <--  服务器证书
  |                           <--  ServerHello Done
  |  客户端密钥交换          -->  |
  |  Change Cipher Spec     -->  |
  |  Finished               -->  |
  |                           <--  Change Cipher Spec
  |                           <--  Finished
  |  安全连接建立              |

通过这个握手过程,客户端和服务器可以在不安全的网络环境中建立一个安全的通信通道,确保数据的机密性、完整性和真实性。

  • CA证书签发过程

CA(Certificate Authority,证书颁发机构)证书签发过程通常包括以下几个步骤:

  1. 申请证书

    • 用户或组织向CA提交证书申请。申请中通常包括公钥和一些身份信息。
  2. 验证身份

    • CA对申请者的身份进行验证。验证方式可能包括电子邮件验证、电话验证、文件验证等,具体取决于证书类型和CA的政策。
  3. 生成证书

    • 验证通过后,CA使用其私钥对申请者的公钥和身份信息进行签名,生成数字证书。这个证书包含申请者的公钥、身份信息、CA的签名以及其他相关信息。
  4. 颁发证书

    • CA将生成的数字证书发送给申请者。申请者可以将证书安装在服务器或客户端上,以便进行安全通信。
  5. 发布证书

    • CA可能会将颁发的证书发布到一个公共的证书库中,以便其他用户或系统可以验证该证书的有效性。
  6. 证书管理

    • 证书颁发后,CA还负责证书的管理,包括证书的续期、吊销等。证书吊销信息通常会发布到CRL(Certificate Revocation List,证书吊销列表)或通过OCSP(Online Certificate Status Protocol,在线证书状态协议)进行查询。

以下是一个简单的流程图示例:

graph TD;
    A[申请证书] --> B[验证身份];
    B --> C[生成证书];
    C --> D[颁发证书];
    D --> E[发布证书];
    E --> F[证书管理];

详细步骤说明

  1. 申请证书

    • 用户生成一对密钥(公钥和私钥)。
    • 用户创建一个证书签名请求(CSR),其中包含公钥和其他身份信息。
    • 用户将CSR提交给CA。
  2. 验证身份

    • CA接收到CSR后,开始验证申请者的身份。
    • 验证方式可能包括:
      • 电子邮件验证:发送验证邮件到申请者的邮箱。
      • 电话验证:通过电话确认申请者的身份。
      • 文件验证:要求申请者提供某些文件证明其身份。
  3. 生成证书

    • 验证通过后,CA使用其私钥对CSR进行签名,生成数字证书。
    • 证书中包含申请者的公钥、身份信息、CA的签名、有效期等信息。
  4. 颁发证书

    • CA将生成的数字证书发送给申请者。
    • 申请者可以将证书安装在其服务器或客户端上。
  5. 发布证书

    • CA可能会将证书发布到一个公共的证书库中。
    • 其他用户或系统可以通过访问证书库来验证证书的有效性。
  6. 证书管理

    • CA负责证书的续期和吊销。
    • 如果证书被吊销,CA会将其添加到CRL中,或者通过OCSP提供证书状态查询服务。

通过以上步骤,CA可以确保颁发的证书是可信的,并且可以用于安全通信。

  • 如果要你去方法CSRF怎么防范?增加一个csrf token?那如果像抖音电商双十一那样有那么多流量,怎么办呢?每次都要生成一个CSRF Token吗?存redis里面负担是不是也很重?再想想

防范CSRF(跨站请求伪造)攻击是Web应用安全中的一个重要方面。CSRF攻击通过欺骗用户在不知情的情况下执行不想要的操作。常见的防范措施包括使用CSRF Token,但在高流量场景下,确实需要考虑性能和存储问题。以下是一些方法和优化建议:

1. 使用CSRF Token

CSRF Token是防范CSRF攻击的常见方法。每次用户请求时,服务器生成一个唯一的Token,并将其嵌入到表单或请求中。服务器在接收到请求时验证Token的有效性。

优化建议:

  • Token缓存:可以将Token缓存到客户端(如浏览器的LocalStorage或SessionStorage)中,减少服务器生成Token的频率。
  • Token复用:在一定时间内(如一个会话期间)复用同一个Token,而不是每次请求都生成新的Token。
  • Token存储:将Token存储在高效的内存数据库中,如Redis,但要注意设置合理的过期时间以减少存储负担。

2. 双重提交Cookie(Double Submit Cookie)

这种方法不需要在服务器端存储Token。服务器生成一个Token并通过Set-Cookie头将其发送给客户端。客户端在后续请求中将Token作为Cookie和请求参数一起发送,服务器验证这两个Token是否一致。

优化建议:

  • 减少Cookie大小:确保Token的长度适中,避免过大的Cookie影响性能。
  • 合理的Cookie过期时间:设置合理的过期时间,避免频繁生成新的Token。

3. SameSite Cookie属性

通过设置Cookie的SameSite属性,可以限制Cookie的发送范围,从而防止CSRF攻击。

SameSite属性值:

  • Strict:完全禁止跨站点请求携带Cookie。
  • Lax:允许部分跨站点请求(如GET请求)携带Cookie,但禁止跨站点POST请求。
  • None:允许所有跨站点请求携带Cookie,但需要设置Secure属性。

4. 使用CAPTCHA

在关键操作(如提交表单、进行交易)前,使用CAPTCHA验证用户的真实性,防止自动化的CSRF攻击。

5. 结合多种方法

在高流量场景下,可以结合多种方法来防范CSRF攻击。例如,使用CSRF Token和SameSite Cookie属性,同时在关键操作中加入CAPTCHA验证。

具体实现示例

以下是一个简单的CSRF Token实现示例:

# 生成CSRF Token
import secrets

def generate_csrf_token():
    return secrets.token_urlsafe(32)

# 验证CSRF Token
def validate_csrf_token(request_token, session_token):
    return request_token == session_token

# 在请求中使用Token
from flask import Flask, request, session

app = Flask(__name__)
app.secret_key = 'your_secret_key'

@app.route('/form', methods=['GET', 'POST'])
def form():
    if request.method == 'POST':
        request_token = request.form.get('csrf_token')
        session_token = session.get('csrf_token')
        if not validate_csrf_token(request_token, session_token):
            return "CSRF Token Invalid", 400
        # 处理表单提交
        return "Form Submitted Successfully"
    else:
        session['csrf_token'] = generate_csrf_token()
        return '''
            <form method="post">
                <input type="hidden" name="csrf_token" value="{}">
                <input type="submit" value="Submit">
            </form>
        '''.format(session['csrf_token'])

if __name__ == '__main__':
    app.run()

总结

在高流量场景下,防范CSRF攻击需要综合考虑安全性和性能。通过合理使用CSRF Token、双重提交Cookie、SameSite Cookie属性和CAPTCHA等方法,可以有效防范CSRF攻击,同时优化系统性能。

是的,每次请求都需要生成一个CSRF(跨站请求伪造)Token,以确保安全性。CSRF Token 是一种防止跨站请求伪造攻击的安全机制,通过在每个请求中包含一个唯一的、不可预测的令牌来验证请求的合法性。

对于像抖音电商双十一这样高流量的场景,确实需要考虑如何高效地生成和验证CSRF Token。以下是一些优化建议:

1. 使用高效的Token生成算法

选择高效的算法来生成CSRF Token,确保生成过程不会成为系统的瓶颈。常见的做法是使用加密算法生成随机数。

2. 缓存Token

可以在服务器端缓存生成的Token,减少重复生成的开销。例如,可以在用户会话中存储Token,并在会话有效期内复用。

3. 分布式Token存储

在高并发场景下,可以使用分布式缓存系统(如Redis)来存储和管理Token,确保Token的生成和验证过程高效且可扩展。

4. 前端优化

在前端,可以通过JavaScript在页面加载时生成Token,并在每次请求时附加到请求中,减少服务器的负担。

5. 负载均衡

使用负载均衡技术将请求分散到多个服务器上,确保每个服务器的负载在可控范围内。

6. 短期Token

生成短期有效的Token,减少Token的存储和管理开销。短期Token可以在一定时间内有效,过期后需要重新生成。

示例代码

以下是一个简单的CSRF Token生成和验证示例(使用Python和Flask框架):

from flask import Flask, session, request, abort
import os
import hashlib

app = Flask(__name__)
app.secret_key = os.urandom(24)

def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = hashlib.sha256(os.urandom(64)).hexdigest()
    return session['_csrf_token']

@app.before_request
def csrf_protect():
    if request.method == "POST":
        token = session.pop('_csrf_token', None)
        if not token or token != request.form.get('_csrf_token'):
            abort(403)

@app.route('/form', methods=['GET', 'POST'])
def form():
    if request.method == 'POST':
        # 处理表单提交
        return 'Form submitted successfully!'
    return '''
        <form method="post">
            <input type="hidden" name="_csrf_token" value="{}">
            <input type="text" name="data">
            <input type="submit" value="Submit">
        </form>
    '''.format(generate_csrf_token())

if __name__ == '__main__':
    app.run()

总结

在高流量场景下,CSRF Token的生成和验证需要进行优化,以确保安全性和性能的平衡。通过使用高效的算法、缓存、分布式存储和前端优化等手段,可以有效应对高并发请求。

  • POST请求一定会被浏览器拦截掉吗?

POST请求并不一定会被浏览器拦截掉。浏览器会根据具体情况决定是否拦截请求。以下是一些可能导致浏览器拦截POST请求的情况:

  1. 跨域请求:如果POST请求涉及跨域(即请求的目标域名与当前页面的域名不同),浏览器会执行跨域资源共享(CORS)策略。如果服务器没有正确配置CORS头,浏览器会拦截请求。

  2. 安全策略:现代浏览器有许多内置的安全策略,例如内容安全策略(CSP)。如果请求违反了这些策略,浏览器可能会拦截请求。

  3. 用户权限:某些请求可能需要用户权限,例如访问用户的地理位置或摄像头。如果用户拒绝权限,相关请求可能会被拦截。

  4. 浏览器扩展:一些浏览器扩展(如广告拦截器)可能会拦截特定类型的请求。

  5. HTTP方法限制:某些服务器配置可能会限制特定HTTP方法(如POST),导致请求被服务器拒绝,而不是被浏览器拦截。

以下是一个简单的POST请求示例:

<!DOCTYPE html>
<html>
<head>
    <title>POST请求示例</title>
</head>
<body>
    <form action="https://example.com/api" method="post">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name">
        <button type="submit">Submit</button>
    </form>
</body>
</html>

在这个示例中,浏览器不会拦截POST请求,除非上述情况之一发生。

参考资料

希望这些信息对你有帮助!如果你有更多问题,请随时提问。

缓存:
  • 浏览器怎么缓存(没走强制缓存和协商缓存前),浏览器图片的memory缓存了解过吗?

浏览器缓存是指浏览器在本地存储一些资源(如HTML、CSS、JavaScript、图片等),以便在用户再次访问相同网站时可以更快地加载页面。浏览器缓存主要分为两种:强制缓存和协商缓存。在讨论这两种缓存之前,了解浏览器的内存缓存(memory cache)也是很重要的。

浏览器缓存的基本流程

  1. 首次请求:当用户首次访问一个网站时,浏览器会向服务器请求所有需要的资源。
  2. 缓存资源:服务器响应后,浏览器会将这些资源存储在本地缓存中。
  3. 再次请求:当用户再次访问该网站时,浏览器会首先检查本地缓存中是否有可用的资源。

强制缓存和协商缓存

在浏览器缓存机制中,强制缓存和协商缓存是两种主要的缓存策略:

  • 强制缓存(强缓存):浏览器直接使用缓存中的资源,不与服务器进行任何通信。常见的HTTP头字段有 ExpiresCache-Control
  • 协商缓存:浏览器会向服务器发送请求,询问资源是否有更新。如果资源没有更新,服务器会告知浏览器继续使用缓存中的资源。常见的HTTP头字段有 Last-ModifiedETag

浏览器的内存缓存(Memory Cache)

内存缓存是浏览器的一种缓存机制,它将资源存储在内存中,而不是硬盘上。内存缓存的特点是速度非常快,但存储时间较短,通常在浏览器关闭或页面刷新后就会被清除。

内存缓存的特点

  1. 速度快:由于内存的读写速度远快于硬盘,内存缓存可以显著提高资源加载速度。
  2. 短暂性:内存缓存的生命周期较短,通常在页面关闭或刷新后就会被清除。
  3. 适用场景:内存缓存通常用于存储短期内频繁访问的资源,如图片、脚本等。

图片的内存缓存

浏览器在加载图片时,会将图片数据存储在内存缓存中,以便在短时间内再次访问时可以快速加载。这对于用户体验的提升非常重要,特别是在页面中有大量图片的情况下。

总结

浏览器缓存机制是提高网页加载速度和用户体验的重要手段。内存缓存作为其中的一部分,主要用于存储短期内频繁访问的资源。了解和合理利用浏览器缓存机制,可以显著提升网页的性能和用户体验。

如果你有更多关于浏览器缓存的问题,欢迎继续提问!

  • service worker了解过吗?

当然了解,Service Worker 是一种运行在浏览器后台的脚本,主要用于实现离线缓存、消息推送和网络请求拦截等功能。它是 PWA(Progressive Web App,渐进式网页应用)的核心技术之一。

Service Worker 的主要特点

  1. 独立于网页运行:Service Worker 在一个独立的线程中运行,不会阻塞主线程。
  2. 拦截和处理网络请求:可以拦截网络请求并进行自定义处理,比如缓存策略。
  3. 离线支持:通过缓存资源,使应用在没有网络连接时也能正常工作。
  4. 消息推送:支持后台消息推送,即使用户没有打开网页也能接收到通知。
  5. 生命周期:Service Worker 有自己独特的生命周期,包括安装、激活和运行。

Service Worker 的生命周期

  1. 安装(install):在这个阶段,Service Worker 会被下载并安装。通常在这个阶段进行缓存资源的操作。
  2. 激活(activate):安装完成后,Service Worker 会进入激活阶段,清理旧的缓存等操作通常在这个阶段进行。
  3. 运行(fetch):激活后,Service Worker 开始拦截和处理网络请求。

示例代码

以下是一个简单的 Service Worker 示例:

// service-worker.js

// 安装阶段
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('my-cache').then(cache => {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js',
        '/image.png'
      ]);
    })
  );
});

// 激活阶段
self.addEventListener('activate', event => {
  console.log('Service Worker 激活');
});

// 拦截网络请求
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request);
    })
  );
});

注册 Service Worker

在你的主 JavaScript 文件中注册 Service Worker:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
    .then(registration => {
      console.log('Service Worker 注册成功:', registration);
    })
    .catch(error => {
      console.log('Service Worker 注册失败:', error);
    });
}

参考资料

希望这些信息对你有所帮助!如果你有更多问题,欢迎继续提问。

  • 强制缓存优先还是协商缓存优先(艹,忘记了)

在浏览器缓存机制中,强制缓存(也称为本地缓存)和协商缓存是两种主要的缓存策略。它们的优先级取决于具体的实现和配置,但通常情况下,强制缓存优先于协商缓存。以下是两者的详细解释:

强制缓存(本地缓存)

强制缓存是指浏览器在缓存有效期内直接使用本地缓存的资源,而不与服务器进行任何通信。常见的 HTTP 头字段包括 ExpiresCache-Control

  • Expires: 指定资源的过期时间,使用绝对时间。
  • Cache-Control: 更加灵活,可以指定相对时间(如 max-age),以及其他缓存指令(如 no-cache, no-store 等)。

协商缓存

协商缓存是指浏览器在缓存有效期过后,向服务器发送请求,询问资源是否有更新。如果资源没有更新,服务器会返回 304 状态码,浏览器继续使用本地缓存的资源。常见的 HTTP 头字段包括 Last-ModifiedETag

  • Last-Modified: 资源的最后修改时间。
  • ETag: 资源的唯一标识符。

优先级

  1. 强制缓存优先:当资源的缓存时间未过期时,浏览器会直接使用本地缓存的资源,不会与服务器进行任何通信。
  2. 协商缓存次之:当资源的缓存时间过期时,浏览器会向服务器发送请求,询问资源是否有更新。如果资源没有更新,服务器返回 304 状态码,浏览器继续使用本地缓存的资源。

流程图

graph TD
    A[浏览器请求资源] --> B{本地缓存是否有效}
    B -- 是 --> C[使用本地缓存]
    B -- 否 --> D{服务器资源是否更新}
    D -- 否 --> E[返回 304,使用本地缓存]
    D -- 是 --> F[返回新资源]

总结

  • 强制缓存优先:在缓存有效期内,浏览器直接使用本地缓存。
  • 协商缓存次之:在缓存过期后,浏览器与服务器协商,决定是否使用本地缓存。

通过合理配置这两种缓存策略,可以有效提高网站的性能和用户体验。

  • 宏任务微任务

在计算机科学中,宏任务(Macro Task)和微任务(Micro Task)是两种不同的任务调度机制,主要用于管理异步操作的执行顺序。它们在事件循环(Event Loop)中扮演着重要角色,尤其是在JavaScript中。

宏任务(Macro Task)

宏任务是指那些需要较长时间才能完成的任务。常见的宏任务包括:

  • setTimeout
  • setInterval
  • setImmediate(Node.js)
  • I/O 操作
  • UI 渲染

宏任务会被放入一个队列中,事件循环会依次执行这些任务。

微任务(Micro Task)

微任务是指那些需要尽快执行的任务,通常在当前宏任务执行完之后立即执行。常见的微任务包括:

  • Promise 的回调函数
  • process.nextTick(Node.js)
  • MutationObserver

微任务会被放入一个单独的队列中,事件循环会在每个宏任务执行完之后,立即执行所有的微任务。

事件循环(Event Loop)

事件循环的工作机制大致如下:

  1. 执行一个宏任务(如果有的话)。
  2. 执行所有的微任务(如果有的话)。
  3. 更新渲染(如果需要的话)。
  4. 重复上述步骤。

示例

以下是一个简单的示例,展示了宏任务和微任务的执行顺序:

console.log('script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('promise1');
}).then(() => {
  console.log('promise2');
});

console.log('script end');

输出结果为:

script start
script end
promise1
promise2
setTimeout

解释

  1. console.log('script start')console.log('script end') 是同步任务,立即执行。
  2. setTimeout 是一个宏任务,放入宏任务队列。
  3. Promisethen 回调是微任务,放入微任务队列。
  4. 同步任务执行完毕后,事件循环会先执行微任务队列中的任务,所以 promise1promise2 先执行。
  5. 最后执行宏任务队列中的 setTimeout

通过理解宏任务和微任务的区别及其执行顺序,可以更好地编写和调试异步代码。

对我的建议:深入了解一下网络安全相关知识,以及TLS握手,why?因为我们这边是自己实现的https协议

网络安全我记得不多,但是我清楚的记得我被拷打的汗流浃背,这几天恶补一下网络相关的东西,问的很深

作者:贪玩的小狐狸在等offer
链接:www.nowcoder.com/discuss/632…
来源:牛客网