Java >> Java Tutorial >  >> Tag >> Spring

Soziale Anmeldung mit Spring Boot

In diesem Beitrag zeige ich, wie man Social Login in einer Spring Boot-Anwendung verwendet. Also erstellen wir eine Anwendung, aber wir verwenden eine formularbasierte Anmeldung, die der einfachste und unsicherste Authentifizierungsmechanismus ist, den es gibt. Wie können wir diese Vermutung überwinden und den neuesten sichereren Mechanismus verwenden?

Social Login – Tada.

Ja, mit einer zunehmenden Anzahl sozialer Netzwerke wird es immer beliebter und einfacher, einen OAuth-basierten Anmeldemechanismus mithilfe sozialer Netzwerke zu erstellen. Mit anderen Worten, Spring Boot bietet eine Lösung mit einem Social-Login-Plugin und in diesem Beitrag zeigen wir, wie Sie Social-Login verwenden, um Ihre Benutzer zu authentifizieren.

Was brauchen Sie

  • IntelliJ
  • Java 8
  • Twitter-/Facebook-/Google-/Linkedin-/Github-Konten
  • Frühlingsstiefel
  • Gradle

Spring Social Core

Der Frühling bietet einen spring-social-core an Projekt, das APIs enthält, um eine Verbindung zu den sozialen Konten des Benutzers herzustellen. Dennoch enthält diese Bibliothek ein Connect-Framework, das eine Lösung zur Verwaltung von Verbindungen mit Anbietern sozialer Dienste bietet. Es bietet Unterstützung für OAuth1a und OAuth2. Der einfachste Weg, diese Bibliothek zu verstehen, besteht darin, dass Sie eine Verbindungsfactory für jeden sozialen Anbieter erstellen. Ein Verbindungsfactory-Locator findet eine Factory zum Erstellen eines Anmeldeanbieters. Ich werde weitere Details bereitstellen, wenn wir dieses Modul implementieren.

Erstelle ein soziales Login-Gradle-Projekt

Falls Sie es aus meinen Blog-Beiträgen noch nicht mitbekommen haben, ich bin von Eclipse auf IntelliJ für den Programmiereditor umgestiegen. Intellij ist einfach intelligenter und einfach zu schreibender Code-Editor. Erstellen Sie also zuerst ein Gradle-Projekt für Spring Boot. (Nebenbemerkung – wenn Sie IntelliJ Ultimate Edition verwenden, bietet es eine Funktion zum Erstellen eines Spring-Projekts.) Wir werden die neueste Version von Spring Boot (2.0.3.RELEASE) verwenden, um dieses Projekt zu erstellen.

Die Gradle-Datei sieht wie folgt aus:

buildscript {
  ext {
    springBootVersion = '2.0.3.RELEASE'
  }
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.betterjavacode'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
  mavenCentral()
  maven {
    url 'https://repo.spring.io/libs-milestone'
  }
}


dependencies {
  compile('org.springframework.boot:spring-boot-starter-thymeleaf')
  compile("org.springframework.boot:spring-boot-starter-web")
  compile("org.springframework.social:spring-social-security:1.1.6.RELEASE")
  compile("org.springframework.social:spring-social-config:1.1.6.RELEASE")
  compile("org.springframework.social:spring-social-core:1.1.6.RELEASE")
  compile('org.springframework.boot:spring-boot-starter-security')
  compile('org.springframework.boot:spring-boot-starter-data-jpa')
  compile("com.fasterxml.jackson.core:jackson-databind:2.9.6")
  compile('mysql:mysql-connector-java:5.1.6')
  compile("org.springframework.social:spring-social-twitter:1.1.2.RELEASE")
  compile("org.springframework.social:spring-social-facebook:2.0.3.RELEASE")
  compile("org.springframework.social:spring-social-google:1.0.0.RELEASE")
  compile("org.springframework.social:spring-social-github:1.0.0.M4")
  compile("org.springframework.social:spring-social-linkedin:1.0.2.RELEASE")
  testCompile('org.springframework.boot:spring-boot-starter-test')
}

Ich werde im weiteren Verlauf jede in der Gradle-Datei hinzugefügte Abhängigkeit erklären.

Erstellen Sie eine Entitätsklasse

Wir werden eine einfache Entitätsklasse für Benutzer verwenden mit nur einem Feld name . Dies sieht wie folgt aus:

@JsonIgnoreProperties(ignoreUnknown = true)
public class User
{
    public String name;

    public String getName() 
    {
        return name;
    }
    public void setName(String name) 
    {
        this.name = name;
    }
}

Erstellen Sie eine soziale Konfiguration, um die Spring Social-Bibliothek anzupassen

Zuerst werden wir eine Schnittstelle SocialConfigurer implementieren die Spring Social Library anbietet. Im Rahmen dieser Implementierung werden wir beispielsweise Verbindungsfabriken für verschiedene soziale Dienstleister erstellen. Auch für dieses Modul verwenden wir InMemoryUsersConnectionRepository . Sie können jederzeit ein JDBC-basiertes Repository für Datenbankbenutzerverbindungen implementieren. Diese Klasse sieht wie folgt aus:

package com.betterjavacode.reusablesociallogin;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
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.config.annotation.SocialConfigurerAdapter;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;
import org.springframework.social.connect.mem.InMemoryUsersConnectionRepository;
import org.springframework.social.connect.support.ConnectionFactoryRegistry;
import org.springframework.social.connect.web.ProviderSignInController;
import org.springframework.social.facebook.connect.FacebookConnectionFactory;
import org.springframework.social.github.connect.GitHubConnectionFactory;
import org.springframework.social.google.connect.GoogleConnectionFactory;
import org.springframework.social.linkedin.connect.LinkedInConnectionFactory;
import org.springframework.social.security.AuthenticationNameUserIdSource;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
import org.springframework.social.twitter.connect.TwitterConnectionFactory;

import javax.inject.Inject;
import javax.sql.DataSource;

@Configuration
@PropertySource("classpath:application.properties")
@EnableSocial
public class SocialConfig implements SocialConfigurer
{

    @Autowired
    private DataSource dataSource;

    @Override
    public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment)
    {
        connectionFactoryConfigurer.addConnectionFactory(new TwitterConnectionFactory(environment.getProperty("spring.social.twitter.consumerKey"), environment.getProperty("spring.social.twitter.consumerSecret")));
        connectionFactoryConfigurer.addConnectionFactory(new FacebookConnectionFactory(environment.getProperty("spring.social.facebook.appId"),environment.getProperty("spring.social.facebook.appSecret")));
        GoogleConnectionFactory googleConnectionFactory = new GoogleConnectionFactory(environment.getProperty("spring.social.google.appId"),environment.getProperty("spring.social.google.appSecret"));
        googleConnectionFactory.setScope("profile");
        connectionFactoryConfigurer.addConnectionFactory(googleConnectionFactory);
        connectionFactoryConfigurer.addConnectionFactory(new GitHubConnectionFactory(environment.getProperty("spring.social.github.appId"), environment.getProperty("spring.social.github.appSecret")));
        connectionFactoryConfigurer.addConnectionFactory(new LinkedInConnectionFactory(environment.getProperty("spring.social.linkedin.appId"), environment.getProperty("spring.social.linkedin.appSecret")));
    }

    @Override
    public UserIdSource getUserIdSource()
    {
        return new UserIdSource() {
            @Override
            public String getUserId() {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication == null) {
                    throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
                }
                return authentication.getName();
            }
        };
    }

    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator)
    {
        InMemoryUsersConnectionRepository usersConnectionRepository = new InMemoryUsersConnectionRepository(
                connectionFactoryLocator);
        return usersConnectionRepository;
    }

}

Wie Sie in dieser Klasse sehen, beziehe ich mich auf application.properties . Die application.properties wird wie folgt aussehen:

spring.social.twitter.consumerKey=[Twitter consumer key]
spring.social.twitter.consumerSecret=[Twitter consumer secret]
spring.social.facebook.appId=[Facebook client id]
spring.social.facebook.appSecret=[Facebook client secret]
spring.social.google.appId=[Google client id]
spring.social.google.appSecret=[Google client secret]
spring.social.github.appId=[Github client id]
spring.social.github.appSecret=[Github client secret]
spring.social.linkedin.appId=[Linkedin client id]
spring.social.linkedin.appSecret=[Linkedin client secret]
server.port = 8448

Mit anderen Worten, um clientid zu erhalten und clientsecret müssen Sie Ihren Antrag bei jedem Sozialleistungsträger registrieren. Wir werden das in diesem Beitrag nicht behandeln.

Erstellen Sie eine Spring-Web-Sicherheitskonfiguration

In dieser Klasse erweitern wir websecurityconfigureradapter und HTTP-Sicherheit als Teil der Spring-Sicherheitsimplementierung konfigurieren. Wir fügen auch einen bean hinzu um Anmeldeanbieter zu erstellen, die Teil von Spring Social sind. Darüber hinaus werden wir diesen Anmeldeanbieter implementieren, um Benutzern die Möglichkeit zu bieten, sich bei ihrem sozialen Anbieter anzumelden.

package com.betterjavacode.reusablesociallogin;


import com.betterjavacode.reusablesociallogin.social.SocialConnectionSignup;
import com.betterjavacode.reusablesociallogin.social.SocialSignInAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.mem.InMemoryUsersConnectionRepository;
import org.springframework.social.connect.web.ProviderSignInController;
import org.springframework.social.security.SpringSocialConfigurer;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{

    @Autowired
    private ConnectionFactoryLocator connectionFactoryLocator;

    @Autowired
    private UsersConnectionRepository usersConnectionRepository;

    @Autowired
    private SocialConnectionSignup socialConnectionSignup;

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/","/socialloginhome","/signin/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

    @Bean
    public ProviderSignInController providerSignInController()
    {
        ((InMemoryUsersConnectionRepository) usersConnectionRepository)
                .setConnectionSignUp(socialConnectionSignup);

        return new ProviderSignInController(
                connectionFactoryLocator,
                usersConnectionRepository,
                new SocialSignInAdapter());
    }
}

Wie Sie in dieser Klasse sehen, haben wir einen bean ProviderSignInController was SocialSignInAdapter verwendet .

Implementieren Sie einen Anmeldeadapter

Dies ist vor allem das Herzstück unserer Implementierung, in der die Authentifizierung stattfindet und dem Benutzer eine Rolle für den Zugriff auf die Anwendung zugewiesen wird. Der Benutzer wird zur Anwendung umgeleitet, wenn sich der Benutzer erfolgreich authentifiziert. Diese Klasse sieht wie folgt aus:

package com.betterjavacode.reusablesociallogin.social;

import com.betterjavacode.reusablesociallogin.util.ConnectionHelper;
import com.betterjavacode.reusablesociallogin.util.ConnectionType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.SignInAdapter;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.NativeWebRequest;

import java.util.ArrayList;
import java.util.List;

@Service
public class SocialSignInAdapter implements SignInAdapter
{
    @Override
    public String signIn(String userId, Connection<?> connection, NativeWebRequest request)
    {
        Authentication authentication = getAuthentication(userId, connection);

        SecurityContextHolder.getContext().setAuthentication(authentication);

        return "/socialloginsuccess";
    }

    private Authentication getAuthentication(String localUserId, Connection<?> connection)
    {
        List<GrantedAuthority> roles = getRoles(connection);

        String password = null;

        Authentication authentication = new UsernamePasswordAuthenticationToken(localUserId, password, roles);

        return authentication;
    }

    private List<GrantedAuthority> getRoles(Connection<?> connection)
    {
        List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();

        ConnectionType type = ConnectionHelper.getConnectionType(connection);

        String role = type.toString();

        roles.add(new SimpleGrantedAuthority(role));

        return roles;
    }
}

Wie Sie in getAuthentication sehen übergeben wir userId und Rollen für die tokenbasierte Authentifizierung.

Wenn sich der Benutzer zuvor noch nicht bei einem sozialen Anbieter angemeldet hat, wird er aufgefordert, sich anzumelden, und wird nach der ersten Anmeldung zur Anwendung weitergeleitet.

package com.betterjavacode.reusablesociallogin.social;

import com.betterjavacode.reusablesociallogin.entity.User;
import com.betterjavacode.reusablesociallogin.util.UserHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.stereotype.Service;

@Service
public class SocialConnectionSignup implements ConnectionSignUp
{
    @Autowired
    UserHelper userHelper;

    @Override
    public String execute(Connection<?> connection)
    {
        User user = userHelper.getUser(connection);

        return user.getName();
    }
}

Wie Sie in dieser Klasse sehen, haben wir Autowired ein userHelper Klasse, diese Klasse wird eine Implementierung haben, um Benutzerdetails von jedem sozialen Anbieter zu erhalten.

Daher dieser UserHelper wird wie folgt aussehen:

package com.betterjavacode.reusablesociallogin.util;

import com.betterjavacode.reusablesociallogin.entity.User;

import org.springframework.social.connect.Connection;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.github.api.GitHub;
import org.springframework.social.google.api.Google;
import org.springframework.social.linkedin.api.LinkedIn;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.stereotype.Component;

@Component
public class UserHelper
{
    public User getUser(Connection<?> connection)
    {
        User user = null;

        //get the connection type
        ConnectionType type = ConnectionHelper.getConnectionType(connection);

        if (type.equals(ConnectionType.TWITTER)) {
            user = getTwitterUser(connection);
        } else if (type.equals(ConnectionType.FACEBOOK)) {
            user = getFacebookUser(connection);
        } else if (type.equals(ConnectionType.GOOGLE)) {
            user = getGoogleUser(connection);
        } else if (type.equals(ConnectionType.GITHUB)) {
            user = getGithubUser(connection);
        } else if (type.equals(ConnectionType.LINKEDIN)){
            user = getLinkedInUser(connection);
        }

        return user;
    }

    private User getTwitterUser(Connection<?> connection)
    {
        User user = new User();
        Twitter twitterApi = (Twitter)connection.getApi();

        String name = twitterApi.userOperations().getUserProfile().getName();

        user.setName(name);

        return user;
    }

    private User getFacebookUser(Connection<?> connection)
    {
        User user = new User();
        Facebook facebookApi = (Facebook)connection.getApi();
        String [] fields = { "name" };
        User userProfile = facebookApi.fetchObject("me", User.class, fields);

        String name = userProfile.getName();

        user.setName(name);

        return user;
    }

    private User getGoogleUser(Connection<?> connection)
    {
        User user = new User();
        Google googleApi = (Google) connection.getApi();
        String name = googleApi.plusOperations().getGoogleProfile().getDisplayName();
        user.setName(name);
        return user;
    }

    private User getGithubUser(Connection<?> connection)
    {
        User user = new User();
        GitHub githubApi = (GitHub) connection.getApi();
        String name = githubApi.userOperations().getUserProfile().getName();
        user.setName(name);
        return user;
    }

    private User getLinkedInUser(Connection<?> connection)
    {
        User user = new User();
        LinkedIn linkedInApi = (LinkedIn) connection.getApi();
        String name = linkedInApi.profileOperations().getUserProfile().getFirstName();
        user.setName(name);
        return user;
    }
}

Einen Controller und Ansichten implementieren

In ähnlicher Weise besteht das letzte Stück in diesem Puzzle darin, einen Controller und entsprechende Ansichten hinzuzufügen, damit der Benutzer beim Zugriff auf die Anwendung zur Authentifizierung aufgefordert wird.

Wir werden jedoch einen Login-Controller hinzufügen, der drei Ansichten für login hat , sociallogin und socialloginsuccess . Dies sieht wie folgt aus:

@Controller
public class LoginController
{
    @RequestMapping(value="/login", method= RequestMethod.GET)
    public String login(Model model)
    {
        return "login";
    }

    @RequestMapping(value ="/socialloginhome", method = RequestMethod.GET)
    public String socialloginhome(Model model)
    {
        return "socialloginhome";
    }

    @RequestMapping(value="/socialloginsuccess", method= RequestMethod.GET)
    public String socialloginsuccess(Model model)
    {
        return "socialloginsuccess";
    }
}

Anwendung ausführen

Nachdem ich die Anwendung erstellt und ausgeführt habe, sieht der Ablauf wie folgt aus:

Sie klicken auf here Sie gelangen zum folgenden Social-Login-Bildschirm:

Ich wähle Facebook und der serverseitige Code leitet mich zum Facebook-Anmeldebildschirm weiter. Sobald ich meine Anmeldeinformationen eingegeben habe, leitet mich Facebook wie folgt zu meiner Anwendung zurück:

Daher haben wir eine erfolgreiche soziale Authentifizierung gezeigt. Schließlich ist Social Login Teil jeder SaaS-Anwendung, die Sie erstellen.

Schlussfolgerung

Abschließend haben wir gezeigt, wie man ein Social-Login-Modul mit der Social-Funktion von Spring Boot erstellt. Außerdem wird der Code dafür hier zum Download zur Verfügung stehen.

Referenzen

  1. Spring Social Überblick – Spring Social
  2. Soziale Entwicklung im Frühling – Soziale Entwicklung im Frühling
  3. Frühlingssozial-Tutorial – Frühlingssozial-Tutorial


Java-Tag