λλ Feathers Crow λ‘λ맡 μ λ°μ΄νΈ μμ μ΄λ―Έ μ΄κ²μ λν΄ μ‘°κΈ μΌμ΅λλ€. μ΄ λ¬Έμ λ λ€μ λ²μ μ Feathers μΈμ¦μμ λ€λ£° λͺ¨λ κ΄λ ¨ λ¬Έμ λ₯Ό μμ§νκΈ° μν κ²μ λλ€.
νμ¬(Buzzard) 릴리μ€μμ Feathers μ½μ΄λ μμ ν νλ μμν¬ λ λ¦½μ΄ λμμ§λ§ μΈμ¦ νλ¬κ·ΈμΈμ μ¬μ ν ββμΌλΆ Express λ―Έλ€μ¨μ΄λ₯Ό λ±λ‘νκ³ μμ΅λλ€. λ€μ λ²μ μμλ λ―Έλ€μ¨μ΄κ° @feathersjs/expressλ‘ μ΄λνκ³ μΈμ¦ μ½μ΄λ νλ μμν¬ λ 립μ μΌλ‘ λ§λ€ κ²μ λλ€. μ΄κ²μ Koa λλ MQTTμ κ°μ μλ‘μ΄ μ μ‘μ νμ©ν©λλ€.
νμ¬ μΉ μμΌμ μ¬μ©μ μ μ μ΄λ²€νΈ λ©μ»€λμ¦μ ν΅ν΄ μΈμ¦λκ³ λ‘κ·Έμμλ©λλ€. μ΄λ‘ μΈν΄ μΌλΆ λ¬Έμ (μ: λλ¬Έ "μΈμ¦ ν ν° μμ" μ€λ₯)κ° λ°μνμΌλ©° νΉν μλ²μ ν΄λΌμ΄μΈνΈ μμͺ½μμ μμ μ μΈ μ¬μ°κ²°κ³Ό κ΄λ ¨νμ¬ λ¬Έμ κ° λ°μνμ΅λλ€.
μ΄λ κ² νλ©΄ μν μ μ₯ μ‘μΈμ€ ν ν°μ λν μ’ μμ±μ΄ μ κ±°λκ³ μμΌ μ¬μΈμ¦μ΄ μ μμ μΌλ‘ μ²λ¦¬λ©λλ€.
Feathersλ μΈμ¦μ μν΄ JWTλ₯Ό μ¬μ©νμ¬ APIλ₯Ό μμ±νκΈ° μν λΌμ΄λΈλ¬λ¦¬μ λλ€. μΌλ°μ μΈ Feathers μ€μ μ΄ μΏ ν€λ₯Ό μ¬μ©νλ ν κ°μ§ κ²½μ°λ oAuth(Facebook, Google λ±) μΈμ¦ νλ¦ νμ JWTλ₯Ό λΈλΌμ°μ μ μ λ¬νλ κ²μ λλ€. μλ‘μ΄ oAuth λ° μΈμ¦ ν΄λΌμ΄μΈνΈλ λ μ΄μ μΏ ν€λ₯Ό μ¬μ©νμ§ μμΌλ©° oAuth νλ¦μ ν λ²μ λͺ¨λ μμ μ μννλ λμ λ λΆλΆμΌλ‘ λΆν ν©λλ€. oAuth μ‘μΈμ€ ν ν°μ Feathers νμ€ μΈμ¦ λ©μ»€λμ¦μ μ¬μ©νμ¬ JWTλ‘ κ΅νν μ μλ λλ©μΈ κ° μΉνμ μΈ(λ‘컬μμλ§ μ‘μΈμ€ κ°λ₯ν) URL ν΄μλ‘ μ€μ λ©λλ€.
μ΄μ λͺ¨λ μΈμ¦ μ€μ μ΄ λ°νμμ νκ°λλ―λ‘ λμ μΌλ‘ μ€μ ν μ μμΌλ©° νμνμ§ μμ μ΅μ μ λν μ€λ₯κ° λ°μνμ§ μμ΅λλ€. λν μ¬μ©μ μ§μ μΈμ¦ μλΉμ€λ₯Ό ν΅κ³Όν μ μμ΅λλ€.
κΈ°μ‘΄ JWTλ‘ μλ‘ κ³ μΉ¨μ νμ©νλ λμ νμ€ μΈμ¦ μλΉμ€λ μ΄μ μλͺ μ΄ λ κΈ΄ μλ‘ κ³ μΉ¨ ν ν°λ λ°νν©λλ€. ν ν° λΈλ리μ€νΈλ μ¬μ ν μλμΌλ‘ ꡬνν΄μΌ νμ§λ§ μλ‘μ΄ μΈμ¦ μλΉμ€μ λ°©λ²μ ν΅ν΄ ν΅ν©νλ κ²μ΄ λ μ¬μΈ κ²μ λλ€.
μ΄ ν λ‘ μ #844μμ μμλμμΌλ©° https://github.com/feathersjs/feathers/issues/844#issuecomment -390123148μμ μ΄κ²μ΄ λ°μνλ μμ μ μμ½ν©λλ€. μ컨λ, νΉν Express μ΄μΈμ νλ μμν¬λ₯Ό μ§μνλ κ²κ³Ό κ΄λ ¨νμ¬ PassportJSμμ λ§μ κ°λ°μ΄ μ΄λ£¨μ΄μ§μ§ μμμΌλ©° Koa λλ Hapiμ κ°μ λλΆλΆμ λ€λ₯Έ HTTP νλ μμν¬λ https://github.com/simov/grantμ κ°μ λ³΄λ€ μ μ°νκ³ λ―Έλλ©ν μΈμ¦ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νλλ‘ μ΄λνμ΅λλ€. oAuthμ©). λν μ€μ λ‘ νμν μ λ΅μλ λ€ κ°μ§ μ νλ§ μμμ΄ λ°νμ‘μ΅λλ€.
Passportλ₯Ό ν΄κ²°ν νμ μμ΄ Feathersλ λ¬Έμ λ₯Ό λͺ ννκ² κ΅¬λΆνμ¬ HTTP λΌμ΄λΈλ¬λ¦¬ λ° κΈ°ν μ μ‘ λ©μ»€λμ¦(μ: MQTT λλ P2P μ°κ²°) μμ μ½κ² λ°°μΉν μ μμΌλ©° ν¨μ¬ λ μ½κ² μ΄ν΄νκ³ μ¬μ©μ μ μν μ μλ μΈμ¦ λ©μ»€λμ¦μ μ 곡ν μ μμ΅λλ€.
params.authentication
Feathers μΈ‘μμλ μλΉμ€ νΈμΆμμ params.authentication
λ₯Ό μ€μ νλ κ²μ΄ μΈμ¦ μ 보λ₯Ό μ 곡νλ _μ μΌν_ λ°©λ²μ
λλ€. params.authentication
μλ μλΉμ€ νΈμΆμ μΈμ¦νλ λ° νμν μ λ³΄κ° ν¬ν¨λλ©° μ΄λ―Έ μ¬μ© μ€μΈ { strategy: 'local', email: 'email', password: 'password' }
νμμ
λλ€.
// Call `find` with a given JWT
app.service('messages').find({
authentication: {
strategy: 'jwt',
accessToken: 'someJWT'
}
});
νΈμΆμ(μ: REST λλ websocket μ μ‘, νν¬ λλ λ¨μ ν
μ€νΈ)λ params.authentication
μ λ¬μ λ΄λΉν©λλ€. μ΄κ²μ authenticate
νν¬κ° λ΄λΆ νΈμΆμ λν΄ μ€νλλμ§ μ¬λΆ λλ μΈμ¦ μ λ³΄κ° μ€μ λ‘ μ΄λμμ μ€λμ§ λ μ΄μ νΌλμ΄ μμ κ²μμ μλ―Έν©λλ€.
authenticate
νν¬authenticate
νν¬λ κ³μ μ‘΄μ¬ν©λλ€. μ λ΅ μ΄λ¦ λͺ©λ‘μ΄ νμνλ©° λ€μ μ€ νλλ₯Ό μνν©λλ€.
params
λ³ν©ν©λλ€.λλ λͺ¨λ μ λ΅μ΄ μ€ν¨ν κ²½μ° μ€ν¨ν 첫 λ²μ§Έ μ λ΅μ μ€λ₯λ₯Ό λμ§λλ€.
params.authentication
μ strategy
μ΄λ¦μ΄ ν¬ν¨λ κ²½μ° ν΄λΉ μ λ΅λ§ νΈμΆλ©λλ€. κ·Έλ μ§ μμΌλ©΄ λͺ¨λ μ λ΅μ΄ νΈμΆλ©λλ€. params.authentication
κ° μ ν μ€μ λμ§ μμ κ²½μ° νν¬λ
μΈλΆ νΈμΆμ λν μ€λ₯ λ°μ
params.user
μλμΌλ‘ μ€μ νλ κ²½μ°).app.service('messages').hooks({
before: authenticate('jwt', 'local', 'anonymous')
});
κΈ°λ³Έ μΈμ¦ μ λ΅μ params.authentication
μ λ°μ΄ν°λ₯Ό κ°μ Έμ€κ³ μ±κ³΅ κ°μ²΄λ₯Ό λ°ννκ±°λ μ±κ³΅νμ§ λͺ»ν κ²½μ° μ€λ₯λ₯Ό throwνλ authenticate
λ©μλκ° μλ κ°μ²΄μ
λλ€.
const { Forbidden } = require('@feathersjs/errors');
const daveStrategy = {
async authenticate (authParams) {
const { username, password } = authParams;
if (username === 'david' && password === 'secret') {
return {
user: {
name: 'Dave',
admin: true
}
};
}
throw new Forbidden('Not super Dave');
}
};
app.authentication.registerStrategy('superdave', daveStrategy);
authenticate
νν¬μμ λ°νλ κ°μ²΄λ params
μλΉμ€ νΈμΆμ λ³ν©λλ―λ‘ μ΄ μμ μμλ params.user
ν©λλ€.
μΈμ¦ μ λ΅μ λ΄λΆμ μΌλ‘ μΆκ° λ©μλλ₯Ό ν¬ν¨νκ³ νΈμΆν μ μμ΅λλ€. Feathers μ λ΅μ νμ₯μ ν΅ν΄ μ¬μ©μ μ μν μ μλ ν΄λμ€λ‘ ꡬνλ©λλ€(μ΄λ κ²μ¦μλ₯Ό λ체νκ³ κΈ°λ³Έμ μΌλ‘ μ λ΅κ³Ό κ²μ¦μλ₯Ό λ¨μΌ ν΄λμ€λ‘ κ²°ν©ν©λλ€):
class LocalStrategy {
constructor(app);
async findUser(authParams);
async comparePassword(user, password);
async authenticate (authParams);
}
class MyLocalStrategy extends LocalStrategy {
async findUser(authParams);
}
app.authentication.registerStrategy('local', new MyLocalStrategy(app));
μ λ΅μ κΈ°λ³Έ λ
Έλ HTTP μμ² λ° μλ΅μ κ°μ Έμ€κ³ params.authentication
(λλ null
)μ λν κ°μ λ°νν΄μΌ νλ parse
λ©μλλ₯Ό μ 곡ν μλ μμ΅λλ€.
const daveStrategy = {
async authenticate (authParams) {
throw new Forbidden('Not super Dave');
}
async parse (req, res) {
const apiKey = req.headers['x-super-dave'];
if (apiKey) {
return {
strategy: 'superdave',
apiKey
}
}
return null;
}
};
κ·Έλ° λ€μ HTTP λΌμ΄λΈλ¬λ¦¬λ μ΄λ¬ν λ©μλλ₯Ό μ¬μ©νμ¬ params.authentication
νκ±°λ μ체 λ―Έλ€μ¨μ΄λ₯Ό μΈμ¦νλμ§ μ¬λΆμ λ°©λ²μ κ²°μ ν μ μμ΅λλ€.
app.authenticate(authParams, [ strategies ])
λ μ£Όμ΄μ§ μΈμ¦ 맀κ°λ³μλ‘ μ£Όμ΄μ§ μ λ΅μ μ€νν©λλ€:
// Will return the user for a JWT (on the server)
const { user } = app.authenticate({ accessToken }, 'jwt');
μλΉμ€ νΈμΆμμ
params.authentication
λ₯Ό μ€μ νλ κ²μ μΈμ¦ μ 보λ₯Ό μ 곡νλ _μ μΌν_ λ°©λ²μ λλ€.
λͺ¨λ service()
νΈμΆμ accessToken
λ₯Ό μ 곡ν΄μΌ νλ μ΄μ μ λν΄ μ€κ³ ν¬μΈνΈλ₯Ό μ΄ν΄λ³΄μ€ μ μμ΅λκΉ?
μ΄κ²μ μλ²μλ§ μ μ©λλ©° μΈμ¦μ΄ ꡬμ±λλ©΄ λͺ¨λ μλΉμ€ νΈμΆμ λν΄ μ€μ λ params.provider
λ° params.headers
(μΉ μμΌμ κ²½μ° μ΄λ κ°μ§ ν€λμΌ λΏμ
λλ€)λ₯Ό ν΅ν΄ μ΄λ―Έ μμμ μΌλ‘ λ°μν©λλ€. μ΄κ²μ μ μ‘ νλ‘ν μ½ λ
립 νν¬ λ° μλΉμ€μ μ€μ μ μ‘ λ©μ»€λμ¦(μ: Express λλ Socket.ioκ° μλ HTTP) μ¬μ΄μ Feathers λΆλ¦¬λ₯Ό κΉ¨κ³ authenticate('jwt')
νν¬κ° μ€μ λ‘ μ€νλκ³ λ¬΄μμ μ¬μ©λλμ§λ₯Ό λ§€μ° νΌλμ€λ½κ² λ§λ€μμ΅λλ€. κ·Έ μΈμ¦ μ 보.
μ μ©μ± μΈ‘λ©΄μμ μΈλΆ λλ λ΄λΆ νΈμΆμ λν΄ μ€μ λ‘ λ³κ²½λλ κ²μ μμ§λ§ μ΄μ μλ²μμ λͺ
μμ μΌλ‘ authenticate
νν¬λ₯Ό νΈλ¦¬κ±°νλ €λ©΄ νμ params.authentication
μ€μ ν μ μμ΅λλ€. μ΄κ²μ μΈμ¦ μ 보λ₯Ό Feathers μΈμ¦ λ©μ»€λμ¦μ μ λ¬νλ νμ€νλ λ°©λ²μ΄ μλ Express μ΄μΈμ μλ‘μ΄ νλ μμν¬ νλ¬κ·ΈμΈ(μ: Koa, HTTP2 λλ λ©μμ§ λκΈ°μ΄)μ νΉν μ€μν©λλ€.
μΈμ¦ ν΄λΌμ΄μΈνΈλ app.authenticate()
λ₯Ό νΈμΆνμ¬ λ‘κ·ΈμΈν νμλ μΈμ¦λ μμ²μ κ³μν©λλ€.
λͺ νν ν΄ μ£Όμ μ κ°μ¬ν©λλ€. v3, νΉν socket.io React Native ν΄λΌμ΄μΈνΈλ₯Ό μ¬μ©ν ν μ€νΈκ° λ§€μ° κΈ°λλ©λλ€.
μ¬κΈ°μ ν리릴리μ¦μ λν μ 보λ₯Ό νμ€ν λ£μ κ²μ λλ€. μΈμ¦λ μΉ μμΌμ ν¨μ¬ λ μμ μ μΌλ‘ μ²λ¦¬ν΄μΌ νλ μΈμ¦ ν΄λΌμ΄μΈνΈλ₯Ό λ§λ¬΄λ¦¬νκΈ°λ§ νλ©΄ λ©λλ€. μΌλ¨ μλ£λλ©΄ μλ‘μ΄ μ½μ΄ + λ‘컬 λ° jwt μΈμ¦μ ν μ€νΈνλ λ° λμμ λ°λ κ²μ΄ μ’μ΅λλ€. oAuthλ κ·Έ μ΄νμ 곧 λ°λΌμΌ ν©λλ€.
3λ²μ μ μΈμ λμ€λμ?
λ΄ μ§λ¬Έμ λν λ΅λ³μ΄ μμ΅λκΉ?
νμ¬ μ§ν μν©μ λ§μ€ν° λΈλμΉμμ νμΈν μ μμΌλ©° λ² ν ν μ€ν°κ° μ¬μ©ν΄ λ³Ό μ μμ λ λΈλ‘κ·Έ κ²μλ¬Όμ μ¬λ¦¬λλ‘ νκ² μ΅λλ€. μνλ λ€μκ³Ό κ°μ΅λλ€.
| λͺ¨λ | μ½λ | λ¬Έμ | CLI
| --- |:---:|:---:|---:|
| @feathersjs/μΈμ¦ | β
| 100% | β|
| @feathersjs/authentication-local | β
| 80% | β|
| @featherjs/μΈμ¦-oauth | β
| 90%| β|
| @feathersjs/μΈμ¦ ν΄λΌμ΄μΈνΈ | β
| 80%| β|
μλ νμΈμ, μ΄κ²μ νλ₯ν©λλ€!
λ―Έλμ μ μ°μ±μ νλ₯νμ§λ§ μμ§ FeathersJS νλ‘μ νΈμ μΈμ¦μ νμ μ΄ μμΌλ©° μ΄λ₯Ό μμ±νκΈ° μν΄ νΌλμ€λ¬μ΄ κ³Όμ μ κ²ͺκ³ μμ΅λλ€.
μλ²½νκ³ ν μ€νΈλ₯Ό κ±°μΉ μμ ν μΈμ¦ ꡬνμ ν΅ν΄ μμ μκ² κΈ°λ₯μ μ§νν μ μμ΅λλ€. MeteorJSλ₯Ό μ¬μ©νλ κ°μ₯ μ’μ μ΄μ μ€ νλλ ν΄λΌμ΄μΈνΈμ λν μλ²½ν κ³μ ν΅ν©μ΄μμ΅λλ€.
FeatherJS λ¬Έμ λ₯Ό μ΄ν΄λ³΄λ©΄ λ§μ μ¬λλ€μ΄ μΈμ¦μ κ΄ν κ²μ΄λ―λ‘ μ΄ μμ μ΄ μ°©λ₯νκ² λμ΄ λ§€μ° κΈ°μ©λλ€!
μ€λλ FeathersJSμμ μμ ν 보μ μΈμ¦ μμ€ν (λ‘컬 μΈμ¦, μ‘μΈμ€ μ μ΄, μ΄λ©μΌ λ±)μ μν μ΅κ³ μ λ¬Έμ(λλ μ°Έμ‘° ꡬν)λ μ΄λμ μμ΅λκΉ?
μ΄κ²μ΄ λ΄κ° μ§κΈκΉμ§ κ°μ§κ³ μλ κ²μ λλ€. λ λμ κ²μ΄ μμ΅λκΉ?
2018/02 - FeathersJSμμ μ΄λ©μΌ νμΈ μ€μ (2017λ
κΈ°μ¬ μ¬ν΄μ)
2018/02 - μ κΈ°μ¬μ 리ν¬μ§ν 리
2017/01 - FeathersJSμμ μ΄λ©μΌ νμΈμ μ€μ νλ λ°©λ²(μμλ¨)
2018/06 - Feathersjsλ₯Ό μ¬μ©ν Vue μΈμ¦
미리 κ°μ¬λ립λλ€!
μ΄μ λͺ¨λ κ΄λ ¨ λ¬Έμ κ° μ’ λ£λμμΌλ©° μ μλ λͺ¨λ λ³κ²½ μ¬νμ΄ κ΅¬νλ Feathers v4μ μννμ ν μ€νΈν μ μμ΅λλ€. μμΈν λ΄μ©μ λ§μ΄κ·Έλ μ΄μ κ°μ΄λ λ₯Ό μ°Έμ‘°νμΈμ.
κ°μ₯ μ μ©ν λκΈ
νμ¬ μ§ν μν©μ λ§μ€ν° λΈλμΉμμ νμΈν μ μμΌλ©° λ² ν ν μ€ν°κ° μ¬μ©ν΄ λ³Ό μ μμ λ λΈλ‘κ·Έ κ²μλ¬Όμ μ¬λ¦¬λλ‘ νκ² μ΅λλ€. μνλ λ€μκ³Ό κ°μ΅λλ€.
| λͺ¨λ | μ½λ | λ¬Έμ | CLI
| --- |:---:|:---:|---:|
| @feathersjs/μΈμ¦ | β | 100% | β|
| @feathersjs/authentication-local | β | 80% | β|
| @featherjs/μΈμ¦-oauth | β | 90%| β|
| @feathersjs/μΈμ¦ ν΄λΌμ΄μΈνΈ | β | 80%| β|