最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JWT+Passport.js身份驗(yàn)證實(shí)現(xiàn)完整步驟

 更新時(shí)間:2025年09月05日 09:32:40   作者:qq_36437172  
Passport.js是一個(gè)輕量級的身份驗(yàn)證中間件,廣泛用于Node.js應(yīng)用程序中,它簡化了用戶認(rèn)證過程,支持多種策略,這篇文章主要介紹了JWT+Passport.js身份驗(yàn)證實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下

前言

在互聯(lián)網(wǎng)應(yīng)用開發(fā)中,「身份驗(yàn)證」「權(quán)限控制」「跨系統(tǒng)登錄」是保障安全與用戶體驗(yàn)的核心需求。在現(xiàn)代 Web 應(yīng)用中,登錄注冊是用戶使用系統(tǒng)的入口,而 JWT+Passport.js 實(shí)現(xiàn)身份驗(yàn)證、RBAC 落地權(quán)限控制、Redis 支撐單點(diǎn)登錄(SSO),共同構(gòu)成完整的用戶身份管理體系。

一、基礎(chǔ)概念

1. 身份驗(yàn)證 vs 授權(quán)

概念核心目標(biāo)常見場景/方式
身份驗(yàn)證確認(rèn)當(dāng)前用戶信息(第一道防線)賬號密碼、手機(jī)驗(yàn)證碼、人臉/指紋登錄
授權(quán)用戶允許第三方(App/小程序)訪問特定資源1. 登錄微信時(shí)授權(quán)昵稱/手機(jī)號;
2. 安裝 App 授權(quán)相機(jī)/位置權(quán)限

2. 憑證

  • 作用:連接「身份驗(yàn)證」與「授權(quán)」的橋梁,用于標(biāo)識用戶身份。
  • 互聯(lián)網(wǎng)場景:用戶登錄后,服務(wù)器會返回 SessionID(會話ID)或 Token(令牌);后續(xù)請求攜帶這個(gè)憑證,服務(wù)器才能識別身份并判斷是否有權(quán)操作。

3. Cookie vs Session:傳統(tǒng)會話維持方案

傳統(tǒng) Web 應(yīng)用常用這對組合維持用戶登錄狀態(tài),但存在明顯差異:

對比維度Cookie(客戶端存儲)Session(服務(wù)端存儲)
存儲位置瀏覽器(文本文件,≤4KB)服務(wù)器(數(shù)據(jù)庫/內(nèi)存,無大小限制)
安全性可被用戶修改,風(fēng)險(xiǎn)高用戶無法直接訪問,更安全(需防CSRF)
生命周期可設(shè)長有效期(如“記住登錄”)短,關(guān)閉瀏覽器/超時(shí)后失效

核心問題:Session依賴服務(wù)端存儲,多服務(wù)器部署時(shí)需同步會話(如用Redis);Cookie受同源策略限制,跨域名無法共享——這也是后續(xù)引入Token的原因。

4. Token vs JWT:無狀態(tài)身份憑證

為解決Session的“服務(wù)端存儲依賴”,無狀態(tài)的Token應(yīng)運(yùn)而生,而JWT是Token的一種標(biāo)準(zhǔn)化實(shí)現(xiàn)。

  • Token:加密字符串,會話信息編碼在 Token 中,服務(wù)器無需存Session,僅解析 Token 即可驗(yàn)證身份(如訪問令牌、刷新令牌)。
類型作用核心流程
訪問令牌授權(quán)訪問特定資源/操作(有效期短)登錄請求→服務(wù)器驗(yàn)證→發(fā)令牌→客戶端存令牌→請求帶令牌→服務(wù)器驗(yàn)證
刷新令牌刷新過期訪問令牌(有效期長,免重登)訪問令牌過期→帶刷新令牌求新令牌→服務(wù)器驗(yàn)證后發(fā)新令牌
  • JWT:遵循RFC 7519標(biāo)準(zhǔn),基于JSON結(jié)構(gòu),自身加密存儲用戶信息(如用戶ID、角色),服務(wù)器解密后直接驗(yàn)證,無需查數(shù)據(jù)庫,效率更高。

JWT核心優(yōu)勢:服務(wù)端無狀態(tài)、支持跨域、驗(yàn)證效率高,是當(dāng)前分布式應(yīng)用的首選身份憑證。

二、登錄注冊與Passport+JWT的關(guān)系

1. 核心職責(zé)劃分

  • 注冊:用戶提交賬號密碼,系統(tǒng)驗(yàn)證合法性后創(chuàng)建用戶記錄(存數(shù)據(jù)庫)
  • 登錄:用戶提交賬號密碼,系統(tǒng)驗(yàn)證通過后生成 JWT
    令牌(身份憑證)
  • Passport+JWT:攔截請求,驗(yàn)證 JWT 令牌有效性,確認(rèn)用戶身份并賦予訪問權(quán)限

2. 完整流程可視化

flowchart LR
    A[用戶] -->|1. 注冊| B[提交賬號密碼]
    B --> C{驗(yàn)證合法性
(用戶名唯一等)}
    C -->|合法| D[創(chuàng)建用戶記錄
(密碼加密存儲)]
    A -->|2. 登錄| E[提交賬號密碼]
    E --> F{驗(yàn)證賬號密碼
(與數(shù)據(jù)庫比對)}
    F -->|通過| G[生成JWT令牌
(包含用戶ID等信息)]
    A -->|3. 訪問受保護(hù)接口| H[請求頭攜帶JWT]
    H --> I[Passport攔截請求]
    I --> J[JWT策略驗(yàn)證令牌]
    J -->|有效| K[解析用戶信息
(附在req.user)]
    K --> L[接口處理邏輯]
    J -->|無效| M[返回401未授權(quán)]

三、代碼實(shí)現(xiàn)

Passport.js是Node.js生態(tài)最流行的身份驗(yàn)證中間件,支持“本地賬號密碼”“OAuth”“JWT”等多種策略。我們以NestJS(Node.js框架)為例,一步步實(shí)現(xiàn)JWT認(rèn)證。

1. 核心原理

Passport.js基于“策略模式”擴(kuò)展驗(yàn)證方式:

  • passport-local 策略處理“賬號密碼登錄”,驗(yàn)證通過后生成JWT;
  • passport-jwt 策略處理“接口訪問驗(yàn)證”,解析請求頭中的JWT,確認(rèn)用戶身份。

2. 安裝依賴

# 核心依賴:Passport本地策略、JWT策略、Nest集成包、加密
pnpm add @nestjs/passport passport passport-local @nestjs/jwt passport-jwt bcryptjs
# 類型定義(開發(fā)依賴)
pnpm add -D @types/passport-local @types/passport-jwt @types/bcryptjs

3. 實(shí)現(xiàn)注冊功能

注冊核心是合法驗(yàn)證密碼加密,避免明文存儲密碼導(dǎo)致安全風(fēng)險(xiǎn)。注冊生成的加密用戶記錄是登錄驗(yàn)證的唯一依據(jù)——沒有注冊的用戶無法通過登錄獲取JWT令牌。

// src/auth/auth.controller.ts
import { Controller, Post, Body, HttpException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { genSalt, hash } from 'bcryptjs';
import { User } from '../users/entities/user.entity'; // 用戶實(shí)體(含id/username/password/role)

@Controller('auth')
export class AuthController {
  constructor(
    @InjectRepository(User) private userRepo: Repository<User>,
  ) {}

  // 注冊接口
  @Post('register')
  async register(
    @Body() dto: { username: string; password: string },
  ) {
    // 1. 驗(yàn)證用戶名是否已存在(防重復(fù)注冊)
    const existUser = await this.userRepo.findOne({
      where: { username: dto.username },
    });
    if (existUser) {
      throw new HttpException('用戶名已存在', 400);
    }

    // 2. 密碼加密(核心!bcrypt自動加鹽,無需手動處理)
    const salt = await genSalt(10); // 生成鹽值(復(fù)雜度10)
    const hashedPassword = await hash(dto.password, salt);

    // 3. 創(chuàng)建用戶記錄(存入數(shù)據(jù)庫,角色默認(rèn)為普通用戶)
    await this.userRepo.save({
      username: dto.username,
      password: hashedPassword, // 存儲加密后的密碼
      role: 'user', // 初始角色(后續(xù)可通過管理員修改)
    });

    return { message: '注冊成功,請登錄' };
  }
}

4.實(shí)現(xiàn)登錄功能(本地策略)

登錄核心是驗(yàn)證用戶合法性生成JWT令牌,令牌將作為后續(xù)訪問受保護(hù)接口的“通行證”。

4.1 配置Local策略(賬號密碼驗(yàn)證)

先定義 LocalStrategy,校驗(yàn)用戶輸入的賬號密碼是否正確(密碼需用bcrypt加密存儲,避免明文)。

// src/auth/local.strategy.ts
import { BadRequestException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { InjectRepository } from '@nestjs/typeorm';
import { compareSync } from 'bcryptjs'; // 密碼比對(bcrypt加密)
import { Strategy } from 'passport-local';
import { Repository } from 'typeorm';
import { User } from '../users/entities/user.entity'; // 用戶實(shí)體

// 繼承PassportStrategy,指定策略類型為local
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(
    @InjectRepository(User) private userRepo: Repository<User>,
  ) {
    super({
      usernameField: 'username', // 對應(yīng)請求體中的“用戶名”字段
      passwordField: 'password', // 對應(yīng)請求體中的“密碼”字段
    });
  }

  // 驗(yàn)證邏輯:Passport會自動調(diào)用該方法
  async validate(username: string, password: string) {
    // 1. 根據(jù)用戶名查用戶(需額外查詢密碼,默認(rèn)不返回)
    const user = await this.userRepo
      .createQueryBuilder('user')
      .addSelect('user.password') // 顯式查詢密碼
      .where('user.username = :username', { username })
      .getOne();

    // 2. 校驗(yàn)用戶是否存在、密碼是否匹配
    if (!user || !compareSync(password, user.password)) {
      throw new BadRequestException('用戶名或密碼錯(cuò)誤');
    }

    // 3. 返回用戶信息(后續(xù)會被注入到req.user中)
    return user;
  }
}

4.2 配置JWT模塊

登錄驗(yàn)證通過后,需要生成JWT給客戶端,后續(xù)接口訪問需攜帶該令牌。在 AuthModule 中注冊JWT模塊,指定密鑰(從配置文件讀取,避免硬編碼)和過期時(shí)間:

// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from '../users/entities/user.entity';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
import { JwtStrategy } from './jwt.strategy';

// 異步配置JWT(從環(huán)境變量讀取密鑰)
const jwtModule = JwtModule.registerAsync({
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    secret: config.get('JWT_SECRET', 'dev-secret-123'), // 密鑰(生產(chǎn)環(huán)境需復(fù)雜)
    signOptions: { expiresIn: '4h' }, // JWT過期時(shí)間(4小時(shí))
  }),
});

@Module({
  imports: [
    TypeOrmModule.forFeature([User]), // 導(dǎo)入用戶Repository
    PassportModule,
    jwtModule, // 注冊JWT模塊
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy], // 注入服務(wù)和策略
  exports: [AuthService, JwtModule], // 導(dǎo)出JWT模塊,供其他模塊使用
})
export class AuthModule {}

4.3 實(shí)現(xiàn)JWT生成邏輯

AuthService 中,用 JwtService 生成JWT(Payload包含用戶ID、用戶名、角色,供后續(xù)權(quán)限判斷):

// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { User } from '../users/entities/user.entity';

@Injectable()
export class AuthService {
  constructor(private jwtService: JwtService) {}

  // 生成JWT(登錄成功后調(diào)用)
  login(user: Partial<User>) {
    // Payload:存儲用戶核心信息(避免敏感數(shù)據(jù),如密碼)
    const payload = { 
      id: user.id, 
      username: user.username, 
      role: user.role // 角色信息,后續(xù)RBAC權(quán)限控制用
    };
    // 簽名生成JWT令牌
    const token = this.jwtService.sign(payload);
    return { token }; // 返回給客戶端
  }
}

4.4 實(shí)現(xiàn)登錄接口

AuthGuard('local') 觸發(fā)Local策略驗(yàn)證,驗(yàn)證通過后調(diào)用 login 生成JWT:

// src/auth/auth.controller.ts
import { Controller, Post, Req, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  // 登錄接口:使用local策略驗(yàn)證
  @UseGuards(AuthGuard('local'))
  @Post('login')
  login(@Req() req) {
    // req.user由LocalStrategy的validate方法返回
    return this.authService.login(req.user);
  }
}

5. Passport+JWT實(shí)現(xiàn)接口驗(yàn)證(身份確認(rèn))

客戶端登錄后,需在請求頭攜帶 Authorization: Bearer <JWT>,我們用Passport的 passport-jwt 策略負(fù)責(zé)攔截請求、驗(yàn)證令牌有效性,確認(rèn)用戶身份。

5.1 定義JWT策略

// src/auth/jwt.strategy.ts
import { UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { User } from '../users/entities/user.entity';
import { AuthService } from './auth.service';

export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    private config: ConfigService,
    private authService: AuthService,
  ) {
    super({
      // 從請求頭的Authorization: Bearer <JWT>中提取令牌
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: config.get('JWT_SECRET'), // 用相同密鑰解密
      ignoreExpiration: false, // 不忽略過期(過期則返回401)
    });
  }

  // 驗(yàn)證JWT有效性(解密后調(diào)用)
  async validate(payload: Partial<User>) {
    // 可額外校驗(yàn)用戶狀態(tài)(如是否被禁用)
    const user = await this.authService.findUserById(payload.id);
    if (!user) {
      throw new UnauthorizedException('令牌無效或用戶不存在');
    }
    return user; // 返回用戶信息,注入到req.user
  }
}

5.2 封裝JWT守衛(wèi)(統(tǒng)一錯(cuò)誤處理)

自定義 JwtAuthGuard,統(tǒng)一處理“未登錄”錯(cuò)誤提示,避免重復(fù)代碼:

// src/auth/jwt-auth.guard.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  // 處理驗(yàn)證結(jié)果
  handleRequest(err: any, user: any) {
    if (err || !user) {
      throw new UnauthorizedException('請先登錄');
    }
    return user;
  }
}

5.3 保護(hù)接口

在需要登錄的接口上使用 JwtAuthGuard,未攜帶有效JWT會被攔截:

// src/users/users.controller.ts
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';

@Controller('users')
export class UsersController {
  // 需登錄才能訪問的接口:獲取當(dāng)前用戶信息
  @UseGuards(JwtAuthGuard)
  @Get('info')
  getUserInfo(@Req() req) {
    // req.user由JwtStrategy的validate方法返回
    return req.user;
  }
}

6. RBAC 權(quán)限控制

6.1 RolesGuard 實(shí)現(xiàn)

自定義RolesGuard,校驗(yàn)用戶角色是否符合接口要求,實(shí)現(xiàn)權(quán)限細(xì)化:

// src/auth/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private requiredRoles: string[]) {} // 接收需要的角色列表

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const user = request.user; // 從req.user中獲取用戶角色(JWT驗(yàn)證后注入)

    // 校驗(yàn)用戶角色是否在允許的列表中
    const hasPermission = this.requiredRoles.some(role => user.role === role);
    if (!hasPermission) {
      throw new ForbiddenException('無權(quán)限訪問,需管理員權(quán)限');
    }
    return true;
  }
}

// 自定義裝飾器:簡化角色配置(如@Roles('admin'))
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

6.2 接口示例

// src/admin/admin.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard, Roles } from '../auth/roles.guard';

@Controller('admin')
@UseGuards(JwtAuthGuard) // 先驗(yàn)證登錄狀態(tài)
export class AdminController {
  // 僅角色為admin的用戶可訪問:查詢所有用戶(管理員權(quán)限)
  @Get('all-users')
  @Roles('admin') // 指定需要的角色
  @UseGuards(new RolesGuard(['admin'])) // 應(yīng)用角色守衛(wèi)
  getAllUsers() {
    // 業(yè)務(wù)邏輯:查詢數(shù)據(jù)庫所有用戶信息
    return { data: [], message: '所有用戶列表' };
  }
}

7.JWT過期處理:避免頻繁重新登錄

JWT一旦生成無法主動作廢,若有效期過短會影響用戶體驗(yàn),過長則增加安全風(fēng)險(xiǎn)。解決方案是**“訪問令牌+刷新令牌”雙令牌機(jī)制**:

令牌類型有效期作用安全策略
訪問令牌短(如30分鐘)訪問受保護(hù)接口(核心憑證)過期后用刷新令牌獲取新令牌
刷新令牌長(如7天)僅用于刷新訪問令牌(無接口訪問權(quán)限)存儲在Redis中,支持主動作廢(如登出)

實(shí)現(xiàn)邏輯:

  1. 登錄時(shí)同時(shí)生成訪問令牌(accessToken)和刷新令牌(refreshToken);
  2. 刷新令牌存儲在Redis中(鍵:refreshToken,值:用戶ID+過期時(shí)間);
  3. 訪問令牌過期時(shí),客戶端攜帶刷新令牌請求/auth/refresh-token接口;
  4. 服務(wù)器驗(yàn)證刷新令牌(Redis中是否存在、是否過期),驗(yàn)證通過則生成新的訪問令牌。
// src/auth/auth.service.ts(新增刷新令牌邏輯)
import { InjectRedis } from '@nestjs-modules/ioredis';
import { Redis } from 'ioredis';
import { v4 as uuidv4 } from 'uuid'; // 生成唯一刷新令牌

@Injectable()
export class AuthService {
  // ... 其他代碼 ...

  // 登錄時(shí)生成雙令牌
  async loginWithDoubleToken(user: Partial<User>) {
    // 1. 生成訪問令牌(短有效期)
    const accessToken = this.jwtService.sign(
      { sub: user.id, username: user.username, role: user.role },
      { expiresIn: '30m' },
    );

    // 2. 生成刷新令牌(唯一ID,長有效期)
    const refreshToken = uuidv4();
    const refreshTokenExpire = 7 * 24 * 60 * 60; // 7天(秒)

    // 3. 刷新令牌存儲到Redis(支持主動作廢)
    await this.redis.set(
      `refresh_token:${refreshToken}`,
      user.id,
      'EX',
      refreshTokenExpire,
    );

    return {
      accessToken,
      refreshToken,
      expiresIn: 30 * 60, // 訪問令牌有效期(秒)
    };
  }

  // 刷新訪問令牌
  async refreshToken(refreshToken: string) {
    // 1. 驗(yàn)證刷新令牌是否存在于Redis
    const userId = await this.redis.get(`refresh_token:${refreshToken}`);
    if (!userId) {
      throw new UnauthorizedException('刷新令牌無效或已過期');
    }

    // 2. 查詢用戶信息
    const user = await this.findUserById(Number(userId));
    if (!user) {
      throw new UnauthorizedException('用戶不存在');
    }

    // 3. 生成新的訪問令牌
    const newAccessToken = this.jwtService.sign(
      { sub: user.id, username: user.username, role: user.role },
      { expiresIn: '30m' },
    );

    return { accessToken: newAccessToken, expiresIn: 30 * 60 };
  }

  // 登出:刪除Redis中的刷新令牌(主動作廢)
  async logout(refreshToken: string) {
    await this.redis.del(`refresh_token:${refreshToken}`);
    return { message: '登出成功' };
  }
}

四、關(guān)鍵安全與最佳實(shí)踐

身份管理系統(tǒng)的安全性直接決定應(yīng)用安全,需嚴(yán)格遵循以下最佳實(shí)踐:

1. 密碼安全:從存儲到傳輸全鏈路防護(hù)

  • 存儲加密:必須使用bcrypt、Argon2等自適應(yīng)哈希算法加密密碼,禁止明文或MD5等弱哈希存儲;
  • 傳輸加密:所有登錄注冊接口必須使用HTTPS,防止密碼在傳輸過程中被截??;
  • 強(qiáng)度校驗(yàn):注冊時(shí)強(qiáng)制密碼復(fù)雜度(如8位以上+字母+數(shù)字+特殊符號),避免弱密碼。

2. JWT安全:避免令牌泄露與篡改

  • 密鑰管理:JWT密鑰需復(fù)雜(如32位以上隨機(jī)字符串),生產(chǎn)環(huán)境中存儲在環(huán)境變量或密鑰管理服務(wù)(如AWS KMS),禁止硬編碼;
  • 令牌存儲:前端優(yōu)先將JWT存儲在httpOnly Cookie(防XSS攻擊),若用localStorage需額外做XSS防護(hù)(如輸入過濾、CSP策略);
  • Payload控制:JWT的Payload僅存儲非敏感信息(如用戶ID、角色),禁止包含密碼、手機(jī)號等敏感數(shù)據(jù)。

3. 接口安全:防御常見攻擊

  • 防CSRF攻擊:使用httpOnly Cookie存儲令牌時(shí),需配合CSRF Token驗(yàn)證;
  • 限流防護(hù):登錄、注冊接口添加限流(如1分鐘內(nèi)最多5次請求),防止暴力給破解;
  • 異常處理:統(tǒng)一錯(cuò)誤提示(如“賬號或密碼錯(cuò)誤”而非“用戶名不存在”),避免泄露用戶存在性信息。

4. 權(quán)限最小化:避免過度授權(quán)

  • 基于RBAC模型嚴(yán)格劃分角色(如user、admin、super_admin),每個(gè)角色僅賦予必要權(quán)限;
  • 敏感操作(如修改密碼、刪除數(shù)據(jù))需二次驗(yàn)證(如短信驗(yàn)證碼、當(dāng)前密碼確認(rèn))。

總結(jié) 

到此這篇關(guān)于JWT+Passport.js身份驗(yàn)證實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)JWT+Passport.js身份驗(yàn)證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解js location.href和window.open的幾種用法和區(qū)別

    詳解js location.href和window.open的幾種用法和區(qū)別

    這篇文章主要介紹了詳解js location.href和window.open的幾種用法和區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • JS格式化數(shù)字保留兩位小數(shù)點(diǎn)示例代碼

    JS格式化數(shù)字保留兩位小數(shù)點(diǎn)示例代碼

    式化數(shù)字保留兩位小數(shù)點(diǎn)實(shí)現(xiàn)的方法有很多,在接下來的文章中將為大家詳細(xì)介紹下如何使用js來實(shí)現(xiàn)
    2013-10-10
  • javascript的currying函數(shù)介紹

    javascript的currying函數(shù)介紹

    curring的概念將函數(shù)式編程的概念和默認(rèn)參數(shù)以及可變參數(shù)結(jié)合在一起.一個(gè)帶n個(gè)參數(shù),curried的函數(shù)固化第一個(gè)參數(shù)為固定參數(shù),并返回另一個(gè)帶n-1個(gè)參數(shù)的函數(shù)對象,分別類似于LISP的原始函數(shù)car和cdr的行為。currying能泛化為偏函數(shù)應(yīng)用(partial function application, PFA),p 這種函數(shù)將任意數(shù)量(順序)的參數(shù)的函數(shù)轉(zhuǎn)化為另一個(gè)帶剩余參數(shù)的函數(shù)對象
    2012-02-02
  • JavaScript使用Replace進(jìn)行字符串替換的方法

    JavaScript使用Replace進(jìn)行字符串替換的方法

    這篇文章主要介紹了JavaScript使用Replace進(jìn)行字符串替換的方法,涉及Replace進(jìn)行一次替換與全部替換的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • shiro授權(quán)的實(shí)現(xiàn)原理

    shiro授權(quán)的實(shí)現(xiàn)原理

    這篇文章主要介紹了shiro授權(quán)的實(shí)現(xiàn)原理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • JavaScript實(shí)現(xiàn)事件的中斷傳播和行為阻止方法示例

    JavaScript實(shí)現(xiàn)事件的中斷傳播和行為阻止方法示例

    這篇文章主要給大家介紹了利用JavaScript實(shí)現(xiàn)事件的中斷傳播和行為阻止的方法示例,文中給出了詳細(xì)的介紹和示例代碼,相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,需要的朋友們下面來一起看看吧。
    2017-01-01
  • Bootstrap模態(tài)框插件使用詳解

    Bootstrap模態(tài)框插件使用詳解

    這篇文章主要為大家詳細(xì)介紹了Bootstrap模態(tài)框插件的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • JS實(shí)現(xiàn)超簡單的仿QQ折疊菜單效果

    JS實(shí)現(xiàn)超簡單的仿QQ折疊菜單效果

    這篇文章主要介紹了JS實(shí)現(xiàn)超簡單的仿QQ折疊菜單效果,可實(shí)現(xiàn)鼠標(biāo)滑過列表展開的QQ折疊菜單效果,非常簡單實(shí)用,需要的朋友可以參考下
    2015-09-09
  • 基于Day.js更優(yōu)雅的處理JavaScript中的日期

    基于Day.js更優(yōu)雅的處理JavaScript中的日期

    Day.js它能夠幫助我們處理JavaScript中的日期,本文就詳細(xì)的介紹一下Day.js的具體使用,可以更簡單的處理JavaScript中的日期和時(shí)間
    2021-09-09
  • JavaScript動態(tài)創(chuàng)建div屬性和樣式示例代碼

    JavaScript動態(tài)創(chuàng)建div屬性和樣式示例代碼

    動態(tài)創(chuàng)建div屬性和樣式在某些情況下還是比較實(shí)用的,下面為大家詳細(xì)介紹下js中div屬性和樣式的動態(tài)創(chuàng)建,感興趣的朋友可以參考下
    2013-10-10

最新評論

静宁县| 鲁山县| 西藏| 崇仁县| 西平县| 蓝田县| 张家界市| 岗巴县| 台东市| 高淳县| 洪洞县| 墨玉县| 偃师市| 义马市| 大港区| 隆子县| 高密市| 封丘县| 桂平市| 社旗县| 长宁区| 威宁| 浠水县| 平潭县| 新邵县| 绵竹市| 资中县| 广丰县| 峨山| 枞阳县| 涿州市| 西吉县| 开阳县| 丹江口市| 辽源市| 磴口县| 吉首市| 万全县| 潼关县| 慈溪市| 南华县|