feat(csms): 添加 OCPP 鉴权

This commit is contained in:
2026-03-16 16:53:33 +08:00
parent 4885cf6778
commit cf0861f8f6
8 changed files with 328 additions and 21 deletions

View File

@@ -8,6 +8,10 @@ import { logger } from 'hono/logger'
import { showRoutes } from 'hono/dev'
import { auth } from './lib/auth.ts'
import { createOcppHandler } from './ocpp/handler.ts'
import { verifyOcppPassword } from './lib/ocpp-auth.ts'
import { useDrizzle } from './lib/db.ts'
import { chargePoint } from './db/schema.ts'
import { eq } from 'drizzle-orm'
import statsRoutes from './routes/stats.ts'
import statsChartRoutes from './routes/stats-chart.ts'
import chargePointRoutes from './routes/charge-points.ts'
@@ -83,11 +87,45 @@ app.get('/api', (c) => {
app.get(
'/ocpp/:chargePointId',
async (c, next) => {
const chargePointId = c.req.param('chargePointId')
const authHeader = c.req.header('Authorization')
if (!authHeader?.startsWith('Basic ')) {
c.header('WWW-Authenticate', 'Basic realm="OCPP"')
return c.json({ error: 'Unauthorized' }, 401)
}
let id: string, password: string
try {
const decoded = atob(authHeader.slice(6))
const colonIdx = decoded.indexOf(':')
if (colonIdx === -1) throw new Error('Invalid format')
id = decoded.slice(0, colonIdx)
password = decoded.slice(colonIdx + 1)
} catch {
return c.json({ error: 'Invalid Authorization header' }, 400)
}
if (id !== chargePointId) {
return c.json({ error: 'Unauthorized' }, 401)
}
const db = useDrizzle()
const [cp] = await db
.select({ passwordHash: chargePoint.passwordHash })
.from(chargePoint)
.where(eq(chargePoint.chargePointIdentifier, chargePointId))
.limit(1)
if (!cp?.passwordHash || !(await verifyOcppPassword(password, cp.passwordHash))) {
return c.json({ error: 'Unauthorized' }, 401)
}
await next()
},
upgradeWebSocket((c) => {
const chargePointId = c.req.param('chargePointId')
if (!chargePointId) {
throw new Error('Missing chargePointId parameter')
}
const connInfo = getConnInfo(c)
return createOcppHandler(chargePointId, connInfo.remote.address)
}),