我最近写了一个小型的完整堆栈Web应用程序,它有一个单独的rest api(Node.JS)和一个客户端(React)。在那里,我使用JWT实现了无状态身份验证。为此,我使用了npm包护照, passport-facebook-token , passport-google-token 和 passport-local 。通过此设置,我能够向用户显示电子邮件/密码登录信息,还可以通过 Facebook 和 Google 进行登录。
在进一步了解详细信息之前,我的主要问题是:是否可以使用电子邮件/密码使用JWT实现状态/无会话身份验证,使用Jaca Spring Boot作为RESTful API时使用Google和Facebook登录和Angular 5作为客户端(在不同的端口/服务器上运行完全分离的项目)?
简而言之,我在NodeJS / React应用程序中实现了如下行为:
NodeJS API - 本地,脸书和谷歌登录的不同路线:
router.post('/login', (req, res, next) => {
return passport.authenticate('local-login', (err, token, userData) => {
processAuthResponse(err, res, token, userData);
})(req, res, next);
});
router.post('/facebook', (req, res, next) => {
return passport.authenticate('facebook-token', (err, token, userData) => {
processAuthResponse(err, res, token, userData);
})(req, res, next);
});
router.post('/google', (req, res, next) => {
return passport.authenticate('google-token', (err, token, userData) => {
processAuthResponse(err, res, token, userData);
})(req, res, next);
});
这里processAuthResponse()只检查护照中传递的args并将一些最终的json发送给客户端。
NodeJS API - Passport Facebook令牌策略。摘要:从Facebook获取配置文件信息,检入数据库,是否存在具有邮件的用户(否则创建)并创建JWT以发送给React客户端。
import FacebookTokenStrategy from 'passport-facebook-token';
import config from '../../config';
import logger from '../services/logger';
import authService from '../services/authService';
import { User } from '../sequelize/models';
const facebook = config.AuthProviders.Facebook;
export default new FacebookTokenStrategy(
{
clientID: facebook.ClientID,
clientSecret: facebook.ClientSecret,
session: false
},
(accessToken, refreshToken, profile, done) => {
process.nextTick(() => {
if (!profile.emails) {
logger.error('Facebook profile has no email address');
return done({
success: false,
message: 'Das Facebook-Profil enthält keine E-Mail Adresse. Füge ihm eine E-Mail Adresse hinzu oder registriere dich mit deiner E-Mail Adresse'
});
}
User.findOne({
where: { email: profile.emails[0].value } // TODO: How do I know the main email address of profile?
}).then(user => {
if (user) {
const updateData = authService.getSequelizeUpdateObjectForUser(
user,
profile.name.givenName,
profile.name.familyName,
profile.photos[0].value // TODO: How do I know the main picture of profile?
);
if (updateData) {
user.update(updateData).then(updatedUser => {
const token = authService.createJwtFromUser(updatedUser.get({
plain: true
}));
return done(null, token);
});
} else {
const token = authService.createJwtFromUser(user.get({
plain: true
}));
return done(null, token);
}
} else {
const email = profile.emails[0].value; // TODO: How do I know the main email address of profile?
const firstName = profile.name.givenName;
const lastName = profile.name.familyName;
const imageUrl = profile.photos[0].value; // TODO: How do I know the main picture of profile?
authService.createUser('facebook', email, firstName, lastName, imageUrl, (newUser, error) => {
if (!newUser) {
logger.error(error);
}
const token = authService.createJwtFromUser(newUser);
return done(null, token);
});
}
});
});
}
);
NodeJS API - 与本地策略几乎相同:
import bcrypt from 'bcryptjs';
import { Strategy as LocalStrategy } from 'passport-local';
import logger from '../services/logger';
import authService from '../services/authService';
import { User, UserAuth } from '../sequelize/models';
const ErrorCodes = require('../constants/ErrorCodes');
export default new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
session: false,
passReqToCallback: true
},
(req, email, password, done) => {
const userData = {
email: email.trim(),
password
};
User.findOne({
where: { email: userData.email },
include: [{ model: UserAuth }]
}).then(user => {
if (!user) {
logger.info(`Local login: User with email '${email}' not found`);
const error = new Error('E-Mail oder Passwort falsch');
error.name = ErrorCodes.INCORRECT_CREDENTIALS_ERROR;
return done(error);
}
// TODO: What to do if email is not verified yet?
user = user.get({ plain: true });
if (!user.UserAuth || !user.UserAuth.password) {
logger.info(`Local login: No UserAuth object or password for user with id ${user.id}`);
const error = new Error('E-Mail oder Passwort falsch');
error.name = ErrorCodes.INCORRECT_CREDENTIALS_ERROR;
return done(error);
}
bcrypt.compare(password, user.UserAuth.password, (err, isMatch) => {
if (err) return done(err);
if (!isMatch) {
logger.info('Local login: Password did not match');
const error = new Error('E-Mail oder Passwort falsch');
error.name = ErrorCodes.INCORRECT_CREDENTIALS_ERROR;
return done(error);
}
const token = authService.createJwtFromUser(user);
return done(null, token);
});
});
}
);
还有一个Google令牌策略,其工作方式类似于facebook令牌实现。
反应客户端 - Facebook登录组件
import React, { Component } from 'react';
import { FacebookLogin } from 'react-facebook-login-component';
import { Icon } from 'antd';
import './style.css';
import config from '../../../../config';
import authService from '../../../../services/authService';
class FacebookLoginButton extends Component {
loginFacebook = res => {
authService.loginFacebook(res.accessToken);
};
render = () => {
return (
<FacebookLogin
socialId={config.AuthProviders.Facebook.ClientID}
className="login-btn login-btn-facebook"
language="de_DE"
scope="public_profile,email"
responseHandler={this.loginFacebook.bind(this)}
xfbml={true}
version="v2.5"
>
<Icon type="facebook" /> Mit Facebook anmelden
</FacebookLogin>
);
};
}
export default FacebookLoginButton;
这里,react-facebook-login-component是一个下载的npm包。谷歌也有类似的东西。我自己实施了电子邮件/密码登录表单。所有这些组件都会触发我的authService,它会对auth api路由进行API调用(使用axios)(/ auth / login,/ auth / facebook,/ auth / google)。
现在又来了:
我希望在新的完整堆栈应用程序中使用 Java Spring Boot 用于RESTful API,而 Angular 5 用于客户端,这两个都是分离的项目
列出的要求:
我现在使用/ login路由实现了我的Java Spring Boot REST Api,它已经为本地登录创建了JWT。它来自本教程:https://www.callicoder.com/spring-boot-spring-security-jwt-mysql-react-app-part-2/
但是,我不知道如何实现这两者:电子邮件/密码和社交提供商(Facebook / Google)身份验证,因为我是Java Spring Boot的新手。
是否需要 Spring Social (我现在不知道它是如何维护的)或 Spring Security 足够的情况?
非常感谢你提前解答的每一个小提示!