将社交登录添加到Spring MVC Web应用程序:配置

过去,用户使用用户名和密码组合登录。 尽管如今有些人仍然偏爱传统方式,但越来越多的用户希望使用其社交媒体帐户登录。

这就是使Spring Social(及其子项目)成为Spring项目组合有用的补充的原因。 但是,将Spring Social与Spring Security集成起来有点麻烦。

Spring Social 1.1.0改变了这一切。 它提供了与Spring Security的无缝集成,并且Spring Security的Java配置支持使配置就像在公园里散步一样。

您不必相信我的话。 继续阅读,您将学到如何做到这一点。

我们解决方案的要求如下:

  • 必须可以使用常规注册表单创建用户帐户。
  • 必须有可能通过使用社交登录来创建用户帐户。
  • 必须可以使用用户名和密码登录。
  • 必须可以使用SaaS API提供程序进行登录。
  • 该应用程序必须支持Facebook和Twitter。
  • 应用程序必须使用“常规” Spring MVC控制器(无REST)。

让我们先看一下本教程的先决条件。

先决条件

本教程假定您已经创建了示例应用程序使用的Facebook和Twitter应用程序。 您可以通过以下链接创建这些应用程序:

  • Facebook开发人员
  • Twitter开发人员

如果您不知道该怎么做,可以查看以下链接:

  • Facebook开发人员–创建应用程序详细信息页面 (当系统询问您的应用程序如何与FB集成时,选择“使用Facebook登录名的网站”)。
  • 如何通过8个简单步骤创建Twitter应用程序 (启用“允许该应用程序用于通过Twitter登录”复选框)。

让我们继续前进,了解如何使用Maven获得所需的依赖关系。

使用Maven获取所需的依赖关系

我们要做的第一件事是使用Maven获得所需的依赖关系。 为此,我们可以在POM文件中声明以下依赖关系:

  • Spring Security(版本3.2.0.RC1)。
    • 核心模块包含核心身份验证和访问控制组件。
  • Apache HttpClient(版本4.2.5)。 Apache HttpClient是Spring Social的可选依赖项(但建议使用)。 如果存在,Spring Social会将其用作HTTP客户端。 否则,Spring social将使用标准的Java SE组件。
  • Spring社交(版本1.1.0.BUILD-SNAPSHOT)。
    • 核心模块包含连接框架,并为OAuth客户端提供支持。
  • Spring Social Facebook(版本1.1.0.BUILD-SNAPSHOT)是Spring Social的扩展,它提供Facebook集成。
  • Spring Social Twitter(版本1.1.0.BUILD-SNAPSHOT)是对Social Social的扩展,它提供了Twitter集成。

pom.xml文件的相关部分如下所示:

<!-- Spring Security -->
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-core</artifactId><version>3.2.0.RC1</version>
</dependency>
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>3.2.0.RC1</version>
</dependency>
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-taglibs</artifactId><version>3.2.0.RC1</version>
</dependency>
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>3.2.0.RC1</version>
</dependency><!-- Use Apache HttpClient as HTTP Client -->
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.2.5</version>
</dependency><!-- Spring Social -->
<dependency><groupId>org.springframework.social</groupId><artifactId>spring-social-core</artifactId><version>1.1.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>    <groupId>org.springframework.social</groupId><artifactId>spring-social-security</artifactId><version>1.1.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency><groupId>org.springframework.social</groupId><artifactId>spring-social-web</artifactId><version>1.1.0.BUILD-SNAPSHOT</version>
</dependency><!-- Spring Social Facebook -->
<dependency><groupId>org.springframework.social</groupId><artifactId>spring-social-facebook</artifactId><version>1.1.0.BUILD-SNAPSHOT</version>
</dependency><!-- Spring Social Twitter -->
<dependency><groupId>org.springframework.social</groupId><artifactId>spring-social-twitter</artifactId><version>1.1.0.BUILD-SNAPSHOT</version>
</dependency>

注意 :我们的应用程序还具有其他依赖项。 例如,它使用Spring Framework 3.2.4.RELEASE,Spring Data JPA 1.3.4和Hibernate 4.2.4.Final。 为了清楚起见,这些依赖项从依赖项列表中省略。 您可以从Github获取依赖项的完整列表 。

您可能还想阅读以下文档,这些文档为您提供了有关此博客文章(Spring Security和Spring Social)中讨论的框架的依赖性的更多信息:

  • Spring Security参考手册:1.4获取Spring Security
  • Spring社会参考手册:1.3如何获得
  • Spring Social Facebook参考手册:1.2如何获取
  • Spring Social Twitter参考手册:1.2如何获取

接下来,我们必须为应用程序的配置属性创建一个属性文件。 让我们找出这是如何完成的。

创建属性文件

我们可以按照以下步骤创建属性文件:

  1. 创建一个名为application.properties的文件,并确保从类路径中找到它。
  2. 配置数据库连接。
  3. 配置休眠。
  4. 将Facebook应用程序ID和应用程序密钥添加到属性文件。
  5. 将Twitter使用者密钥和使用者密钥添加到属性文件中。

application.properties文件的内容如下所示:

#Database Configuration
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/socialtwitter
db.username=socialtwitter
db.password=password#Hibernate Configuration
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.format_sql=true
hibernate.hbm2ddl.auto=validate
hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
hibernate.show_sql=false#Facebook
facebook.app.id=foo
facebook.app.secret=bar#Twitter
twitter.consumer.key=foo
twitter.consumer.secret=bar

在配置应用程序之前,我们必须创建一些通用组件。 让我们找出这些组件是什么,以及如何创建它们。

创建通用组件

我们必须创建在身份验证过程中使用的三个组件。 这些组件是:

  • 我们创建了一个类,其中包含经过身份验证的用户的用户详细信息。
  • 我们必须创建一个实现UserDetailsS​​ervice接口的类。 当用户使用表单登录时,此类用于加载用户信息。
  • 我们必须创建一个实现SocialUserDetailsS​​ervice接口的类。 当用户使用社交登录时,该类用于加载用户信息。

让我们继续前进,找出如何实现这些类。

创建用户详细信息类

在创建包含已认证用户的用户详细信息的类时,我们必须考虑以下要求:

  • 存储使用表单登录的用户的用户详细信息的类必须实现UserDetails接口。
  • 存储使用社交登录的用户的用户详细信息的类必须实现SocialUserDetails接口。

Spring Social具有满足这两个要求的SocialUser类。 但是,通常我们希望将特定于应用程序的信息添加到我们的用户详细信息类中。

我们可以按照以下步骤进行操作:

  1. 创建用户详细信息类。
  2. 扩展SocialUser类。
  3. 将应用程序特定的字段添加到创建的类。 我们的示例应用程序的特定于应用程序的字段是: idfirstNamelastNamerolesocialSignInProvider
  4. 创建一个构造函数,该构造函数将用户名,密码和授予的权限的集合作为参数。 将这些参数传递给SocialUser类的构造函数。
  5. 为特定于应用程序的字段创建吸气剂。
  6. 添加一个内部构建器类,该类用于构建新的ExampleUserDetails对象。

我们的用户详细信息类的源代码如下所示:

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.social.security.SocialUser;import java.util.Collection;
import java.util.HashSet;
import java.util.Set;public class ExampleUserDetails extends SocialUser {private Long id;private String firstName;private String lastName;private Role role;private SocialMediaService socialSignInProvider;public ExampleUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities) {super(username, password, authorities);}//Getters are omitted for the sake of clarity.public static class Builder {private Long id;private String username;private String firstName;private String lastName;private String password;private Role role;private SocialMediaService socialSignInProvider;private Set<GrantedAuthority> authorities;public Builder() {this.authorities = new HashSet<>();}public Builder firstName(String firstName) {this.firstName = firstName;return this;}public Builder id(Long id) {this.id = id;return this;}public Builder lastName(String lastName) {this.lastName = lastName;return this;}public Builder password(String password) {if (password == null) {password = "SocialUser";}this.password = password;return this;}public Builder role(Role role) {this.role = role;SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.toString());this.authorities.add(authority);return this;}public Builder socialSignInProvider(SocialMediaService socialSignInProvider) {this.socialSignInProvider = socialSignInProvider;return this;}public Builder username(String username) {this.username = username;return this;}public ExampleUserDetails build() {ExampleUserDetails user = new ExampleUserDetails(username, password, authorities);user.id = id;user.firstName = firstName;user.lastName = lastName;user.role = role;user.socialSignInProvider = socialSignInProvider;return user;}}
}

角色是一个简单的枚举,它指定示例应用程序的“合法”用户角色。 其源代码如下:

public enum Role {ROLE_USER
}

SocialMediaService是一个枚举,用于标识用户为我们的示例应用程序创建用户帐户时使用的SaaS API提供程序。 其源代码如下:

public enum SocialMediaService {FACEBOOK,TWITTER
}

实现UserDetailsS​​ervice接口

通过执行以下步骤,我们可以创建自己的UserDetailsS​​ervice接口实现:

  1. 创建一个实现UserDetailsS​​ervice接口的类。
  2. UserRepository字段添加到创建的类。
  3. 创建一个将UserRepository作为构造函数参数的构造函数,并使用@Autowired注释对构造函数进行注释。
  4. 实现UserDetailsS​​ervice接口的loadUserByUsername(String username)方法。 此方法的实现包括以下步骤:
    1. 通过调用UserRepository接口的findByEmail()方法来获取用户。 此方法返回其电子邮件与作为方法参数给出的用户名匹配的用户。
    2. 如果找不到用户,则抛出新的UsernameNotFoundException
    3. 创建一个新的ExampleUserDetails对象。
    4. 返回创建的对象。

RepositoryUserDetailsS​​ervice类的源代码如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;public class RepositoryUserDetailsService implements UserDetailsService {private UserRepository repository;@Autowiredpublic RepositoryUserDetailsService(UserRepository repository) {this.repository = repository;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = repository.findByEmail(username);if (user == null) {throw new UsernameNotFoundException("No user found with username: " + username);}ExampleUserDetails principal = ExampleUserDetails.getBuilder().firstName(user.getFirstName()).id(user.getId()).lastName(user.getLastName()).password(user.getPassword()).role(user.getRole()).socialSignInProvider(user.getSignInProvider()).username(user.getEmail()).build();return principal;}
}

UserRepository是一个简单的Spring Data JPA存储库,其源代码如下所示:

import org.springframework.data.jpa.repository.JpaRepository;public interface UserRepository extends JpaRepository<User, Long> {public User findByEmail(String email);
}

用户是我们的示例应用程序的唯一实体,它包含为我们的示例应用程序创建了用户帐户的用户的信息。 其源代码的相关部分如下所示:

import javax.persistence.*;@Entity
@Table(name = "users")
public class User extends BaseEntity<Long> {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(name = "email", length = 100, nullable = false, unique = true)private String email;@Column(name = "first_name", length = 100,nullable = false)private String firstName;@Column(name = "last_name", length = 100, nullable = false)private String lastName;@Column(name = "password", length = 255)private String password;@Enumerated(EnumType.STRING)@Column(name = "role", length = 20, nullable = false)private Role role;@Enumerated(EnumType.STRING)@Column(name = "sign_in_provider", length = 20)private SocialMediaService signInProvider;public User() {}//Getters and other methods are omitted for the sake of clarity.
}

实现SocialUserDetailsS​​ervice接口

我们可以通过执行以下步骤来实现SocialUserDetailsS​​ervice接口:

  1. 创建一个实现SocialUserDetailsS​​ervice的类。
  2. UserDetailsS​​ervice字段添加到创建的类。
  3. 创建一个将UserDetailsS​​ervice对象作为构造函数参数的构造函数,并使用@Autowired注释对构造函数进行注释。
  4. 实现SocialUserDetailsInterfaceloadUserByUserId(String userId)方法。
  5. 通过调用loadUserByUsername()方法获取正确的UserDetails对象,并将用户ID作为方法参数传递。 我们可以这样做是因为我们的应用程序使用用户的用户名作为用户ID。
  6. 将返回的对象强制转换SocialUserDetails对象并返回。

SimpleSocialUserDetailsS​​ervice类的源代码如下所示:

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.social.security.SocialUser;
import org.springframework.social.security.SocialUserDetails;
import org.springframework.social.security.SocialUserDetailsService;public class SimpleSocialUserDetailsService implements SocialUserDetailsService {private UserDetailsService userDetailsService;public SimpleSocialUserDetailsService(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}@Overridepublic SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException, DataAccessException {UserDetails userDetails = userDetailsService.loadUserByUsername(userId);return (SocialUserDetails) userDetails;}
}

就这些。 现在,我们准备配置应用程序的应用程序上下文。 让我们找出如何做到这一点。

配置应用程序上下文

本节介绍如何使用Java配置来配置示例应用程序的应用程序上下文。 遵循以下准则,将应用程序上下文配置分为多个配置类:

  1. 每个配置类都包含与示例应用程序的特定部分相关联的配置。 如果我们必须在创建初始配置后的几个月(或几年)内检出某项内容或进行某些更改,则可以轻松找到相关的配置。
  2. 对配置进行了划分,使之可以通过使用Spring Test MVC轻松编写Web层的单元测试。 我们将在本教程的第三部分中进一步讨论这一点,在该部分中,我们将为应用程序的Web层编写单元测试。
  3. 当我们为应用程序编写集成测试时,使用该配置可以轻松删除对外部资源的依赖。 我们将在本教程的第四部分中进一步讨论这一点,该教程描述了如何为应用程序编写集成测试。

注意 :如果要使用XML配置,可以查看此博客文章的示例应用程序,该示例应用程序也具有有效的XML配置 (尽管没有web.xml)。

让我们从配置应用程序的持久层开始。

配置持久层

我们应用程序的持久层存储用户帐户信息,并提供一种访问此信息的方法。 这很重要,原因有两个:

  • 我们可以提供一种使用用户名和密码登录的方法。
  • 我们可以存储特定于应用程序的信息,并将此信息链接到使用社交登录的用户。

让我们找出如何通过使用两个Java配置类来配置它。

注意 :示例应用程序的持久层使用Spring Data JPA 1.3.4。 我将使该部分尽可能的薄。 如果您想了解有关Spring Data JPA的更多信息,可以阅读我的Spring Data JPA教程 。 我还写了一本关于Spring Data的书,它应该可以帮助您立即开始使用。

我们可以按照以下步骤配置持久层:

  1. 创建配置类,并使用@Configuration注释对创建的类进行注释。
  2. @EnableJpaRepositories批注为类注解,并设置Spring Data JPA信息库的基本包。
  3. 通过使用@EnableTransactionManagement批注注释配置类来启用Spring事务管理。
  4. 在类中添加一个Environment字段,并使用@Autowired批注对该字段进行批注。 我们不需要使用@PropertySource批注来配置属性文件,因为已经在“父”应用程序上下文配置类中对其进行了配置。
  5. 配置数据源bean。 这个bean提供了到实体管理器的数据库连接,但是它还有另一个目的。 当Spring Social保持与数据库的连接并从数据库加载它们的连接时,将使用它。
  6. 配置事务管理器bean。
  7. 配置实体管理器工厂bean。

PersistenceContext类的源代码如下所示:

import com.jolbox.bonecp.BoneCPDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Properties;@Configuration
@EnableJpaRepositories(basePackages = {"net.petrikainulainen.spring.social.signinmvc.user.repository"
})
@EnableTransactionManagement
public class PersistenceContext {@Resourceprivate Environment env;@Beanpublic DataSource dataSource() {BoneCPDataSource dataSource = new BoneCPDataSource();dataSource.setDriverClass(env.getRequiredProperty("db.driver"));dataSource.setJdbcUrl(env.getRequiredProperty("db.url"));dataSource.setUsername(env.getRequiredProperty("db.username"));dataSource.setPassword(env.getRequiredProperty("db.password"));return dataSource;}@Beanpublic JpaTransactionManager transactionManager() {JpaTransactionManager transactionManager = new JpaTransactionManager();transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());return transactionManager;}@Beanpublic LocalContainerEntityManagerFactoryBean entityManagerFactory() {LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();entityManagerFactoryBean.setDataSource(dataSource());entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());entityManagerFactoryBean.setPackagesToScan({"net.petrikainulainen.spring.social.signinmvc.common.model","net.petrikainulainen.spring.social.signinmvc.user.model"});Properties jpaProperties = new Properties();jpaProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));jpaProperties.put("hibernate.format_sql", env.getRequiredProperty("hibernate.format_sql"));jpaProperties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty("hibernate.hbm2ddl.auto"));jpaProperties.put("hibernate.ejb.naming_strategy", env.getRequiredProperty("hibernate.ejb.naming_strategy"));jpaProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));entityManagerFactoryBean.setJpaProperties(jpaProperties);return entityManagerFactoryBean;}
}

让我们继续研究如何为应用程序创建安全配置。

配置Spring Security

Spring Security为使用表单登录或社交登录的用户提供身份验证机制,并且还负责授权。

我们可以按照以下步骤配置Spring Security:

  1. 创建配置类,并使用@Configuration注释对创建的类进行注释。
  2. @EnableWebSecurity注释对类进行注释。 这样就可以通过实现WebSecurityConfigurer接口来配置Spring Security。
  3. 确保我们的配置类扩展了WebSecurityConfigurerAdapter类,该类是用于创建WebSecurityConfigurer实例的基类。 完成此操作后,我们可以通过覆盖方法来自定义安全配置。
  4. ApplicationContext字段添加到配置类,并使用@Autowired批注对该字段进行批注。
  5. UserRepository字段添加到配置中,并使用@Autowired注释对该字段进行注释。
  6. 重写WebSecurityConfigurerAdapter类的configure(WebSecurity web)方法。 确保Spring Security忽略对静态资源(例如CSS和Javascript文件)的请求。
  7. 重写WebSecurityConfigurerAdapter类的configure(HttpSecurity http)方法,并通过以下步骤实现它:
    1. 通过执行以下步骤配置表单登录:
      1. 将登录页面网址设置为“ / login”。
      2. 将处理登录表单提交的URL设置为“ / login / authenticate”。
      3. 将登录失败网址设置为“ / login?error = bad_credentials”。
    2. 通过执行以下步骤配置注销功能:
      1. 确保注销后删除名为JSESSIONID的cookie。
      2. 将注销网址设置为“ / logout”。
      3. 将注销成功URL设置为“ / login”。
    3. 配置基于url的授权。 此阶段的主要目的是确保匿名用户可以访问与登录/注册过程相关的所有URL,并保护应用程序的其余部分不受匿名用户的攻击。
    4. SocialAuthenticationFilter添加到Spring Security过滤器链。 为此,我们可以创建一个新的SpringSocialConfigurer对象,并确保在配置Spring Security时使用该对象。
    5. ApplicationContext字段的值设置为所有SecurityConfigurer实例共享的对象。
  8. 配置用来对用户密码进行哈希处理的PasswordEncoder bean(如果用户使用表单注册和登录)。 我们可以通过创建一个新的BCryptPasswordEncoder对象并返回创建的对象来做到这一点。
  9. 配置UserDetailsS​​ervice Bean。 我们可以通过创建一个新的RepositoryUserDetailsS​​ervice对象并将UserRepository作为构造函数参数来实现。
  10. 重写WebSecurityConfigurerAdapter类的registerAuthentication(AuthenticationManagerBuilder auth)方法。 如果用户使用表单登录,我们将使用此方法配置身份验证请求。 通过执行以下步骤来实现此方法:
    1. UserDetailsS​​ervice bean传递给作为方法参数给出的AuthenticationManagerBuilder对象。
    2. PasswordEncoder bean传递给作为方法参数给出的AuthenticationManagerBuilder对象。
  11. 配置SocialUserDetailsS​​ervice bean。 我们可以通过创建一个新的SimpleSocialUserDetailsS​​ervice对象并将UserDetailsS​​ervice bean作为构造函数参数来实现。 使用社交登录时,此bean加载用户特定的数据。

我们的应用程序上下文配置类的源代码如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.social.security.SocialUserDetailsService;
import org.springframework.social.security.SpringSocialConfigurer;@Configuration
@EnableWebSecurity
public class SecurityContext extends WebSecurityConfigurerAdapter {@Autowiredprivate ApplicationContext context;@Autowiredprivate UserRepository userRepository;@Overridepublic void configure(WebSecurity web) throws Exception {web//Spring Security ignores request to static resources such as CSS or JS files..ignoring().antMatchers("/static/**");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http//Configures form login.formLogin().loginPage("/login").loginProcessingUrl("/login/authenticate").failureUrl("/login?error=bad_credentials")//Configures the logout function.and().logout().deleteCookies("JSESSIONID").logoutUrl("/logout").logoutSuccessUrl("/login")//Configures url based authorization.and().authorizeRequests()//Anyone can access the urls.antMatchers("/auth/**","/login","/signin/**","/signup/**","/user/register/**").permitAll()//The rest of the our application is protected..antMatchers("/**").hasRole("USER")//Adds the SocialAuthenticationFilter to Spring Security's filter chain..and().apply(new SpringSocialConfigurer()).and().setSharedObject(ApplicationContext.class, context);}@Overrideprotected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder(10);}@Beanpublic SocialUserDetailsService socialUserDetailsService() {return new SimpleSocialUserDetailsService(userDetailsService());}@Beanpublic UserDetailsService userDetailsService() {return new RepositoryUserDetailsService(userRepository);}
}

让我们继续前进,了解如何配置Spring Social。

配置Spring Social

Spring Social提供与Facebook和Twitter等SaaS API提供程序的集成。 我们可以按照以下步骤配置Spring Social:

  1. 创建实现SocialConfigurer接口的应用程序上下文配置类,并使用@Configuration注释对创建的类进行注释。 SocialConfigurer接口声明了可用于配置Spring Social的回调方法。
  2. @EnableSocial批注对类进行批注。 这将启用Spring Social并导入SocialConfiguration配置类。
  3. @Profile注释为类添加注释,并将字符串“ application”设置为其值。 这样可以确保仅在活动Spring概要文件为“ application”时使用此应用程序上下文配置类。 这很重要,因为它使我们无需依赖Facebook或Twitter即可为我们的应用程序编写集成测试。
  4. DataSource字段添加到配置类,并使用@Autowired批注对该字段进行批注。
  5. SocialConfigurer界面的addConnectionFactories()方法添加到创建的配置类中。 此方法采用以下两个方法参数:
    1. 第一个参数是ConnectionFactoryConfigurer对象,可用于注册连接工厂。
    2. 第二个参数是一个Environment对象,它代表示例应用程序在其中运行的环境。
  6. 通过执行以下步骤来实现addConnectionFactories()方法:
    1. 创建一个新的TwitterConnectionFactory对象,并将使用者密钥和使用者密钥作为构造函数参数传递。
    2. 通过调用ConnectionFactoryConfigurer接口的addConnectionFactory()方法来注册创建的TwitterConnectionFactory对象。 将创建的TwitterConnectionFactory对象作为方法参数传递。
    3. 创建一个新的FacebookConnectionFactory对象,并将应用程序ID和应用程序密钥作为构造函数参数传递。
    4. 通过调用ConnectionFactoryConfigurer接口的addConnectionFactory方法来注册创建的FacebookConnectionFactory对象。 将创建的FacebookConnectionFactory对象作为方法参数传递。
  7. SocialConfigurer接口的getUserIdSource()方法添加到创建的类中。 此方法返回的UserIdSource对象负责确定用户的正确帐户ID。 因为我们的示例应用程序使用用户的用户名作为帐户ID,所以我们必须通过返回新的AuthenticationNameUserIdSource对象来实现此方法。
  8. SocialConfigurer接口的getUsersConnectionRepository()方法添加到创建的类中。 此方法将ConnectionFactoryLocator对象作为方法参数,并返回UsersConnectionRepository对象。
  9. 通过执行以下步骤来实现getUsersConnectionRepository()方法:
    1. 创建一个新的JdbcUsersConnectionRepository对象,并将以下对象作为构造函数参数传递:
      1. 第一个参数是DataSource对象。 我们将dataSource字段的值作为第一个方法参数传递。
      2. 第二个参数是ConnectionFactoryLocator对象。 我们将connectionFactoryLocator方法参数的值作为第二个方法参数传递。
      3. 第三个参数是TextEncryptor对象,该对象加密SaaS API提供程序与我们的应用程序之间建立的连接的授权详细信息。 我们通过调用Encryptors类的noOpText()方法来创建此对象。 这意味着我们的示例应用程序将这些详细信息存储为纯文本。 这在开发阶段很方便,但是我们不应该在生产中使用它
    2. 返回创建的对象。
  10. 配置ConnectController bean。 配置此bean的方法有两个参数。 第一个参数是ConnectionFactoryLocator bean。 第二个参数是使用的ConnectionRepository bean。 在创建新的ConnectController对象时,将这些参数作为构造函数参数传递。

我们的配置类的源代码如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.security.crypto.encrypt.Encryptors;
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.jdbc.JdbcUsersConnectionRepository;
import org.springframework.social.connect.web.ConnectController;
import org.springframework.social.facebook.connect.FacebookConnectionFactory;
import org.springframework.social.security.AuthenticationNameUserIdSource;
import org.springframework.social.twitter.connect.TwitterConnectionFactory;import javax.sql.DataSource;@Configuration
@EnableSocial
@Profile("application")
public class SocialContext implements SocialConfigurer {@Autowiredprivate DataSource dataSource;@Overridepublic void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {cfConfig.addConnectionFactory(new TwitterConnectionFactory(env.getProperty("twitter.consumer.key"),env.getProperty("twitter.consumer.secret")));cfConfig.addConnectionFactory(new FacebookConnectionFactory(env.getProperty("facebook.app.id"),env.getProperty("facebook.app.secret")));}@Overridepublic UserIdSource getUserIdSource() {return new AuthenticationNameUserIdSource();}@Overridepublic UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {return new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator,Encryptors.noOpText());}@Beanpublic ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {return new ConnectController(connectionFactoryLocator, connectionRepository);}
}

下一步是配置应用程序的Web层。 让我们开始工作。

配置Web层

我们可以按照以下步骤配置应用程序的Web层:

  1. 通过执行以下步骤来创建配置类:
    1. 扩展WebMvcConfigurerAdapter类。
    2. @Configuration注释对创建的类进行注释。
  2. 通过使用@ComponentScan批注注释该类并设置控制器的基本软件包,确保找到所有控制器类。
  3. 通过使用@EnableWebMvc注释对类进行注释来启用注释驱动的Web mvc。
  4. 确保容器的默认Servlet提供静态资源。
    1. 通过重写WebMvcConfigurerAdapter类的addResourceHandlers()方法来配置静态资源。
    2. 确保对静态资源的请求被转发到容器的默认Servlet。 这是通过重写WebMvcConfigurerAdapter类的configureDefaultServletHandling()方法来完成的。
  5. 配置异常解析器 bean。
  6. 配置ViewResolver bean。

WebAppContext类的源代码如下所示:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;import java.util.Properties;@Configuration
@ComponentScan(basePackages = {"net.petrikainulainen.spring.social.signinmvc.common.controller","net.petrikainulainen.spring.social.signinmvc.security.controller","net.petrikainulainen.spring.social.signinmvc.user.controller"
})
@EnableWebMvc
public class WebAppContext extends WebMvcConfigurerAdapter {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("/static/");}@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}@Beanpublic SimpleMappingExceptionResolver exceptionResolver() {SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();Properties exceptionMappings = new Properties();exceptionMappings.put("java.lang.Exception", "error/error");exceptionMappings.put("java.lang.RuntimeException", "error/error");exceptionResolver.setExceptionMappings(exceptionMappings);Properties statusCodes = new Properties();statusCodes.put("error/404", "404");statusCodes.put("error/error", "500");exceptionResolver.setStatusCodes(statusCodes);return exceptionResolver;}@Beanpublic ViewResolver viewResolver() {InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setViewClass(JstlView.class);viewResolver.setPrefix("/WEB-INF/jsp/");viewResolver.setSuffix(".jsp");return viewResolver;}
}

让我们找出如何将所有这些结合在一起,并为我们的应用程序创建一个“父”应用程序上下文配置类。

绑在一起

最后一个应用程序上下文配置类具有三个职责:

  1. 它配置了整个示例应用程序中使用的常规组件。
  2. 它确保在类路径扫描期间找到我们应用程序的服务类。
  3. 它是我们应用程序的根应用程序上下文配置类。

我们可以按照以下步骤创建此配置类:

  1. 创建配置类,并使用@Configuration注释对创建的类进行注释。
  2. 通过使用@ComponentScan批注注释该类并设置我们的服务的基本包,确保在组件扫描期间找到我们的服务类。
  3. 通过使用@Import批注对类进行导入,以导入其他应用程序上下文配置类。
  4. @PropertySource注释对类进行注释,并将其配置为从类路径中查找名为application.properties的属性文件。 这样可以确保可以在导入的应用程序上下文配置类中访问配置属性。
  5. 配置MessageSource bean。
  6. 配置PropertyPlaceHolderConfigurer bean。

ExampleApplicationContext类的源代码如下所示:

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.context.support.ResourceBundleMessageSource;@Configuration
@ComponentScan(basePackages = {"net.petrikainulainen.spring.social.signinmvc.user.service"
})
@Import({WebAppContext.class, PersistenceContext.class, SecurityContext.class, SocialContext.class})
@PropertySource("classpath:application.properties")
public class ExampleApplicationContext {@Beanpublic MessageSource messageSource() {ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();messageSource.setBasename("i18n/messages");messageSource.setUseCodeAsDefaultMessage(true);return messageSource;}@Beanpublic PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}
}

现在,我们已经配置了示例应用程序的应用程序上下文。 但是,我们仍然必须配置Web应用程序。 让我们看看如何使用Java配置来做到这一点。

配置Web应用程序

我们的最后一步是配置示例应用程序。 只要将应用程序部署到Servlet 3.0兼容容器中,我们就可以在没有web.xml的情况下执行此操作。

我们可以按照以下步骤配置Web应用程序:

  1. 创建一个实现WebApplicationInitializer接口的类。
  2. 通过覆盖WebApplicationInitializer接口的onStartup()方法来配置我们的应用程序。 我们可以通过执行以下步骤来实现此方法:
      1. 创建应用程序的根上下文,并将ExampleApplicationContext类注册到创建的根上下文。
      2. 配置调度程序servlet 。

    C)配置字符编码过滤器 。
    D)配置Spring Security过滤器链 。
    E)配置Sitemesh 。 F)将上下文加载器侦听器添加到Servlet上下文。

ExampleApplicationConfig类的源代码如下所示:

import org.sitemesh.config.ConfigurableSiteMeshFilter;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;import javax.servlet.*;
import java.util.EnumSet;public class ExampleApplicationConfig implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();rootContext.register(ExampleApplicationContext.class);ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));dispatcher.setLoadOnStartup(1);dispatcher.addMapping("/");EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();characterEncodingFilter.setEncoding("UTF-8");characterEncodingFilter.setForceEncoding(true);FilterRegistration.Dynamic characterEncoding = servletContext.addFilter("characterEncoding", characterEncodingFilter);characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");FilterRegistration.Dynamic security = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy());security.addMappingForUrlPatterns(dispatcherTypes, true, "/*");FilterRegistration.Dynamic sitemesh = servletContext.addFilter("sitemesh", new ConfigurableSiteMeshFilter());sitemesh.addMappingForUrlPatterns(dispatcherTypes, true, "*.jsp");servletContext.addListener(new ContextLoaderListener(rootContext));}
}

下一步是什么?

现在,我们已经使用Java配置成功配置了示例应用程序。 本教程教了我们两件事:

  • 我们学习了如何实现Spring Security和Spring Social所需的组件。
  • 我们学习了通过使用Java配置来集成Spring Security和Spring Social。

本教程的下一部分描述了如何向示例应用程序添加注册和身份验证功能。

PS与往常一样,此博客文章的示例应用程序可在Github上获得 。

参考: 在Spring MVC Web应用程序中添加社交登录:来自Petri Kainulainen博客的JCG合作伙伴 Petri Kainulainen进行的配置 。

翻译自: https://www.javacodegeeks.com/2013/10/adding-social-sign-in-to-a-spring-mvc-web-application-configuration.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[leetcode] 35. 搜索插入位置(Java)(二分)

35. 搜索插入位置 二分&#xff0c;太简单&#xff0c;没啥好说的 class Solution {public int searchInsert(int[] nums, int target) {if (nums.length 0) return 0;int i 0, j nums.length;int mid (i j) / 2;while (i < j) {if (nums[mid] target) {return mid;} …

UI设计中颜色的前进色与后退色

暖色调的颜色属于前进色。膨胀色可以使物体的视觉效果变大&#xff0c;而收缩色可以使物体的视觉效果变小。颜色的另外一种效果。有的颜色看起来向上凸出&#xff0c;而有的颜色看起来向下凹陷&#xff0c;其中显得凸出的颜色被称为前进色&#xff0c;而显得凹陷的颜色被称为后…

手型向下 点击一下 福昕_PPT多张缩略图点击放大展示

↑点击上方“菜鸟PPT”关注&#xff0c;教你玩转PPT&#xff01;多张图片单击放大展示&#xff0c;以前做过一个比较复杂的&#xff0c;在同一页PPT里面做&#xff0c;加了很多个“进入”和“退出”的动画&#xff0c;维护起来比较麻烦。今天&#xff0c;菜鸟菌跟大家一起来学习…

6个经典的JavaScript报错分析

代码报错是经常发生的一件事&#xff0c;我们要确定是什么原因造成的&#xff0c;以及如何避免错误。 1. Uncaught TypeError: Cannot read property 该错误说明没有某个属性&#xff0c;一般是该属性前面的值是undefined或者是null的情况会出现。 2. TypeError: ‘undefined…

编写自动调试器以在测试执行期间捕获异常

以前&#xff0c;我曾说过&#xff0c; 您总是想保留一些调试器断点作为例外 。 此帮助可防止代码在不引起注意的情况下腐烂掉-有时掩盖了另一个问题。 如果您认真对待这一点&#xff0c;则最好将此概念扩展到自动化测试中。 但是想出一个全面的解决方案并不简单。 您可以仅从…

git显示服务器所有分支,git 查看所有远程分支以及同步

在多台电脑使用git管理开发分支的时候&#xff0c;会出现这样的情况。电脑A创建了分支1&#xff0c;并且push上了远程仓库。电脑B本地clone仓库默认只会clone下master分支&#xff0c;而其他电脑A推送的分支是不会默认同步下来的。那么如何同步呢&#xff1f;查看电脑B本地仓库…

Vue中浏览器的的前进和后退

项目开发的时候&#xff0c;有时候可能需要我们来对页面后退和前进&#xff0c;这个东西跟浏览器自带的前进后退功能很像,下面来大致讲一下在vue中浏览器的前进和后退 一、后退功能 vue中的后退有好多种方法可以使用&#xff0c;使用这些方法前要确认有之前的页面&#xff0c;否…

pandas:DataFrname(三)

pandas:从文件读取 读取文件&#xff1b;从文件名。url,文件对象中加载数据 read_csv 默认分隔符为逗号 read_table 默认分隔符为\t读取文件函数主要参数 sep 指定分隔符&#xff0c;可用正则表达式 headerNone 指定文件无列名 names 指定列名 index_col 指定某列作为索引 …

json-schema 可视化编辑器发布了

json-schema 的用途越来越广泛&#xff0c;除了定义数据结构外&#xff0c;我们还可以使用 json-schema 验证数据格式和生成随机数据&#xff0c;但是编写复杂数据结构的 json-schema 是非常痛苦的事情。假设一个 100 字段的数据结构&#xff0c;如果用 json-schema 定义&#…

tfw文件如何导入cad_如何将CAD的线稿导入PS并和底色分离

【新朋友】 点击标题下方的 CG伴学 迅速关注【老朋友】 点击右上角的按钮 分享 或者 收藏这是 【CG伴学 】制作的第249篇答疑教程观看往期视频教程请点击或者前往公众号自定义菜单【学习中心】我们专门制作了教程汇总目录&#xff0c;你可以根据标题关键词搜索获取我们在后台收…

HOW-TO:在Spring 4和Java 7中使用@PropertySource批注

今天&#xff0c;我将我当前正在从事的项目之一迁移到了Spring 4.0。 由于它是我用来学习和演示Spring功能的非常简单的Web应用程序&#xff0c;因此只需要更新项目的POM文件并更改Spring版本。 我将项目部署到Tomcat 7服务器&#xff0c;显然该应用程序未启动。 我在IntelliJ控…

Python全栈工程师(函数嵌套、变量作用域)

ParisGabriel 感谢 大家的支持 每天坚持 一天一篇 点个订阅吧 灰常感谢 当个死粉也阔以 Python人工智能从入门到精通 globals&#xff08;&#xff09;/locals&#xff08;&#xff09;函数&#xff1a;     globals&#xff08;&#xff09;返回当前全局作用域内变量…

可视化分析js的内存分配与回收

之前写了一篇文章浏览器是怎么看闭包的&#xff0c;发现有些读者对js内存分配与回收懵懵懂懂&#xff0c;理解文章的配图有些困难&#xff0c;我想主要是因为配图省略了一些细节。今天专门写一篇关于js内存分配回收的文章&#xff0c;帮助大家理解js代码的内存表示。原文备份在…

记录程序写入日志_终于有人把MySQL 三大日志讲清楚了

点击上方"蓝字"&#xff0c;关注了解更多日志是 mysql 数据库的重要组成部分&#xff0c;记录着数据库运行期间各种状态信息。mysql日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。作为开发&#xff0c;我们重点需要关注的是二进制日志( …

在ADF实体PK属性中使用MySQL自动增量PK列

大家好。 继续进行ADF MySQL解决方法系列&#xff0c;今天我们将看到要使用MySQL PK自动增量列和ADF实体PK属性来进行的工作。 如果使用的是Oracle数据库&#xff0c;则可以使用oracle.jbo.domain.DBSequence以及序列和触发器来立即进行操作。 为简单起见&#xff0c;我们将修…

HBuilder的快捷操作

1.通过&#xff01;感叹号单击tab 就可以快速生成&#xff1a; <!DOCTYPE html><html lang"zh"><head> <meta charset"UTF-8" /> <meta name"viewport" content"widthdevice-width, initial-scale1.0" /&g…

14.正则表达式、re模块、元字符

正则表达式 一、正则表达式所面向的问题 1、判断一个字符串是否匹配给定的格式 判断用户注册帐号是否满足格式 2、从一个字符串中按指定格式提取信息 抓取页面中的链接 二、判断用户提交的邮箱的格式是否正确 三、抓取页面中特定部分数据 er模块 1、findall方法&a…

WeScale 技术篇 —— mpvue 与微信小程序的火花

介绍项目介绍WeScale 定位为音乐训练小程序&#xff0c;初期规划了基础音阶的三个训练&#xff0c;以及他们的镜像模式。数字简谱字母简谱数字简谱对字母简谱后期看情况更新追加其他训练。产品展示扫描下方小程序码或在微信小程序中搜索 WeScale&#xff0c;即可使用。人员介绍…

使用Maven程序集创建漏洞评估工件

本文将讨论如何使用Maven程序集创建可提供给第三方漏洞评估站点&#xff08;例如Veracode &#xff09;进行审查的工件。 错误的静态分析与漏洞评估 在这一点上&#xff0c;每个人都知道findbug并虔诚地使用它&#xff0c;对吗&#xff1f; 对&#xff1f; Findbugs使用静态…

洛谷 P3835: 【模板】可持久化平衡树

题目传送门&#xff1a;洛谷P3835。 题意简述&#xff1a; 题面说的很清楚了。 题解&#xff1a; 考虑建立一棵每个节点都表示一个版本的树。 以初始版本 \(0\) 为根。对于第 \(i\) 个操作&#xff0c;从 \(v_i\) 向 \(i\) 连一条边&#xff0c;而边权则是 \(opt_i\) 和 \(x_i\…