Пример OAuth2 Social Login в Spring Boot.
1. Цель статьи
Традиционный способ, который должен пройти пользователь, чтобы получить доступ в защищенный website это создать аккаунт, потом войти в их email для активации аккаунта, запомнить логин и пароль, большинство пользователей забывают данную информацию после определенного времени. Информация, которую пользователь должен ввести для создания аккаунта иногда может его демотивировать.

Теперь очень распространен вход в website через социальную сеть, который используется многими website. Данным способом пользователь может быстро создать аккаунт на вашем website, вашему приложению так же не нужно отправлять email активации аккаунта пользователю.

В первый раз, когда пользователь использует аккаунт социальной сети для входа в ваш website, у вас есть 3 разных выбора для решения.
- Выбор 1: Их аккаунт социальной сети будет принят и ваш website не выполняет абсолютно никаких больше действий.
- Выбор 2: Основываясь на публичной информации аккаунта пользователя в социальной сети, ваш website автоматически создает соответствующий аккаун.
- Выбор 3: Ваш Website перенаправляет пользователя к странице регистрации с информацией по умолчанию полученной из их аккаунта социальной сети, это позволяет пользователю не заполнять всю информацию.
В данной статье мы создадим приложение с Spring Boot + JPA + Spring Social Security. Ниже является изображение предварительного просмотра приложения.
ВЫБОР 2:
В случае когда пользователь в первый раз регистрируется с аккаунтом социальной сети, автоматически создается запись APP_USER, с автоматическим User_Name созданным системой.

ВЫБОР 3:
В случае если пользователь в первый раз входит в систему используя социальную сеть, система перенаправляет пользователя к странице регистрации с информацией по умолчанию, полученной с аккаунта пользователя в социальной сети. Пользователь может менять информацию User_Name, First Name, Last Name, ... После, пользователь нажимает на "Submit" созданную запись APP_USER.

2. Создать проект Spring Boot
На Eclipse, создать проект Spring Boot.

База данных использованная в данном приложении это MySQL, SQL Server, PostGres или Oracle или любая другая база данных, которая вам знакома.

Объявить библиотеки Spring Social в ваш проект:
- spring-security-oauth2
- spring-social-core
- spring-social-config
- spring-social-security
- spring-social-facebook
- spring-social-twitter
- spring-social-github
- spring-social-linkedin
- spring-social-google
Полное содержиние файла pom.xml:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.o7planning</groupId>
<artifactId>SpringBootSocialJPA</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBootSocialJPA</name>
<description>Spring Boot + Oauth2 Social + JPA</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- http://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-core -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-core</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-config -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-config</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-web -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-web</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-security -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-facebook -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-facebook</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-twitter -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-twitter</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-github -->
<!-- <dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-github</artifactId>
<version>1.0.0.M4</version>
</dependency> -->
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-linkedin -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-linkedin</artifactId>
<version>1.0.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.social/spring-social-google -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-google</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<!-- SQL Server Mssql-Jdbc Driver -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SQL Server JTDS Driver -->
<!-- https://mvnrepository.com/artifact/net.sourceforge.jtds/jtds -->
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.3.1</version>
</dependency>
<!-- Email validator,... -->
<!-- http://mvnrepository.com/artifact/commons-validator/commons-validator%20 -->
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<!-- Repository for ORACLE JDBC Driver -->
<repository>
<id>codelds</id>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SpringBootSocialJpaApplication.java
package org.o7planning.sbsocial;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootSocialJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSocialJpaApplication.class, args);
}
}
3. Конфигурация DataSource
Информация базы данных, которую необходимо конфигурировать в файле application.properties:
application.properties (MySQL)
spring.thymeleaf.cache=false
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/social
spring.datasource.username=root
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
application.properties (Mssql-Jdbc Driver)
spring.thymeleaf.cache=false
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.url=jdbc:sqlserver://tran-vmware-pc\\SQLEXPRESS:1433;databaseName=social
spring.datasource.username=sa
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
application.properties (PostGres)
spring.thymeleaf.cache=false
# ===============================
# DATABASE CONNECTION
# ===============================
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://tran-vmware-pc:5432/social
spring.datasource.username=postgres
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# Fix Postgres JPA Error:
# Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
application.properties (Oracle)
spring.thymeleaf.cache=false
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:db12c
spring.datasource.username=social
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
4. Конфигурировать Security & Spring Social

Конфигурировать Spring Security:
WebSecurityConfig.java
package org.o7planning.sbsocial.config;
import org.o7planning.sbsocial.entity.AppRole;
import org.springframework.beans.factory.annotation.Autowired;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.social.security.SpringSocialConfigurer;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// Pages do not require login
http.authorizeRequests().antMatchers("/", "/signup", "/login", "/logout").permitAll();
http.authorizeRequests().antMatchers("/userInfo").access("hasRole('" + AppRole.ROLE_USER + "')");
// For ADMIN only.
http.authorizeRequests().antMatchers("/admin").access("hasRole('" + AppRole.ROLE_ADMIN + "')");
// When the user has logged in as XX.
// But access a page that requires role YY,
// AccessDeniedException will be thrown.
http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/403");
// Form Login config
http.authorizeRequests().and().formLogin()//
// Submit URL of login page.
.loginProcessingUrl("/j_spring_security_check") // Submit URL
.loginPage("/login")//
.defaultSuccessUrl("/userInfo")//
.failureUrl("/login?error=true")//
.usernameParameter("username")//
.passwordParameter("password");
// Logout Config
http.authorizeRequests().and().logout().logoutUrl("/logout").logoutSuccessUrl("/");
// Spring Social Config.
http.apply(new SpringSocialConfigurer())
//
.signupUrl("/signup");
}
// This bean is load the user specific data when form login is used.
@Override
public UserDetailsService userDetailsService() {
return userDetailsService;
}
}
Чтобы пользователь мог регистрировать через социальную сеть в вашем website, на каждой социальной сети вам нужно зарегистрировать сертификаты (certificate) OAuth2, после регистрации у вас будет пара "Ключ + Пароль". Объявите эту пару ключ и пароль в файле social-cfg.properties:

Внизу я зарегистрировал сертификаты (certificate) OAuth2 чтобы данное приложение могло сделать логин через некоторые распространенные социальные сети. Но работает только если данное приложение работает на http://localhost:8080, в случае если вы запускаете приложение да других доменах (domain) или портах (port) вам нужно зарегистрировать ваши отдельные сертификаты (certificate).
social-cfg.properties
social.auto-signup=false
# Google
# http://localhost:8080/auth/google
google.client.id=160790488111-he5fn6rq0foqg05te70dk25gifeoum9s.apps.googleusercontent.com
google.client.secret=VILTmX1UjOnyw2_meYvDQEdl
google.scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile
# Facebook
# http://localhost:8080/auth/facebook
facebook.app.id=1084911261562762
facebook.app.secret=81a324fdbc4cade1ee25523c7bff58b3
facebook.scope=public_profile,email
# Twitter
# http://localhost:8080/auth/twitter
twitter.consumer.key=
twitter.consumer.secret=
twitter.scope=
# Linkedin
# http://localhost:8080/auth/linkedin
linkedin.consumer.key=...
linkedin.consumer.secret=...
linkedin.scope=
Файл social-cfg.properties будет прочитан в классе SocialConfig.
Примечание: свойство social.auto-signup:
- social.auto-signup=true: Значит когда пользователь заходит в первый раз через социальную сеть, автоматически создается запись APP_USER.
- social.auto-signup=false: Значит когда пользователь заходит в первый раз через социальную сеть, приложение перенаправляет пользователя к странице регистрации, с информацией по умолчанию, пользователь может редактировать данную информацию, потом нажать "Submit", теперь создается запись APP_USER.
SocialConfig.java
package org.o7planning.sbsocial.config;
import javax.sql.DataSource;
import org.o7planning.sbsocial.dao.AppUserDAO;
import org.o7planning.sbsocial.social.ConnectionSignUpImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
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.ConnectionSignUp;
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.google.connect.GoogleConnectionFactory;
import org.springframework.social.linkedin.connect.LinkedInConnectionFactory;
import org.springframework.social.security.AuthenticationNameUserIdSource;
import org.springframework.social.twitter.connect.TwitterConnectionFactory;
@Configuration
@EnableSocial
// Load to Environment.
@PropertySource("classpath:social-cfg.properties")
public class SocialConfig implements SocialConfigurer {
private boolean autoSignUp = false;
@Autowired
private DataSource dataSource;
@Autowired
private AppUserDAO appUserDAO;
// @env: read from social-cfg.properties file.
@Override
public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
try {
this.autoSignUp = Boolean.parseBoolean(env.getProperty("social.auto-signup"));
} catch (Exception e) {
this.autoSignUp = false;
}
// Twitter
TwitterConnectionFactory tfactory = new TwitterConnectionFactory(//
env.getProperty("twitter.consumer.key"), //
env.getProperty("twitter.consumer.secret"));
// tfactory.setScope(env.getProperty("twitter.scope"));
cfConfig.addConnectionFactory(tfactory);
// Facebook
FacebookConnectionFactory ffactory = new FacebookConnectionFactory(//
env.getProperty("facebook.app.id"), //
env.getProperty("facebook.app.secret"));
ffactory.setScope(env.getProperty("facebook.scope"));
// auth_type=reauthenticate
cfConfig.addConnectionFactory(ffactory);
// Linkedin
LinkedInConnectionFactory lfactory = new LinkedInConnectionFactory(//
env.getProperty("linkedin.consumer.key"), //
env.getProperty("linkedin.consumer.secret"));
lfactory.setScope(env.getProperty("linkedin.scope"));
cfConfig.addConnectionFactory(lfactory);
// Google
GoogleConnectionFactory gfactory = new GoogleConnectionFactory(//
env.getProperty("google.client.id"), //
env.getProperty("google.client.secret"));
gfactory.setScope(env.getProperty("google.scope"));
cfConfig.addConnectionFactory(gfactory);
}
// The UserIdSource determines the userID of the user.
@Override
public UserIdSource getUserIdSource() {
return new AuthenticationNameUserIdSource();
}
// USERCONNECTION.
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
// org.springframework.social.security.SocialAuthenticationServiceRegistry
JdbcUsersConnectionRepository usersConnectionRepository = new JdbcUsersConnectionRepository(dataSource,
connectionFactoryLocator,
Encryptors.noOpText());
if (autoSignUp) {
// After logging in to social networking.
// Automatically creates corresponding APP_USER if it does not exist.
ConnectionSignUp connectionSignUp = new ConnectionSignUpImpl(appUserDAO);
usersConnectionRepository.setConnectionSignUp(connectionSignUp);
} else {
// After logging in to social networking.
// If the corresponding APP_USER record is not found.
// Navigate to registration page.
usersConnectionRepository.setConnectionSignUp(null);
}
return usersConnectionRepository;
}
// This bean manages the connection flow between the account provider
// and the example application.
@Bean
public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, //
ConnectionRepository connectionRepository) {
return new ConnectController(connectionFactoryLocator, connectionRepository);
}
}
Смотрите так же как зарегистрировать сертификаты (certificate) OAuth2 с разными социальными сетями:
5. Entity & DAO
При первом запуске данного приложения, таблицы будут созданы основываясь на Entity.

Таблица USERCONNECTION будет создана основываясь на классе UserConnection, данная таблица будет использована автоматически с помощью Spring Social API, он сохраняет публичную информацию пользователя, взятую из социальной сети такие как ProviderId, ProviderUserId, Displayname, Imageurl, ... Вы не можете изменить структуру данной таблицы.
UserConnection.java
package org.o7planning.sbsocial.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "Userconnection")
public class UserConnection implements Serializable {
private static final long serialVersionUID = -6991752510891411572L;
@Id
@Column(name = "Userid", length = 255, nullable = false)
private String userId;
@Id
@Column(name = "Providerid", length = 255, nullable = false)
private String providerId;
@Id
@Column(name = "Provideruserid", length = 255, nullable = false)
private String providerUserId;
@Column(name = "Rank", nullable = false)
private int rank;
@Column(name = "Displayname", length = 255, nullable = true)
private String displayName;
@Column(name = "Profileurl", length = 512, nullable = true)
private String profileUrl;
@Column(name = "Imageurl", length = 512, nullable = true)
private String imageUrl;
@Column(name = "Accesstoken", length = 512, nullable = true)
private String accessToken;
@Column(name = "Secret", length = 512, nullable = true)
private String secret;
@Column(name = "Refreshtoken", length = 512, nullable = true)
private String refreshToken;
@Column(name = "Expiretime", nullable = true)
private Long expireTime;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public String getProviderUserId() {
return providerUserId;
}
public void setProviderUserId(String providerUserId) {
this.providerUserId = providerUserId;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getProfileUrl() {
return profileUrl;
}
public void setProfileUrl(String profileUrl) {
this.profileUrl = profileUrl;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
}
Таблица PERSISTENT_LOGINS будет создана основываясь на классе PersistentLogin, данная таблица будет использована автоматически с помощью Spring Security Remember Me. Вы не можете изменить структуру данной таблицы.
PersistentLogin.java
package org.o7planning.sbsocial.entity;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "persistent_logins")
public class PersistentLogin {
@Id
@Column(name = "Series", length = 64, nullable = false)
private String series;
@Column(name = "Username", length = 64, nullable = false)
private String userName;
@Column(name = "Token", length = 64, nullable = false)
private String token;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "Last_Used", nullable = false)
private Date lastUsed;
public String getSeries() {
return series;
}
public void setSeries(String series) {
this.series = series;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Date getLastUsed() {
return lastUsed;
}
public void setLastUsed(Date lastUsed) {
this.lastUsed = lastUsed;
}
}
Таблица APP_USER, APP_ROLE, USER_ROLE будет автоматички создана основываясь на классы AppUser, AppRole, UserRole:
AppUser.java
package org.o7planning.sbsocial.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "App_User", //
uniqueConstraints = { //
@UniqueConstraint(name = "APP_USER_UK", columnNames = "User_Name"),
@UniqueConstraint(name = "APP_USER_UK2", columnNames = "Email") })
public class AppUser {
@Id
@GeneratedValue
@Column(name = "User_Id", nullable = false)
private Long userId;
@Column(name = "User_Name", length = 36, nullable = false)
private String userName;
@Column(name = "Email", length = 128, nullable = false)
private String email;
@Column(name = "First_Name", length = 36, nullable = true)
private String firstName;
@Column(name = "Last_Name", length = 36, nullable = true)
private String lastName;
@Column(name = "Encryted_Password", length = 128, nullable = false)
private String encrytedPassword;
@Column(name = "Enabled", length = 1, nullable = false)
private boolean enabled;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEncrytedPassword() {
return encrytedPassword;
}
public void setEncrytedPassword(String encrytedPassword) {
this.encrytedPassword = encrytedPassword;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
AppRole.java
package org.o7planning.sbsocial.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "App_Role", //
uniqueConstraints = { //
@UniqueConstraint(name = "APP_ROLE_UK", columnNames = "Role_Name") })
public class AppRole {
public static final String ROLE_USER = "ROLE_USER";
public static final String ROLE_ADMIN = "ROLE_ADMIN";
@Id
@GeneratedValue
@Column(name = "Role_Id", nullable = false)
private Long roleId;
@Column(name = "Role_Name", length = 30, nullable = false)
private String roleName;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
UserRole.java
package org.o7planning.sbsocial.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "User_Role", //
uniqueConstraints = { //
@UniqueConstraint(name = "USER_ROLE_UK", //
columnNames = { "User_Id", "Role_Id" }) })
public class UserRole {
@Id
@GeneratedValue
@Column(name = "Id", nullable = false)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "User_Id", nullable = false)
private AppUser appUser;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Role_Id", nullable = false)
private AppRole appRole;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public AppUser getAppUser() {
return appUser;
}
public void setAppUser(AppUser appUser) {
this.appUser = appUser;
}
public AppRole getAppRole() {
return appRole;
}
public void setAppRole(AppRole appRole) {
this.appRole = appRole;
}
}

AppUserDAO.java
package org.o7planning.sbsocial.dao;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.o7planning.sbsocial.entity.AppRole;
import org.o7planning.sbsocial.entity.AppUser;
import org.o7planning.sbsocial.form.AppUserForm;
import org.o7planning.sbsocial.utils.EncrytedPasswordUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionKey;
import org.springframework.social.connect.UserProfile;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class AppUserDAO {
@Autowired
private EntityManager entityManager;
@Autowired
private AppRoleDAO appRoleDAO;
public AppUser findAppUserByUserId(Long userId) {
try {
String sql = "Select e from " + AppUser.class.getName() + " e " //
+ " Where e.userId = :userId ";
Query query = entityManager.createQuery(sql, AppUser.class);
query.setParameter("userId", userId);
return (AppUser) query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
public AppUser findAppUserByUserName(String userName) {
try {
String sql = "Select e from " + AppUser.class.getName() + " e " //
+ " Where e.userName = :userName ";
Query query = entityManager.createQuery(sql, AppUser.class);
query.setParameter("userName", userName);
return (AppUser) query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
public AppUser findByEmail(String email) {
try {
String sql = "Select e from " + AppUser.class.getName() + " e " //
+ " Where e.email = :email ";
Query query = entityManager.createQuery(sql, AppUser.class);
query.setParameter("email", email);
return (AppUser) query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
private String findAvailableUserName(String userName_prefix) {
AppUser account = this.findAppUserByUserName(userName_prefix);
if (account == null) {
return userName_prefix;
}
int i = 0;
while (true) {
String userName = userName_prefix + "_" + i++;
account = this.findAppUserByUserName(userName);
if (account == null) {
return userName;
}
}
}
// Auto create App User Account.
public AppUser createAppUser(Connection<?> connection) {
ConnectionKey key = connection.getKey();
// (facebook,12345), (google,123) ...
System.out.println("key= (" + key.getProviderId() + "," + key.getProviderUserId() + ")");
UserProfile userProfile = connection.fetchUserProfile();
String email = userProfile.getEmail();
AppUser appUser = this.findByEmail(email);
if (appUser != null) {
return appUser;
}
String userName_prefix = userProfile.getFirstName().trim().toLowerCase()//
+ "_" + userProfile.getLastName().trim().toLowerCase();
String userName = this.findAvailableUserName(userName_prefix);
//
// Random Password! TODO: Need send email to User!
//
String randomPassword = UUID.randomUUID().toString().substring(0, 5);
String encrytedPassword = EncrytedPasswordUtils.encrytePassword(randomPassword);
//
appUser = new AppUser();
appUser.setEnabled(true);
appUser.setEncrytedPassword(encrytedPassword);
appUser.setUserName(userName);
appUser.setEmail(email);
appUser.setFirstName(userProfile.getFirstName());
appUser.setLastName(userProfile.getLastName());
this.entityManager.persist(appUser);
// Create default Role
List<String> roleNames = new ArrayList<String>();
roleNames.add(AppRole.ROLE_USER);
this.appRoleDAO.createRoleFor(appUser, roleNames);
return appUser;
}
public AppUser registerNewUserAccount(AppUserForm appUserForm, List<String> roleNames) {
AppUser appUser = new AppUser();
appUser.setUserName(appUserForm.getUserName());
appUser.setEmail(appUserForm.getEmail());
appUser.setFirstName(appUserForm.getFirstName());
appUser.setLastName(appUserForm.getLastName());
appUser.setEnabled(true);
String encrytedPassword = EncrytedPasswordUtils.encrytePassword(appUserForm.getPassword());
appUser.setEncrytedPassword(encrytedPassword);
this.entityManager.persist(appUser);
this.entityManager.flush();
this.appRoleDAO.createRoleFor(appUser, roleNames);
return appUser;
}
}
AppRoleDAO.java
package org.o7planning.sbsocial.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.o7planning.sbsocial.entity.AppRole;
import org.o7planning.sbsocial.entity.AppUser;
import org.o7planning.sbsocial.entity.UserRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class AppRoleDAO {
@Autowired
private EntityManager entityManager;
public List<String> getRoleNames(Long userId) {
String sql = "Select ur.appRole.roleName from " + UserRole.class.getName() + " ur " //
+ " where ur.appUser.userId = :userId ";
Query query = this.entityManager.createQuery(sql, String.class);
query.setParameter("userId", userId);
return query.getResultList();
}
public AppRole findAppRoleByName(String roleName) {
try {
String sql = "Select e from " + AppRole.class.getName() + " e " //
+ " where e.roleName = :roleName ";
Query query = this.entityManager.createQuery(sql, AppRole.class);
query.setParameter("roleName", roleName);
return (AppRole) query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
public void createRoleFor(AppUser appUser, List<String> roleNames) {
//
for (String roleName : roleNames) {
AppRole role = this.findAppRoleByName(roleName);
if (role == null) {
role = new AppRole();
role.setRoleName(AppRole.ROLE_USER);
this.entityManager.persist(role);
this.entityManager.flush();
}
UserRole userRole = new UserRole();
userRole.setAppRole(role);
userRole.setAppUser(appUser);
this.entityManager.persist(userRole);
this.entityManager.flush();
}
}
}
UserConnectionDAO.java
package org.o7planning.sbsocial.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.o7planning.sbsocial.entity.UserConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class UserConnectionDAO {
@Autowired
private EntityManager entityManager;
public UserConnection findUserConnectionByUserProviderId(String userProviderId) {
try {
String sql = "Select e from " + UserConnection.class.getName() + " e " //
+ " Where e.userProviderId = :userProviderId ";
Query query = entityManager.createQuery(sql, UserConnection.class);
query.setParameter("userProviderId", userProviderId);
List<UserConnection> list = query.getResultList();
return list.isEmpty() ? null : list.get(0);
} catch (NoResultException e) {
return null;
}
}
}
6. Services & Utility

UserDetailsServiceImpl.java
package org.o7planning.sbsocial.service;
import java.util.ArrayList;
import java.util.List;
import org.o7planning.sbsocial.dao.AppRoleDAO;
import org.o7planning.sbsocial.dao.AppUserDAO;
import org.o7planning.sbsocial.entity.AppUser;
import org.o7planning.sbsocial.social.SocialUserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private AppUserDAO appUserDAO;
@Autowired
private AppRoleDAO appRoleDAO;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
System.out.println("UserDetailsServiceImpl.loadUserByUsername=" + userName);
AppUser appUser = this.appUserDAO.findAppUserByUserName(userName);
if (appUser == null) {
System.out.println("User not found! " + userName);
throw new UsernameNotFoundException("User " + userName + " was not found in the database");
}
System.out.println("Found User: " + appUser);
// [ROLE_USER, ROLE_ADMIN,..]
List<String> roleNames = this.appRoleDAO.getRoleNames(appUser.getUserId());
List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
if (roleNames != null) {
for (String role : roleNames) {
// ROLE_USER, ROLE_ADMIN,..
GrantedAuthority authority = new SimpleGrantedAuthority(role);
grantList.add(authority);
}
}
SocialUserDetailsImpl userDetails = new SocialUserDetailsImpl(appUser, roleNames);
return userDetails;
}
}
SocialUserDetailsServiceImpl.java
package org.o7planning.sbsocial.service;
import org.o7planning.sbsocial.social.SocialUserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
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.SocialUserDetails;
import org.springframework.social.security.SocialUserDetailsService;
import org.springframework.stereotype.Service;
@Service
public class SocialUserDetailsServiceImpl implements SocialUserDetailsService {
@Autowired
private UserDetailsService userDetailService;
// After user created by ConnectionSignUpImpl.execute(Connection<?>)
// This method is called by the Spring Social API.
@Override
public SocialUserDetails loadUserByUserId(String userName) throws UsernameNotFoundException, DataAccessException {
System.out.println("SocialUserDetailsServiceImpl.loadUserByUserId=" + userName);
// See UserDetailServiceImpl.
UserDetails userDetails = ((UserDetailsServiceImpl) userDetailService).loadUserByUsername(userName);
return (SocialUserDetailsImpl) userDetails;
}
}
При первом входе пользователя с его аккаунтом социальной сети, приложение получит объект Connection, данный объект включает профайл (Profile) пользователя. Вы напишете код в классе ConnectionSignUpImpl, чтобы создать запись таблицы APP_USER. (Смотрите так же код в классе SocialConfig).
ConnectionSignUpImpl.java
package org.o7planning.sbsocial.social;
import org.o7planning.sbsocial.dao.AppUserDAO;
import org.o7planning.sbsocial.entity.AppUser;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
public class ConnectionSignUpImpl implements ConnectionSignUp {
private AppUserDAO appUserDAO;
public ConnectionSignUpImpl(AppUserDAO appUserDAO) {
this.appUserDAO = appUserDAO;
}
// After logging in social networking.
// This method will be called to create a corresponding App_User record
// if it does not already exist.
@Override
public String execute(Connection<?> connection) {
AppUser account = appUserDAO.createAppUser(connection);
return account.getUserName();
}
}
SocialUserDetailsImpl.java
package org.o7planning.sbsocial.social;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.o7planning.sbsocial.entity.AppUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.social.security.SocialUserDetails;
public class SocialUserDetailsImpl implements SocialUserDetails {
private static final long serialVersionUID = -5246117266247684905L;
private List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
private AppUser appUser;
public SocialUserDetailsImpl(AppUser appUser, List<String> roleNames) {
this.appUser = appUser;
for (String roleName : roleNames) {
GrantedAuthority grant = new SimpleGrantedAuthority(roleName );
this.list.add(grant);
}
}
@Override
public String getUserId() {
return this.appUser.getUserId() + "";
}
@Override
public String getUsername() {
return appUser.getUserName();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return list;
}
@Override
public String getPassword() {
return appUser.getEncrytedPassword();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
EncrytedPasswordUtils.java
package org.o7planning.sbsocial.utils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class EncrytedPasswordUtils {
// Encryte Password with BCryptPasswordEncoder
public static String encrytePassword(String password) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder.encode(password);
}
}
Класс SecurityUtil содержит утилитарный метод для автоматического логина пользователя в приложение.
SecurityUtil.java
package org.o7planning.sbsocial.utils;
import java.util.List;
import org.o7planning.sbsocial.entity.AppUser;
import org.o7planning.sbsocial.social.SocialUserDetailsImpl;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.social.security.SocialUserDetails;
public class SecurityUtil {
// Auto Login.
public static void logInUser(AppUser user, List<String> roleNames) {
SocialUserDetails userDetails = new SocialUserDetailsImpl(user, roleNames);
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
WebUtils.java
package org.o7planning.sbsocial.utils;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class WebUtils {
public static String toString(UserDetails user) {
StringBuilder sb = new StringBuilder();
sb.append("UserName:").append(user.getUsername());
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
if (authorities != null && !authorities.isEmpty()) {
sb.append(" (");
boolean first = true;
for (GrantedAuthority a : authorities) {
if (first) {
sb.append(a.getAuthority());
first = false;
} else {
sb.append(", ").append(a.getAuthority());
}
}
sb.append(")");
}
return sb.toString();
}
}
7. Form, Validator, Controller

AppUserForm это класс представляющий данные, которые могут быть введены пользователями при регистрации нового аккаунта.
AppUserForm.java
package org.o7planning.sbsocial.form;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionKey;
import org.springframework.social.connect.UserProfile;
public class AppUserForm {
private Long userId;
private String email;
private String userName;
private String firstName;
private String lastName;
private String password;
private String role;
private String signInProvider;
private String providerUserId;
public AppUserForm() {
}
public AppUserForm(Connection<?> connection) {
UserProfile socialUserProfile = connection.fetchUserProfile();
this.userId = null;
this.email = socialUserProfile.getEmail();
this.userName = socialUserProfile.getUsername();
this.firstName = socialUserProfile.getFirstName();
this.lastName = socialUserProfile.getLastName();
ConnectionKey key = connection.getKey();
// google, facebook, twitter
this.signInProvider = key.getProviderId();
// ID of User on google, facebook, twitter.
// ID của User trên google, facebook, twitter.
this.providerUserId = key.getProviderUserId();
}
public Long getUserId() {
return userId;
}
public void setUserId(Long id) {
this.userId = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getSignInProvider() {
return signInProvider;
}
public void setSignInProvider(String signInProvider) {
this.signInProvider = signInProvider;
}
public String getProviderUserId() {
return providerUserId;
}
public void setProviderUserId(String providerUserId) {
this.providerUserId = providerUserId;
}
}
AppUserFormValidator испоьзуется для проверки данных, введенные пользователем при регистрации нового аккаунта.
AppUserValidator.java
package org.o7planning.sbsocial.validator;
import org.apache.commons.validator.routines.EmailValidator;
import org.o7planning.sbsocial.dao.AppUserDAO;
import org.o7planning.sbsocial.entity.AppUser;
import org.o7planning.sbsocial.form.AppUserForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
@Component
public class AppUserValidator implements Validator {
// common-validator library.
private EmailValidator emailValidator = EmailValidator.getInstance();
@Autowired
private AppUserDAO appUserDAO;
@Override
public boolean supports(Class<?> clazz) {
return clazz == AppUserForm.class;
}
@Override
public void validate(Object target, Errors errors) {
AppUserForm form = (AppUserForm) target;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "", "Email is required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "", "User name is required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "", "First name is required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "", "Last name is required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "", "Password is required");
if (errors.hasErrors()) {
return;
}
if (!emailValidator.isValid(form.getEmail())) {
errors.rejectValue("email", "", "Email is not valid");
return;
}
AppUser userAccount = appUserDAO.findAppUserByUserName( form.getUserName());
if (userAccount != null) {
if (form.getUserId() == null) {
errors.rejectValue("userName", "", "User name is not available");
return;
} else if (!form.getUserId().equals(userAccount.getUserId() )) {
errors.rejectValue("userName", "", "User name is not available");
return;
}
}
userAccount = appUserDAO.findByEmail(form.getEmail());
if (userAccount != null) {
if (form.getUserId() == null) {
errors.rejectValue("email", "", "Email is not available");
return;
} else if (!form.getUserId().equals(userAccount.getUserId() )) {
errors.rejectValue("email", "", "Email is not available");
return;
}
}
}
}
MainController.java
package org.o7planning.sbsocial.controller;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import org.o7planning.sbsocial.dao.AppUserDAO;
import org.o7planning.sbsocial.entity.AppRole;
import org.o7planning.sbsocial.entity.AppUser;
import org.o7planning.sbsocial.form.AppUserForm;
import org.o7planning.sbsocial.utils.SecurityUtil;
import org.o7planning.sbsocial.utils.WebUtils;
import org.o7planning.sbsocial.validator.AppUserValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
@Transactional
public class MainController {
@Autowired
private AppUserDAO appUserDAO;
@Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
@Autowired
private UsersConnectionRepository connectionRepository;
@Autowired
private AppUserValidator appUserValidator;
@InitBinder
protected void initBinder(WebDataBinder dataBinder) {
// Form target
Object target = dataBinder.getTarget();
if (target == null) {
return;
}
System.out.println("Target=" + target);
if (target.getClass() == AppUserForm.class) {
dataBinder.setValidator(appUserValidator);
}
// ...
}
@RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET)
public String welcomePage(Model model) {
model.addAttribute("title", "Welcome");
model.addAttribute("message", "This is welcome page!");
return "welcomePage";
}
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String adminPage(Model model, Principal principal) {
// After user login successfully.
String userName = principal.getName();
System.out.println("User Name: " + userName);
UserDetails loginedUser = (UserDetails) ((Authentication) principal).getPrincipal();
String userInfo = WebUtils.toString(loginedUser);
model.addAttribute("userInfo", userInfo);
return "adminPage";
}
@RequestMapping(value = "/logoutSuccessful", method = RequestMethod.GET)
public String logoutSuccessfulPage(Model model) {
model.addAttribute("title", "Logout");
return "logoutSuccessfulPage";
}
@RequestMapping(value = "/userInfo", method = RequestMethod.GET)
public String userInfo(Model model, Principal principal) {
// After user login successfully.
String userName = principal.getName();
System.out.println("User Name: " + userName);
UserDetails loginedUser = (UserDetails) ((Authentication) principal).getPrincipal();
String userInfo = WebUtils.toString(loginedUser);
model.addAttribute("userInfo", userInfo);
return "userInfoPage";
}
@RequestMapping(value = "/403", method = RequestMethod.GET)
public String accessDenied(Model model, Principal principal) {
if (principal != null) {
UserDetails loginedUser = (UserDetails) ((Authentication) principal).getPrincipal();
String userInfo = WebUtils.toString(loginedUser);
model.addAttribute("userInfo", userInfo);
String message = "Hi " + principal.getName() //
+ "<br> You do not have permission to access this page!";
model.addAttribute("message", message);
}
return "403Page";
}
@RequestMapping(value = { "/login" }, method = RequestMethod.GET)
public String login(Model model) {
return "loginPage";
}
// User login with social networking,
// but does not allow the app to view basic information
// application will redirect to page / signin.
@RequestMapping(value = { "/signin" }, method = RequestMethod.GET)
public String signInPage(Model model) {
return "redirect:/login";
}
@RequestMapping(value = { "/signup" }, method = RequestMethod.GET)
public String signupPage(WebRequest request, Model model) {
ProviderSignInUtils providerSignInUtils //
= new ProviderSignInUtils(connectionFactoryLocator, connectionRepository);
// Retrieve social networking information.
Connection<?> connection = providerSignInUtils.getConnectionFromSession(request);
//
AppUserForm myForm = null;
//
if (connection != null) {
myForm = new AppUserForm(connection);
} else {
myForm = new AppUserForm();
}
model.addAttribute("myForm", myForm);
return "signupPage";
}
@RequestMapping(value = { "/signup" }, method = RequestMethod.POST)
public String signupSave(WebRequest request, //
Model model, //
@ModelAttribute("myForm") @Validated AppUserForm appUserForm, //
BindingResult result, //
final RedirectAttributes redirectAttributes) {
// Validation error.
if (result.hasErrors()) {
return "signupPage";
}
List<String> roleNames = new ArrayList<String>();
roleNames.add(AppRole.ROLE_USER);
AppUser registered = null;
try {
registered = appUserDAO.registerNewUserAccount(appUserForm, roleNames);
} catch (Exception ex) {
ex.printStackTrace();
model.addAttribute("errorMessage", "Error " + ex.getMessage());
return "signupPage";
}
if (appUserForm.getSignInProvider() != null) {
ProviderSignInUtils providerSignInUtils //
= new ProviderSignInUtils(connectionFactoryLocator, connectionRepository);
// (Spring Social API):
// If user login by social networking.
// This method saves social networking information to the UserConnection table.
providerSignInUtils.doPostSignUp(registered.getUserName(), request);
}
// After registration is complete, automatic login.
SecurityUtil.logInUser(registered, roleNames);
return "redirect:/userInfo";
}
}
8. View (Thymeleaf)

_menu.html
<div xmlns:th="http://www.thymeleaf.org"
style="border: 1px solid #ccc;padding:5px;margin-bottom:20px;">
<a th:href="@{/}">Home</a>
|
<a th:href="@{/userInfo}">User Info</a>
|
<a th:href="@{/admin}">Admin</a>
|
<a th:if="${#request.userPrincipal != null}" th:href="@{/logout}">Logout</a>
</div>
403Page.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Access Denied</title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h3 th:if="${message != null}" th:utext="${message}" style="color: red;"></h3>
<div th:if="${userInfo != null}" th:utext="${userInfo}"></div>
</body>
</html>
adminPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:utext="${title}"></title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>Admin Page</h2>
<h3>Welcome :
<span th:utext="${#request.userPrincipal.name}"></span>
</h3>
<b>This is protected page!</b>
<br/><br/>
<div th:if="${userInfo != null}" th:utext="${userInfo}"></div>
</body>
</html>
loginPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h1>Login</h1>
<h2>Social Login</h2>
<a th:href="@{/auth/facebook}">Face Book</a>
<br />
<a th:href="@{/auth/google}">Google</a>
<br />
<!-- /login?error=true -->
<div th:if="${#request.getParameter('error') == 'true'}"
style="color:red;margin:10px 0px;">
Login Failed!!!<br />
Reason :
<span th:if="${#session!= null and #session.getAttribute('SPRING_SECURITY_LAST_EXCEPTION') != null}"
th:utext="${#session.getAttribute('SPRING_SECURITY_LAST_EXCEPTION').message}">
Static summary
</span>
</div>
<h3>Enter user name and password:</h3>
<form name='f' th:action="@{/j_spring_security_check}" method='POST'>
<table>
<tr>
<td>User:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td>Remember Me?</td>
<td><input type="checkbox" name="remember-me" /></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="submit" /></td>
</tr>
</table>
</form>
<br>
Username/pass:
<ul>
<li>dbuser1/123</li>
<li>dbadmin1/123</li>
</ul>
</body>
</html>
loginSuccessfulPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Logout</title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h1>Logout Successful!</h1>
</body>
</html>
signupPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Sign Up</title>
<style>
.error-message {
font-size:90%;
font-style:italic;
color:red;
}
</style>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h1>Register</h1>
<h2 style="color:blue;"
th:if="${myForm.signInProvider != null}">Signup with <span th:utex="${myForm.signInProvider}"></span></h2>
<form th:object="${myForm}" method="POST">
<input type="hidden" th:field="*{userId}" />
<input type="hidden" th:field="*{signInProvider}" />
<table border="0">
<tr>
<td>User Name</td>
<td><input th:field="*{userName}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('userName')}" th:errors="*{userName}">..</span>
</td>
</tr>
<tr>
<td>Email</td>
<td><input th:field="*{email}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('email')}" th:errors="*{email}">..</span>
</td>
</tr>
<tr>
<td>First Name</td>
<td><input th:field="*{firstName}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}">..</span>
</td>
</tr>
<tr>
<td>Last Name</td>
<td><input th:field="*{lastName}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}">..</span>
</td>
</tr>
<tr>
<td>Password</td>
<td><input th:field="*{password}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('password')}" th:errors="*{password}">..</span>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Submit" /></td>
<td></td>
</tr>
</table>
</form>
<div class="error-message" th:utext="${errorMessage}"></div>
</body>
</html>
userInfoPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>User Info</title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>User Info Page</h2>
<h3>Welcome : <span th:utext="${#request.userPrincipal.name}"></span></h3>
<b>This is protected page!</b>
<br/><br/>
<div th:if="${userInfo != null}" th:utext="${userInfo}"></div>
</body>
</html>
welcomePage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:utext="${title}"></title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>Message : <span th:utext="${message}"></span></h2>
</body>
</html>
No ADS
Руководства Spring Boot
- Установите Spring Tool Suite для Eclipse
- Руководство Spring для начинающих
- Руководство Spring Boot для начинающих
- Общие свойства Spring Boot
- Руководство Spring Boot и Thymeleaf
- Руководство Spring Boot и FreeMarker
- Руководство Spring Boot и Groovy
- Руководство Spring Boot и Mustache
- Руководство Spring Boot и JSP
- Руководство Spring Boot, Apache Tiles, JSP
- Используйте Logging в Spring Boot
- Мониторинг приложений с помощью Spring Boot Actuator
- Создание веб-приложения с несколькими языками с помощью Spring Boot
- Используйте несколько ViewResolver в Spring Boot
- Используйте Twitter Bootstrap в Spring Boot
- Руководство Spring Boot Interceptor
- Руководство Spring Boot, Spring JDBC и Spring Transaction
- Руководство Spring JDBC
- Руководство Spring Boot, JPA и Spring Transaction
- Руководство Spring Boot и Spring Data JPA
- Руководство Spring Boot, Hibernate и Spring Transaction
- Интеграция Spring Boot, JPA и H2 Database
- Руководство Spring Boot и MongoDB
- Используйте несколько DataSources с Spring Boot и JPA
- Используйте несколько DataSource с Spring Boot и RoutingDataSource
- Создайте приложение для входа с Spring Boot, Spring Security, Spring JDBC
- Создайте приложение для входа с Spring Boot, Spring Security, JPA
- Создайте приложение регистрации пользователей с помощью Spring Boot, Spring Form Validation
- Пример OAuth2 Social Login в Spring Boot.
- Запускать фоновые запланированные задачи в Spring
- Пример CRUD Restful Web Service c Spring Boot
- Пример Spring Boot Restful Client c RestTemplate
- Пример CRUD с Spring Boot, REST и AngularJS
- Защита Spring Boot RESTful Service используя Basic Authentication
- Защита Spring Boot RESTful Service используя Auth0 JWT
- Пример Upload file c Spring Boot
- Пример Download file c Spring Boot
- Пример Upload file c Spring Boot и jQuery Ajax
- Пример Upload file c Spring Boot и AngularJS
- Создание веб-приложения для корзины покупок с помощью Spring Boot, Hibernate
- Руководство Spring Email
- Создайте простое приложение Chat с Spring Boot и Websocket
- Разверните приложение Spring Boot на Tomcat Server
- Развертывание приложения Spring Boot на Oracle WebLogic Server
- Установите бесплатный сертификат Let's Encrypt SSL для Spring Boot
- Настройте Spring Boot для перенаправления HTTP на HTTPS
Show More