前期准备:申请小程序并获得appid、secret
一、通过小程序获取code和iv以及encryptedData
以下为小程序代码
// template
<template>
<button class="confirm" @tap="actionHandle">登录</button>
</template>
//js
<script setup lang="ts">
import { reactive } from 'vue';
const state = reactive({
code: ''
})
async function actionHandle(e: any){
uni.showLoading({
title: '正在登录中',
mask: true
})
await login() // 一定要先获取code,并且code只能使用一次,一次后需要重新生成
uni.getUserProfile({
desc: 'Wexin', // 这个参数是必须的
success: res => {
const params = {
iv: res.iv,
encryptedData: res.encryptedData,
code: state.code
}
try{
uni.request({
url: 'http://localhost:3000/user/login',
data: params,
method: 'POST'
})
}catch(e){
console.log(e)
}
}
})
}
function login() {
uni.login().then(res => {
state.code = res.code
})
}
</script>
nest使用nest g resource user生成user模块,并连接数据库【nest简易上手教程】
二、login接口接收数据并解密用户数据
建立一张简陋的user表
// src/User/entities
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@CreateDateColumn()
createTime: Date;
@UpdateDateColumn()
updateTime: Date;
@Column('text')
openid: string
@Column({
default: false,
})
isDelete: false;
@Column('text')
nickname: string;
@Column('text')
avatar: string;
@Column()
gender: number;
@Column('text', { default: null })
mobile: string;
@Column('text')
appid: string
@Column('text', { default: null })
token: string;
}
// src/User/dto/login.dto.ts
import { IsNotEmpty } from 'class-validator';
export class LoginDTO {
readonly iv: string;
readonly encryptedData: string;
@IsNotEmpty({ message: 'code不能为空' })
readonly code: string;
}
// src/User/user.controller.ts
@Post('/login')
login(@Body() loginDTO: LoginDTO) {
return this.userService.login(loginDTO);
}
建完保存就会发现数据库中自动生成了一张user表
三、获取session_key,生成token,加入jwt
yarn add @nestjs/passport passport passport-local @nestjs/jwt passport-jwt
yarn add -D @types/passport-local @types/passport-jwt
// src/customer/user.module.ts
import { Module } from '@nestjs/common';
import {TypeOrmModule} from '@nestjs/typeorm'
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { User } from './entities/user.entity';
import {HttpModule} from '@nestjs/axios'
import { JwtModule } from '@nestjs/jwt'
@Module({
imports: [
TypeOrmModule.forFeature([User]),
HttpModule,
JwtModule.register({
secret: 'dsdgfgjhjk', // 密钥
signOptions: {expiresIn: '8h'} // 过期时间
})
],
controllers: [UserController],
providers: [UserService]
})
export class UserModule {}
//src/customer/customer.controller.ts
@ApiOkResponse({ description: '登陆', type: TokenResponse })
@Post('/login')
login(@Body() loginDTO: LoginDTO) {
return this.customerService.login(loginDTO);
}
接下来是写逻辑的地方
// src/customer/user.service.ts
import { Injectable, HttpException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { HttpService } from '@nestjs/axios';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { LoginDTO } from './dto/login.dto';
import {AxiosResponse} from 'axios'
import { map } from 'rxjs/operators';
import {WXBizDataCrypt} from './WXBizDataCrypt'
import { User } from './entities/user.entity';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
private readonly httpService: HttpService,
private readonly jwtService: JwtService
){}
private appid = '你的appid'
private secret = '微信公众平台获取的secret'
private grant_type = 'authorization_code'
async login(loginDTO: LoginDTO): Promise<any>{
const {code, iv, encryptedData} = loginDTO
const url = `https://api.weixin.qq.com/sns/jscode2session?grant_type=${this.grant_type}&appid=${this.appid}&secret=${this.secret}&js_code=${code}`
const info = await this.getInfo(url) // 获取openid和session_key
let token = ''
// 如果openid不存在则为新用户
const hasUser = await this.userRepository.findOne({ where: { openid: info.data.openid } });
if (hasUser) {
// 直接取用户token
token = hasUser.token
} else {
// 注册插入一条新信息
const pc = new WXBizDataCrypt(this.appid, info.data?.session_key)
const data = pc.decryptData(encryptedData, iv)
const newUser: User = new User();
newUser.openid = info.data.openid
newUser.nickname = data.nickName
newUser.appid = this.appid
newUser.avatar = data.avatarUrl
newUser.gender = data.gender
token = await this.certificate(newUser)
newUser.token = token
await this.userRepository.save(newUser);
}
return {
token
}
}
// 生成 token
async certificate(user: User) {
const payload = {
openid: user.openid,
nickname: user.nickname,
};
const token = this.jwtService.sign(payload);
return token
}
getInfo(url): Promise<AxiosResponse> {
return this.httpService.post(url).pipe(map(response => response)).toPromise()
}
小程序调用login接口获得token存在本地,或者用postman试一下