Banco de Dados e Autenticação
Aula 4 de 7
ORM/ODM
Prisma (SQL)
npm install prisma @prisma/client
npx prisma init
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
password String
role Role @default(USER)
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}
enum Role {
USER
ADMIN
}
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
async function main() {
// Criar
const user = await prisma.user.create({
data: {
email: '[email protected]',
name: 'Alice',
password: 'hash123'
}
});
// Buscar com relação
const users = await prisma.user.findMany({
where: { role: 'ADMIN' },
include: { posts: true },
orderBy: { createdAt: 'desc' }
});
// Atualizar
await prisma.user.update({
where: { id: 1 },
data: { name: 'Alice Updated' }
});
// Paginação
const page = await prisma.user.findMany({
skip: 0,
take: 10,
cursor: { id: 1 }
});
}
main().finally(() => prisma.$disconnect());
Migrations
npx prisma migrate dev --name init
npx prisma migrate deploy
npx prisma db push
npx prisma studio
Sequelize
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize(process.env.DATABASE_URL);
const User = sequelize.define('User', {
email: { type: DataTypes.STRING, unique: true, allowNull: false },
name: { type: DataTypes.STRING, allowNull: false },
password: { type: DataTypes.STRING, allowNull: false },
role: { type: DataTypes.ENUM('USER', 'ADMIN'), defaultValue: 'USER' }
});
const Post = sequelize.define('Post', {
title: { type: DataTypes.STRING, allowNull: false },
content: { type: DataTypes.TEXT },
published: { type: DataTypes.BOOLEAN, defaultValue: false }
});
User.hasMany(Post, { foreignKey: 'authorId' });
Post.belongsTo(User, { foreignKey: 'authorId' });
await sequelize.sync({ alter: true });
Mongoose (MongoDB)
const mongoose = require('mongoose');
await mongoose.connect(process.env.MONGODB_URI);
const userSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
name: { type: String, required: true },
password: { type: String, required: true },
role: { type: String, enum: ['USER', 'ADMIN'], default: 'USER' }
}, { timestamps: true });
const User = mongoose.model('User', userSchema);
const user = await User.create({
email: '[email protected]',
name: 'Alice',
password: 'hash123'
});
const users = await User.find({ role: 'ADMIN' }).populate('posts');
JWT (JSON Web Token)
npm install jsonwebtoken bcrypt
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const SECRET = process.env.JWT_SECRET || 'super-secret';
// Hash de senha
async function hashPassword(password) {
const salt = await bcrypt.genSalt(12);
return bcrypt.hash(password, salt);
}
// Comparar senha
async function comparePassword(password, hash) {
return bcrypt.compare(password, hash);
}
// Gerar token
function generateToken(payload) {
return jwt.sign(payload, SECRET, { expiresIn: '15m' });
}
// Gerar refresh token
function generateRefreshToken(payload) {
return jwt.sign(payload, SECRET + '-refresh', { expiresIn: '7d' });
}
// Verificar token
function verifyToken(token) {
return jwt.verify(token, SECRET);
}
Login e Refresh Token
const express = require('express');
const router = express.Router();
router.post('/login', async (req, res) => {
const { email, password } = req.body;
const user = await prisma.user.findUnique({ where: { email } });
if (!user) {
return res.status(401).json({ error: 'Credenciais inválidas' });
}
const valid = await comparePassword(password, user.password);
if (!valid) {
return res.status(401).json({ error: 'Credenciais inválidas' });
}
const payload = { userId: user.id, role: user.role };
const token = generateToken(payload);
const refreshToken = generateRefreshToken(payload);
res.json({ token, refreshToken, user: { id: user.id, name: user.name, email: user.email } });
});
router.post('/refresh', (req, res) => {
const { refreshToken } = req.body;
try {
const decoded = jwt.verify(refreshToken, SECRET + '-refresh');
const newToken = generateToken({ userId: decoded.userId, role: decoded.role });
res.json({ token: newToken });
} catch {
res.status(401).json({ error: 'Refresh token inválido' });
}
});
RBAC Middleware
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ error: 'Token não fornecido' });
}
const token = authHeader.split(' ')[1]; // Bearer <token>
try {
const decoded = verifyToken(token);
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ error: 'Token inválido ou expirado' });
}
}
function requireRole(...roles) {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Acesso não autorizado' });
}
next();
};
}
router.get('/admin/users', authMiddleware, requireRole('ADMIN'), async (req, res) => {
const users = await prisma.user.findMany();
res.json(users);
});
router.put('/posts/:id', authMiddleware, async (req, res) => {
const post = await prisma.post.findUnique({ where: { id: Number(req.params.id) } });
if (!post) return res.status(404).json({ error: 'Post não encontrado' });
// Verificar se é o autor ou admin
if (post.authorId !== req.user.userId && req.user.role !== 'ADMIN') {
return res.status(403).json({ error: 'Sem permissão' });
}
const updated = await prisma.post.update({
where: { id: post.id },
data: req.body
});
res.json(updated);
});
Lab: Exercício - API com Autenticação e RBAC
// index.js
const express = require('express');
const prisma = require('./prisma');
const authRoutes = require('./routes/auth');
const userRoutes = require('./routes/users');
const { authMiddleware } = require('./middleware/auth');
const app = express();
app.use(express.json());
app.use('/auth', authRoutes);
app.use('/users', authMiddleware, userRoutes);
app.listen(3000);
# Testar fluxo completo
curl -X POST http://localhost:3000/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"123456","name":"Admin"}'
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"123456"}'
# Usar token nas requisições
TOKEN="eyJhbGciOiJIUzI1NiIs..."
curl http://localhost:3000/users \
-H "Authorization: Bearer $TOKEN"
Sempre use bcrypt com salt rounds >= 12 para hash de senhas. Tokens JWT devem ter expiração curta (15min) combinados com refresh tokens de longa duração (7d). Prisma é a escolha moderna para ORM com type-safety e migrations.