我的筹码是:
我想在我的应用程序中使用相应的cordova插件实现社交登录按钮(Facebook,Google等),登录用户并在我现有的自定义服务器端API上验证并存储他/她的数据。我无法找到关于如何做到这一点的任何好教程。
我希望我的用户使用随机密码保留在数据库中,并且能够从我的应用程序登录。
我想象的是:
(客户端)
FB.login(function(userDetailsAndToken) {
myBackendAPI.socialLogin(userDetailsAndToken).then(function(user) {
//Successfully logged in with Facebook!
this.user = user;
}
}
并在后端(Java Spring):
@PostMapping("/social/account")
public ResponseEntity socialAccount(@Valid @RequestBody FacebookDTO facebookDTO) {
validateSocialUser(facebookDTO);
//if user exists return invalid etc.
Optional<User> existingUser = userRepository.findOneByEmail(facebookDTO.getEmail());
if (existingUser.isPresent()) {
return ResponseEntity.badRequest();
}
//User doesn't exist. Proceed with login
//map from social to my User entity
User user = socialMapper.toEntity(facebookDTO);
userService
.createUser(user.getLogin(), user.getPassword(),
user.getFirstName(), user.getLastName(),
user.getEmail().toLowerCase(), user.getImageUrl(),
user.getLangKey());
return new ResponseEntity<>(HttpStatus.CREATED);
}
这可行且安全吗?关于如何实现这一目标的任何好的资源/图书馆或指南?
答案 0 :(得分:4)
以下是有关使用FB和Google Auth的示例演示,但我不是来自Java背景,因此您将只找到客户端解决方案。
<强>服务强>
让我们实现登录功能的逻辑。在oauth.service.ts
文件夹中创建oauth
文件,然后将以下代码粘贴到其中:
import { Injectable, Injector } from '@angular/core';
import { FacebookOauthProvider } from './facebook/facebook-oauth.provider';
import { IOathProvider } from './oauth.provider.interface';
import { GoogleOauthProvider } from './google/google-oauth.provider';
import { OAuthToken } from './models/oauth-token.model';
@Injectable()
export class OAuthService {
private oauthTokenKey = 'oauthToken';
private injector: Injector;
constructor(injector: Injector) {
this.injector = injector;
}
login(source: string): Promise {
return this.getOAuthService(source).login().then(accessToken => {
if (!accessToken) {
return Promise.reject('No access token found');
}
let oauthToken = {
accessToken: accessToken,
source: source
};
this.setOAuthToken(oauthToken);
return oauthToken;
});
}
getOAuthService(source?: string): IOathProvider {
source = source || this.getOAuthToken().source;
switch (source) {
case 'facebook':
return this.injector.get(FacebookOauthProvider);
case 'google':
return this.injector.get(GoogleOauthProvider);
default:
throw new Error(`Source '${source}' is not valid`);
}
}
setOAuthToken(token: OAuthToken) {
localStorage.setItem(this.oauthTokenKey, JSON.stringify(token));
}
getOAuthToken(): OAuthToken {
let token = localStorage.getItem(this.oauthTokenKey);
return token ? JSON.parse(token) : null;
}
}
身份验证提供程序和令牌接口
正如我们已经提到的,IOathProvider
应该包含login()函数。因此,我们应该设置以下接口作为IOathProvider
对象的抽象类型/模型。在oauth.provider.interface.ts
文件夹中创建oauth
文件,并在其中加入以下行:
export interface IOathProvider {
login(): Promise;
}
Facebook和Google身份验证服务
下一步,我们应该为我们的应用程序拥有的每个身份验证提供程序实现服务,即FacebookOauthProvider
和GoogleOauthProvider
。
安装依赖项
这时ng2-cordova-oauth
库很方便。我们可以通过执行命令来安装它:
npm install ng2-cordova-oauth --save
此外,我们的应用程序依赖于Cordova InAppBrowser plugin
。我们将安装它:
ionic plugin add cordova-plugin-inappbrowser
不要忘记在package.json文件中包含cordova-plugin-inappbrowser
,因此只要您从头开始安装项目,就可以将其与其他插件一起安装。
实施Facebook和Google身份验证提供程序
让我们在facebook-oauth.provider.ts
下创建oauth/facebook/ path
文件。在此文件中,请在代码段中包含代码:
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { IOathProvider } from '../oauth.provider.interface';
import { CordovaOauth } from 'ng2-cordova-oauth/oauth';
import { Facebook } from 'ng2-cordova-oauth/provider/facebook';
import { Config } from '../../../config';
interface ILoginResponse {
access_token: string;
}
@Injectable()
export class FacebookOauthProvider implements IOathProvider {
private cordovaOauth: CordovaOauth;
private http: Http;
private config: Config;
private facebook: Facebook;
constructor(http: Http, config: Config) {
this.http = http;
this.config = config;
this.facebook = new Facebook({ clientId: config.facebook.appId, appScope: config.facebook.scope });
this.cordovaOauth = new CordovaOauth();
}
login(): Promise {
return this.cordovaOauth.login(this.facebook)
.then((x: ILoginResponse) => x.access_token);
}
}
同样,使用CordovaOauth
提供的ng2-cordova-oauth library
对象,我们将使用自己的Google authentication provider
函数实现login()
。但是,这里我们从Config传递另一个clientId
,该Google
对应于我们使用Google Developer Console
使用google-oauth.provider.ts
配置的应用程序。
因此,请创建一个import { Injectable } from '@angular/core';
import { IOathProvider } from '../oauth.provider.interface';
import { OAuthProfile } from '../models/oauth-profile.model';
import { CordovaOauth } from 'ng2-cordova-oauth/oauth';
import { Google } from 'ng2-cordova-oauth/provider/google';
import { Config } from '../../../config';
import { Http } from '@angular/http';
interface ILoginResponse {
access_token: string;
}
@Injectable()
export class GoogleOauthProvider implements IOathProvider {
private http: Http;
private config: Config;
private cordovaOauth: CordovaOauth;
private google: Google;
constructor(http: Http, config: Config) {
this.http = http;
this.config = config;
this.google = new Google({ clientId: config.google.appId, appScope: config.google.scope });
this.cordovaOauth = new CordovaOauth();
}
login(): Promise {
return this.cordovaOauth.login(this.google).then((x: ILoginResponse) => x.access_token);
}
getProfile(accessToken: string): Promise {
let query = `access_token=${accessToken}`;
let url = `${this.config.google.apiUrl}userinfo?${query}`;
return this.http.get(url)
.map(x => x.json())
.map(x => {
let name = x.name.split(' ');
return {
firstName: name[0],
lastName: name[1],
email: x.email,
provider: 'google'
};
})
.toPromise();
}
}
文件并粘贴以下行:
const PORT = 3000;
var objServer = require('express')();
var http = require('http').Server(objServer);
var io = require('socket.io')(http);
//objServer.use(express.static("public"));
objServer.get('/', function (req, res) {
res.sendFile(__dirname + '/public/index_test.html');
});
//node http server started
http.listen(PORT, function () {
console.log('listening on *:' + PORT);
});
this文章的完整学分,您可以在Github中找到Working Code。我还没有涵盖整个教程,只包括该教程的部分(谷歌和Facebook)。我们需要安装什么插件以及如何使用TypeScript,如果您需要,那么您可以参考该教程
答案 1 :(得分:2)
我的一个项目的例子。用于JWT身份验证的Java客户端:
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends ResourceServerConfigurerAdapter {
public MicroserviceSecurityConfiguration() {
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/profile-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/swagger-resources/configuration/ui").permitAll();
}
@Bean
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(
@Qualifier("loadBalancedRestTemplate") RestTemplate keyUriRestTemplate) {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(getKeyFromAuthorizationServer(keyUriRestTemplate));
return converter;
}
@Bean
public RestTemplate loadBalancedRestTemplate(RestTemplateCustomizer customizer) {
RestTemplate restTemplate = new RestTemplate();
customizer.customize(restTemplate);
return restTemplate;
}
private String getKeyFromAuthorizationServer(RestTemplate keyUriRestTemplate) {
HttpEntity<Void> request = new HttpEntity<Void>(new HttpHeaders());
return (String) keyUriRestTemplate
.exchange("http://someserver/oauth/token_key", HttpMethod.GET, request, Map.class).getBody()
.get("value");
}
}
社交按钮的Java后端,包括(Google和Facebook):
import package.repository.SocialUserConnectionRepository;
import package.repository.CustomSocialUsersConnectionRepository;
import package.security.social.CustomSignInAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.social.UserIdSource;
import org.springframework.social.config.annotation.ConnectionFactoryConfigurer;
import org.springframework.social.config.annotation.EnableSocial;
import org.springframework.social.config.annotation.SocialConfigurer;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.web.ConnectController;
import org.springframework.social.connect.web.ProviderSignInController;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.social.connect.web.SignInAdapter;
import org.springframework.social.facebook.connect.FacebookConnectionFactory;
import org.springframework.social.google.connect.GoogleConnectionFactory;
import org.springframework.social.security.AuthenticationNameUserIdSource;
import org.springframework.social.twitter.connect.TwitterConnectionFactory;
// jhipster-needle-add-social-connection-factory-import-package
import javax.inject.Inject;
/**
* Basic Spring Social configuration.
*
* <p>Creates the beans necessary to manage Connections to social services and
* link accounts from those services to internal Users.</p>
*/
@Configuration
@EnableSocial
public class SocialConfiguration implements SocialConfigurer {
private final Logger log = LoggerFactory.getLogger(SocialConfiguration.class);
@Inject
private SocialUserConnectionRepository socialUserConnectionRepository;
@Inject
Environment environment;
@Bean
public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
ConnectController controller = new ConnectController(connectionFactoryLocator, connectionRepository);
controller.setApplicationUrl(environment.getProperty("spring.application.url"));
return controller;
}
@Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
// Google configuration
String googleClientId = environment.getProperty("spring.social.google.clientId");
String googleClientSecret = environment.getProperty("spring.social.google.clientSecret");
if (googleClientId != null && googleClientSecret != null) {
log.debug("Configuring GoogleConnectionFactory");
connectionFactoryConfigurer.addConnectionFactory(
new GoogleConnectionFactory(
googleClientId,
googleClientSecret
)
);
} else {
log.error("Cannot configure GoogleConnectionFactory id or secret null");
}
// Facebook configuration
String facebookClientId = environment.getProperty("spring.social.facebook.clientId");
String facebookClientSecret = environment.getProperty("spring.social.facebook.clientSecret");
if (facebookClientId != null && facebookClientSecret != null) {
log.debug("Configuring FacebookConnectionFactory");
connectionFactoryConfigurer.addConnectionFactory(
new FacebookConnectionFactory(
facebookClientId,
facebookClientSecret
)
);
} else {
log.error("Cannot configure FacebookConnectionFactory id or secret null");
}
// Twitter configuration
String twitterClientId = environment.getProperty("spring.social.twitter.clientId");
String twitterClientSecret = environment.getProperty("spring.social.twitter.clientSecret");
if (twitterClientId != null && twitterClientSecret != null) {
log.debug("Configuring TwitterConnectionFactory");
connectionFactoryConfigurer.addConnectionFactory(
new TwitterConnectionFactory(
twitterClientId,
twitterClientSecret
)
);
} else {
log.error("Cannot configure TwitterConnectionFactory id or secret null");
}
// jhipster-needle-add-social-connection-factory
}
@Override
public UserIdSource getUserIdSource() {
return new AuthenticationNameUserIdSource();
}
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
return new CustomSocialUsersConnectionRepository(socialUserConnectionRepository, connectionFactoryLocator);
}
@Bean
public SignInAdapter signInAdapter() {
return new CustomSignInAdapter();
}
@Bean
public ProviderSignInController providerSignInController(ConnectionFactoryLocator connectionFactoryLocator, UsersConnectionRepository usersConnectionRepository, SignInAdapter signInAdapter) throws Exception {
ProviderSignInController providerSignInController = new ProviderSignInController(connectionFactoryLocator, usersConnectionRepository, signInAdapter);
providerSignInController.setSignUpUrl("/social/signup");
providerSignInController.setApplicationUrl(environment.getProperty("spring.application.url"));
return providerSignInController;
}
@Bean
public ProviderSignInUtils getProviderSignInUtils(ConnectionFactoryLocator connectionFactoryLocator, UsersConnectionRepository usersConnectionRepository) {
return new ProviderSignInUtils(connectionFactoryLocator, usersConnectionRepository);
}
}
package package.repository;
import package.domain.SocialUserConnection;
import org.springframework.social.connect.*;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class CustomSocialUsersConnectionRepository implements UsersConnectionRepository {
private SocialUserConnectionRepository socialUserConnectionRepository;
private ConnectionFactoryLocator connectionFactoryLocator;
public CustomSocialUsersConnectionRepository(SocialUserConnectionRepository socialUserConnectionRepository, ConnectionFactoryLocator connectionFactoryLocator) {
this.socialUserConnectionRepository = socialUserConnectionRepository;
this.connectionFactoryLocator = connectionFactoryLocator;
}
@Override
public List<String> findUserIdsWithConnection(Connection<?> connection) {
ConnectionKey key = connection.getKey();
List<SocialUserConnection> socialUserConnections =
socialUserConnectionRepository.findAllByProviderIdAndProviderUserId(key.getProviderId(), key.getProviderUserId());
return socialUserConnections.stream()
.map(SocialUserConnection::getUserId)
.collect(Collectors.toList());
};
@Override
public Set<String> findUserIdsConnectedTo(String providerId, Set<String> providerUserIds) {
List<SocialUserConnection> socialUserConnections =
socialUserConnectionRepository.findAllByProviderIdAndProviderUserIdIn(providerId, providerUserIds);
return socialUserConnections.stream()
.map(SocialUserConnection::getUserId)
.collect(Collectors.toSet());
};
@Override
public ConnectionRepository createConnectionRepository(String userId) {
if (userId == null) {
throw new IllegalArgumentException("userId cannot be null");
}
return new CustomSocialConnectionRepository(userId, socialUserConnectionRepository, connectionFactoryLocator);
};
}
package package.security.social;
import package.config.JHipsterProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.SignInAdapter;
import org.springframework.web.context.request.NativeWebRequest;
import javax.inject.Inject;
public class CustomSignInAdapter implements SignInAdapter {
@SuppressWarnings("unused")
private final Logger log = LoggerFactory.getLogger(CustomSignInAdapter.class);
@Inject
private UserDetailsService userDetailsService;
@Inject
private JHipsterProperties jHipsterProperties;
@Override
public String signIn(String userId, Connection<?> connection, NativeWebRequest request) {
UserDetails user = userDetailsService.loadUserByUsername(userId);
Authentication newAuth = new UsernamePasswordAuthenticationToken(
user,
null,
user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(newAuth);
return jHipsterProperties.getSocial().getRedirectAfterSignIn();
}
}
仅适用于JWT的示例
import io.github.jhipster.config.JHipsterProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.SignInAdapter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.context.request.ServletWebRequest;
import javax.servlet.http.Cookie;
public class CustomSignInAdapter implements SignInAdapter {
@SuppressWarnings("unused")
private final Logger log = LoggerFactory.getLogger(CustomSignInAdapter.class);
private final UserDetailsService userDetailsService;
private final JHipsterProperties jHipsterProperties;
private final TokenProvider tokenProvider;
public CustomSignInAdapter(UserDetailsService userDetailsService, JHipsterProperties jHipsterProperties,
TokenProvider tokenProvider) {
this.userDetailsService = userDetailsService;
this.jHipsterProperties = jHipsterProperties;
this.tokenProvider = tokenProvider;
}
@Override
public String signIn(String userId, Connection<?> connection, NativeWebRequest request){
try {
UserDetails user = userDetailsService.loadUserByUsername(userId);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
user,
null,
user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
String jwt = tokenProvider.createToken(authenticationToken, false);
ServletWebRequest servletWebRequest = (ServletWebRequest) request;
servletWebRequest.getResponse().addCookie(getSocialAuthenticationCookie(jwt));
} catch (AuthenticationException ae) {
log.error("Social authentication error");
log.trace("Authentication exception trace: {}", ae);
}
return jHipsterProperties.getSocial().getRedirectAfterSignIn();
}
private Cookie getSocialAuthenticationCookie(String token) {
Cookie socialAuthCookie = new Cookie("social-authentication", token);
socialAuthCookie.setPath("/");
socialAuthCookie.setMaxAge(10);
return socialAuthCookie;
}
}
JHipsterProperties只是简单的Properties配置之前的层。 这是由JHipster生成的。您可以生成单片应用程序,并查看示例后端和前端应如何工作以启用社交按钮。
答案 2 :(得分:1)
要保护数据库中的数据,您必须使用任何“散列”数据。算法。我建议使用HMAC SHA 256算法。哈希能够解密数据。要进行散列,您需要使用密钥,您可以根据需要将密钥设置为静态或动态。使用任何RSA加密算法将加密密钥存储在数据库中,并且在散列时,您可以解密密钥并传入散列方法。
使用apache HmacUtils进行散列。
String hashedPassword = HmacUtils.hmacSha1Hex(password,passwordkey);
要进行加密和解密请使用以下教程RSA Encryption and Decryption