관리 메뉴

취미개발 블로그와 마음수양

스프링 페이스북 회원가입 로그인 - (1) 본문

FrameWork_ETC/Spring

스프링 페이스북 회원가입 로그인 - (1)

아라한사 2014. 11. 20. 17:40
스프링 social



깃헙 주소들 :

저작자 님의 글 : As always, the example application of this blog post is available at Github.

저의 깃헙: https://github.com/arahansa/LearningSpringSocial

==========

역자 주**

본 글은 해외 블로거의 스프링 소셜 튜토리얼을 보면서 바로 그냥 번역한 것에 불과하다;;;; 뭔가 정리 파워포인트도 남기지만, 
읽는 이로 하여금 더 난해함을 줄 수가 있다. 

하지만 구글에 스프링 페이스북을 검색해도 잘 나오지도 않고(내 검색기능이 딸린건지 ㅠㅠ)
그냥 뭐 제대로 한번 익히고 싶은 마음이 있어서 정리를 해본다.

번역글을 읽어도 무슨 말인지 역자는 처음에 이해가 안되서...노파심에 
큰 그림을 보고 이해를 하기 위한 정리파워포인트를 남겨보지만..
이게 더 이해가 안되는것같기도;;하하;;



====


옛날에는 사용자들이 사용자이름과 비밀번호의 조합으로 로그인을 했었다. 하지만 요즘엔 점점 더 많은 사람들이 그들의 소셜계정을 이용해서 로그인과 회원가입을 한다.

이것이 스프링 소셜이 스프링 프로젝트 포트폴리오에서 유용한 추가사항이 되는 이유다. 그러나 스프링 시큐리티와 스프링 소셜의 통합은 조금 번거로운 작업이었다.

스프링 소셜 1.1.0 은 모든 것을 변화시키면서 매끄러운 스프링 시큐리티와의 통합을 제공하였고, 스프링 시큐리티의 자바 Config설정 지원은 설정을 좀 더 쉽게 해주게 하였다.

우리의 해결책은 다음과 같다. 
  • 일반적인 유저 등록 폼으로 사용자 계정 생성 가능
  • 소셜 계정으로 사용자 등록 가능
  • 유저네임과 패스워드로 로그인 가능
  • SaaS API provider를 통해서 로그인 가능
  • 어플리케이션이 페북, 트위터 지원
  • 어플리케이션이 반드시 "규칙적인(regular)" 스프링 MVC컨트롤러 사용(no Rest).

이 튜토리얼을 하기 앞서서 선행조건들을 살펴보자.

선행조건
이 튜토리얼은 이미 예제프로그램에 사용될 페이스북과 트위터 어플리케이션을 이미 만들었다고 가정하다. 다음링크에서 이러한 어플리케이션을 만들 수 있다.

만약 어떻게 해야 할지 모른다면,  다음 링크를 확인해보라.
다음 메이븐 의존 설정으로 넘어가자.
====
역자 추가

역자는 cmd에서 메이븐으로 돌리는 것보다 이클립스에서 그냥 바로 키는 것을 좋아한다.
본포스트 원작성자는 liquibase 로 자동 sql로 테이블을 잡아서 프로젝트를 돌리는데 이것을 할려면 cmd 에서 명령어를 쳐야했고
이클립스로 프로젝트를 불러왔을 때 뭔가 에러가 나서, 처음에 좀 빌드환경이 개인적으로 힘들었다.

그래서 역자의 깃허브에 공유프로젝트를 올렸는데.. 그냥 있는 테이블을 가지고 구동을 시킨다.
본 sql 문을 mysql 에 작성해주면 될것이다. 

CREATE DATABASE `socialtwitter` /*!40100 DEFAULT CHARACTER SET utf8 */;

CREATE TABLE `userconnection` (
  `userId` varchar(255) NOT NULL,
  `providerId` varchar(255) NOT NULL,
  `providerUserId` varchar(255) NOT NULL,
  `rank` int(11) NOT NULL,
  `displayName` varchar(255) DEFAULT NULL,
  `profileUrl` varchar(512) DEFAULT NULL,
  `imageUrl` varchar(512) DEFAULT NULL,
  `accessToken` varchar(255) NOT NULL,
  `secret` varchar(255) DEFAULT NULL,
  `refreshToken` varchar(255) DEFAULT NULL,
  `expireTime` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`userId`,`providerId`,`providerUserId`),
  UNIQUE KEY `UserConnectionRank` (`userId`,`providerId`,`rank`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

=====
필요 의존성관리 with Maven 
제일 처음 해야 할 것은 메이븐 의존설정관리다. pom파일에 다음설정을 하자. (각 세부적인 사항에 대한 번역은 생략한다)
  • Spring Security (version 3.2.0.RELEASE).
    • The core module contains core authentication and and access control components.
    • The config module contains the code used to parse XML configuration files using the Spring Security XML namespace.
    • The taglibs module contains the Spring Security JPS tag libraries.
    • The web module contains filters and all other code related to web security.
  • Apache HttpClient (version 4.3.2). Apache HttpClient is an optional dependency (but recommended) dependency of Spring Social. If it is present, Spring Social will use it as a HTTP client. If not, Spring social will use the standard Java SE components.
  • Spring Social (version 1.1.0.RELEASE).
    • The config module contains the code used to parse XML configuration files using the Spring Social XML namespace. It also adds support for Java Configuration of Spring Social.
    • The core module contains the connect framework and provides support for OAuth clients.
    • The security module integrates Spring Security with Spring Social. It delegates the authentication concerns typically taken care by Spring Security to service providers by using Spring Social.
    • The web module contains components which handle the authentication handshake between our web application and the service provider.
  • Spring Social Facebook (version 1.1.0.RELEASE) is an extension to Spring Social and it provides Facebook integration.
  • Spring Social Twitter (version 1.1.0.RELEASE) is an extension to Social Social which provides Twitter integration.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>3.2.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>3.2.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>3.2.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>3.2.0.RELEASE</version>
</dependency>
 
<!-- Use Apache HttpClient as HTTP Client -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.3.2</version>
</dependency>
 
<!-- Spring Social -->
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-config</artifactId>
    <version>1.1.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-core</artifactId>
    <version>1.1.0.RELEASE</version>
</dependency>
<dependency>    
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-security</artifactId>
    <version>1.1.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-web</artifactId>
    <version>1.1.0.RELEASE</version>
</dependency>
  
<!-- Spring Social Facebook -->
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-facebook</artifactId>
    <version>1.1.0.RELEASE</version>
</dependency>
  
<!-- Spring Social Twitter -->
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-twitter</artifactId>
    <version>1.1.0.RELEASE</version>
</dependency>

==========================================================
우리 프로그램에서는 다른 의존성또한 가진다. 예를 들자면, 스프링 프레임워크, 스프링데이터, 하이버네이트등등을 가진다. 이러한 의존성들은 명확성을 위해 생략되었다. 당신은 여기의 링크get the full list of dependencies from Github. 에서 의존파일정보를 받을 수 있다.

(역자: 
본 포스트저자분이신 외국분의 메이븐 설정파일을 이클립스에서 불러오면 뭔가 execution 인가 하는 쪽에서;;  에러가 난다. 
* 이클립스에서 빌드 안하고 바로 메이븐 빌드하면 에러없이 잘 된다. 

하지만 역자는 누누이 말했지만, 이클립스 빌드 환경을 좋아해서 메이븐 설정파일을 새로 팠다.
역자의 깃허브에서 받을 수 있다. )

다음 문서들이 또한 더 많은 정보를 줄 수 있다.
다음에는 우리는 우리 프로그램의 설정정보들을 위한 properties 파일들을 만들어볼것이다. 어떻게 되는지 알아보자.

properties 파일 생성


우리는 다음 단계로 properties 파일들을 만들 것이다.
1.  application.properties 파일들을 만든다(클래스패스에서 확인가능한)
2.  DB 코넥션을 만든다.
3. 하이버네이트 설정
4. 페이스북 애플리케이션 id 와 어플리케이션 시크릿을 properties 파일에 넣는다.
5. twitter Consumer key 와 Consumer 시크릿을 properties 파일에 넣는다.

application.properties 파일은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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

어플리케이션 설정 하기전에, 몇가지 공통적인 컴포넌트들을 만들어야. 이것들이 무엇이고, 어떻게 만드는지 한번 보자.

공통 컴포넌트 만들기

인증과정에서 사용될 세가지 컴포넌트들을 만들어야 한다. 다음과 같다.
  • 인증된 유저의 세부사항을 담을 클래스
  • UserDetailsService interface를 구현할 클래스. 유저가 폼으로 로그인 할 시, 유저정보를 불러오는데 사용될 클래스다.
  • SocialUserDetailsService interface를 구현한 클래스. 유저가 소셜로그인시, 사용자 정보를 불러오는데 사용될 클래스
어떻게 이러한 클래스들을 구현할지 알아보자.

UserDetailClass 만들기

우리는 계정에 대한 다음 요구 사항을 이행해야 한다. when 우리가 인증된 유저정보를 담을 클래스를 만들 때.

  • 폼로그인을 사용하는 유저세부사항을 담을 클래스는 UserDetails interface를 구현해야 한다. 
  • 소셜등록을 하는 사람의 정보를 담을 클래스는  SocialUserDetails interface를 구현해야함.
스프링 소셜은  SocialUser class 를 가지고 있다. 이 클래스는 이러한 두가지 요구사항을 이행한다. 하지만 우리는 종종 우리의 유저정보 클래스에 더 자세한 내용을 추가하고 싶다. 
다음 단계를 거쳐서 이것을 해보자!
  1. user details 클래스 생성
  2. SocialUser 클래스 확장
  3. 프로그램의 구체적인 필드를 생성한 클래스에 추가. 우리의 예제 프로그램에서 구체적인 필드는 id, firstName, lastName, role, socialSignInProvider 이다.
  4. username과 password, 허용된 권한을 파라미터콜렉션으로 받는 생성자를 만든다. 이러한 파라미터를 socialUser 클래스의 생성자에 전달한다.
  5. 구체적인 필드의 게터세터를 만든다.
  6. ExampleUserDetails 객체를 만들기위한 내부 빌더클래스를 만든다.
userdetails 클래스의 소스코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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;
        }
    }
}

Role 은 간단한 Enum 인데, 예제프로그램에서 legal 유저의 권한을 명시하는 이늄이며 소스코드는 다음과 같다. 
1
2
3
public enum Role {
    ROLE_USER
}

SocialMediaService 는 이늄인데, SaaS API provider 를 명시하는 이늄인데, 유저가 사용자 계정을 우리의 예제 프로그램에 생성할때 사용된다. 소스코드는 다음과 같다.
1
2
3
4
public enum SocialMediaService {
    FACEBOOK,
    TWITTER
}


UserDetailsService 인터페이스의 구현
우리는 다음단계를 통해서 우리소유의 UserdetailsService 인터페이스의 구현체를 가질 수 있다. 
  1. UserdetailsService 인터페이스를 구현하는 클래스 생성
  2. UserRepository 필드를 생성한 클래스에 추가
  3. Userrepository를 생성자 필드로 받는 생성자를 생성하고 생성자에 @Autowired 어노테이션을 붙인다.
  4.  UserDetailsService인터페이스의 loadUserByName(String username) 메소드를 구현한다.  이 메소드의 구현은 다음단계로 구성된다.
    1. UserRepository 인터페이스의  findByEmail() 메소드를 통해유저를 불러온다. 이 메소드는 메소드파라미터에 주어진 유저이름에 매칭하는 유저를 반환한다.
    2. 유저가 발견되지 않으면 USernameNotFoundException 을 반환한다.
    3. ExampleUserDetails 객체를 생성한다.
    4. 생성된 객체를 반환한다.
RepositoryUserDetailsService 클래스는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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;
 
    @Autowired
    public RepositoryUserDetailsService(UserRepository repository) {
        this.repository = repository;
    }
 
    @Override
    public 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 는 간단한 스프링 데이터 JPA 리파지토리이다. 소스코드는 다음과 같다.
1
2
3
4
5
6
import org.springframework.data.jpa.repository.JpaRepository;
 
public interface UserRepository extends JpaRepository<User, Long> {
 
    public User findByEmail(String email);
}

User는 예제프로그램의 유일한 엔티티이며 예제프로그램에 생성될 계정의 계정정보를 담고 있다. 소스코드의 관련있는 부분은 다음과 같다. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import javax.persistence.*;
 
@Entity
@Table(name = "user_accounts")
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.
}

SocialUserDetailService 인터페이스의 구현

다음단계를 통해 SocialUserDetailService를 구현할 수 있다.
  1. SocialUserDetailsService 를 구현하는 클래스를 생성한다.
  2. 생성된 클래스에 UserDetailsService필드를 추가한다.
  3. UserDetailsService객체를 생성자 파라미터로 받는 생성자를 만들고 생성자에 @Autowired 어노테이션을 붙인다.
  4. SocialUserDetailInterface 의 loadUserByUserId(String userId)메소드를 구현한다.
  5. loadUserByUserName() 메소드를 통해서 UserDetails객체를 받고 user id 를 메소드 파라미터로 보낸다.우리의 프로그램이 user id로 사용자의 유저네임을 쓰기 때문에 가능하다.
  6. 반환된 객체를 SocialUserDetails 객체로 형 변환하고 반환한다.
SimpleSocialUserDetailsService 클래스의 소스코드는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
    }
 
    @Override
    public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException, DataAccessException {
        UserDetails userDetails = userDetailsService.loadUserByUsername(userId);
        return (SocialUserDetails) userDetails;
    }
}

이것이 전부다. 우리는 어플리케이션콘텍스트를 설정할 준비가 되어있다. 어떻게 할지 알아보자~

어플리케이션 콘텍스트 설정 
이번 섹션은 어떻게 예제프로그램에서 java설정으로 어플리케이션 콘텍스트를 설정하는지 설명한다. 이 어플리케이션콘텍스트 설정은 다음 가이드라인과 함께 몇개의 설정클래스로 나누어져 있다.

  1. 각각의 설정 클래스들은 우리의 예제어플리케이션의 구체적인 부분과 관련된 설정을 포함한다. 이것은 초기 설정 이후의 몇가지 변경사항이 발생하거나 검사하할때, 관련된 설정사항을 찾기 편하게 해준다. 
  2. 이 설정은 웹계층의 단위테스트를  Spring MVC Test.  를 통해서 쉽게 하게 만들어준다. 이 튜토리얼의 세번째 파트에서 어디에 우리가 유닛테스트를 작성할지 살펴본다.
  3. 이 설정은 우리가 예제를 위한 통합테스트를 작성할 때, 외부 리소스들의 제거를 쉽게 해준다. 이 튜토리얼의 네번째 부분에서 어떻게 우리가 통합 테스트를 할지 알아본다. 
어플리케이션의 영속계층을 설정하면서 시작해보자.

영속 계층 설정

이 어플리케이션의 영속계층은 유저 계정의 정보를 저장하고 이 정보에 접근할 방법을 제공한다. 이것의 중요한 두가지 이유:
우리는 유저이름과 패스워드로 로그인할 방법을 제공한다.
우리는 어플리케이션의 구체적인 정보를 저장하고 이것을 소셜로그인하는 유저에게 링크한다.

자바설정으로 어떻게 우리가 설정할지 알아보자.
=============
JPA 설정의 구체적인 내용은 다음 튜토리얼을 보라는 얘기같다. 번역 생략
The persistence layer of example application uses Spring Data JPA 1.3.4. I will keep this section as thin as possible. If you want to learn more about Spring Data JPA, you can read mySpring Data JPA tutorial. I have also written a book about Spring Data which should help you to get started in no time.
========

우리는 다음 단계를 통해서 우리의 영속 계층을 설정한다.
  1. @Configuration 어노테이션과 함께 설정클래스를 생성한다.
  2. @EnableJpaRepositories 어노테이션을 붙이고, Spring data JPA 리파지토리의 basePackage를 지정한다.
  3. 스프링 트랜잭션 관리를 위하여  @EnableTransactionManagement 어노테이션을 붙인다.
  4. Environment 클래스를 붙인다.  @Autowired 와 함께. 우리는  @PropertySource 어노테이션을이용하여 properties 파일을 설정할 필요가 없다. 왜냐하면 이것은 이미 상위(paraent) 어플리케이션 콘텍스트에 설정되어있기 때문이다.
  5. data Source bean 을 설정한다. 이것은 엔티티매니저에게 데이터베이스 코넥션을 제공한다. 그러나 이것은 다른 목적이다. 스프링 소셜에서의  연결과 불러오는 것을 영속화하는 데 데이터소스는 쓰인다.
  6. 트랜잭션 매니져 빈을 설정한다.
  7. 엔티티 매니져 팩토리 빈을 설정한다.
PersistenceContext 의 소스코드는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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 {
 
    @Resource
    private Environment env;
 
    @Bean
    public 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;
    }
 
    @Bean
    public JpaTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return transactionManager;
    }
 
    @Bean
    public 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;
    }
}


스프링 시큐리티 설정

스프링 시큐리티는 폼로그인이나 소셜로그인을 사용하는 유저들에게 인증(authentication) 메커니즘을 제공한다.  그리고 인가(authorization)에 대한 책임을 진다.

우리는 다음단계를 통해 스프링 시큐리티 설정을 할 수 있다.
  1.  @Configuration 어노테이션이 붙은 설정 클래스를 만든다.
  2.  @EnableWebSecurity 어노테이션을 적용한다. 이것은  WebSecurityConfigurer interface.를 구현함으로써 스프링 설정을 가능하게 해준다. 
  3. 우리의 설정클래스가  WebSecurityConfigurerAdapter class 를 상속하게 해준다. 이것은 웹시큐리티설정 인스턴스를 만들기 위한 기본 객체이다. 그리고 우리는 메소드를 오버라이딩 함으로써 시큐리티 설정을 커스터마이징 하게 되는 것이다~
  4. 설정에 Userrepository 필드를 넣어주고 @Autowired 어노테이션을 붙여준다.
  5. 웹시큐리티설정어댑터클래스의  configure(WebSecurity web)  메소드를 오버라이딩한다. 스프링시큐리티가 정적리소스(css, js 같은)를 향한 요청을 무시하도록 하라~!
  6. 웹시큐리티설정어댑터의 configure(HttpSecurity http)  메소드를 오버라이딩하고 다음과 같은 단계를 구현하라.
    1. 다음단계로 폼로그인을 설정하라
      1.1 로그인페이지 url 은 '/login' 
      1.2  로그인폼 제출 처리는 /login/authenticate
      1.3 로그인 실패 url은 /login?error=bad_credentials 
    2. logout 기능은 다음단계로 구현
      2.1 로그아웃 이후에 JSESSIONID 라고 불리는 쿠키를 제거
      2.2 로그아웃 url = '/logout'
      2.3 로그아웃 성공 url 은 '/login'
    3. url 에 기반한 인증을 설정하라. 이 절의 요점은 익명유저는 회원가입절차에 관련된 모든 url에 접근할 수 있는 것이고, 나머지 애플리케이션의 부분들은 익명유저들에게서 보호되는 것이다. 
    4. 스프링시큐리티 필터 체인에  SocialAuthenticationFilter을 등록한다. 우리는 SpringSocialConfigurer 객체를 생성함으로써 이것을 할 수 있다. 그리고 이 오브젝트가 스프링시큐리티가 설정될 때 사용되게 할 수 있다. 
  7.    PasswordEncoder 빈을 설정한다. 사용자의 비밀번호를 해쉬할 수 있도록. 우리는 이것을 함으로써   BCryptPasswordEncoder 객체를 생성하고 생성된 객체를 반환할 수 있다.
  8.  새로운  RepositoryUserDetailsService 객체를 생성하고 UserRepository를 생성자 파라미터로 전달여  UserDetailsService빈을 설정한다.
  9. WebSecurityConfigurerAdapter 클래스의   configure(AuthenticationManagerBuilder auth)  메소드를 오버라이드 한다. 우리는 이 메소드를 인증요청에 사용할 수 있다. 유저가 폼 로그인 할때. 이 메소드의 구현은 다음과 같다. 
    1. UserDetailsService빈을  AuthenticationManagerBuilder 객체에 메소드 파라미터로 전달.
    2.  PasswordEncoder 빈을  AuthenticationManagerBuilder 객체에 메소드 파라미터로 전달
  10. SimpleSocialUserDetailsService 객체를 생성하고  UserDetailsService 빈의 생성자 필드로 전달하면서 SocialUserDetailsService bean 을 설정한다.
시큐리티. 어플리케이션 콘텍스트 설정 소스코드는 다음과 같다. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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 {
 
    @Autowired
    private UserRepository userRepository;
 
    @Override
    public void configure(WebSecurity web) throws Exception {
        web
                //Spring Security ignores request to static resources such as CSS or JS files.
                .ignoring()
                    .antMatchers("/static/**");
    }
 
    @Override
    protected 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",
                                "/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());
    }
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(10);
    }
 
    @Bean
    public SocialUserDetailsService socialUserDetailsService() {
        return new SimpleSocialUserDetailsService(userDetailsService());
    }
 
    @Bean
    public UserDetailsService userDetailsService() {
        return new RepositoryUserDetailsService(userRepository);
    }
}

다음 단계로 스프링 소셜을 설정해보자.

스프링 소셜 설정..

스프링 소셜은 SaaS API 프로바이더(facebook, 트위터같은)와의 통합을 제공한다. 우리는 다음단계를 통해 스프링 소셜을 설정할 수 있다.
  1. SocialConfigurer 인터페이스를 구현하는 어플리케이션 콘텍스트 설정클래스를   @Configuration 어노테이션과 함께 만든다.   SocialConfigurer 인터페이스는 스프링소셜을 설정하는데 쓰이는 콜백메소드를 선언한다.
  2. @EnableSocial어노테이션을 사용함으로써 스프링 소셜을 활성화하고, 스프링소셜설정클래스를 임포트한다.
  3. DataSource필드를 추가하고 @Autowired 적용하라.
  4. SocialConfigurer interface의 addConnectionFactories() 메소드를 생성된 설정 클래스에 추가한다.이 메소드는 다음에 설명된 두개의 파라미터를 가진다.
    1. 첫번째 파라미터는 커넥션팩토리에 사용되는  ConnectionFactoryConfigurer 객체이다.
    2. 두번째 파라미터는 예제프로그램이 돌아가는 환경을 나타내는   Environment 객체이다.
  5. addConnectionFactories() 메소드 를 다음단계로 구현한다.
    1.  새로운 
      TwitterConnectionFactory object를 만들고 consumer key 와 consumer secret을 생성자의 파라미터로 전달한다.
    2.   ConnectionFactoryConfigurer인터페이스의 
     addConnectionFactory() 를 호출함으로써 생성된  TwitterConnectionFactory객체를 전달한다. 생성된 TwitterConnectionFactory 객체를 메소드의 파라미터로 전달한다.
    3.   FacebookConnectionFactory 객체를 만들고 어플리케이션 아이디와 secrect을 생성자 파라미터로 전달한다.
    4.  ConnectionFactoryConfigurer인터페이스의 메소드를 호출함으로써  FacebookConnectionFactory 객체를 만들고 메소드 파라미터로 전달한다. 
  6. SocialConfigurer 인터페이스의  getUserIdSource()메소드를 생성된 클래스에 추가한다. 이 메소드에 의해 반환된  UserIdSource 객체는 정확한 유저계정의 아이디를 결정하는 책임이 있다. 우리의 예제 프로그램은 username 을 계정 id로 사용하기 때문에 우리는   AuthenticationNameUserIdSource객체를 반환하는 메소드를 구현해야 한다. 
  7. SocialConfigurer interface의  getUsersConnectionRepository()  메소드를 생성된 클래스에 추가한다. 이 메소드는  ConnectionFactoryLocator 객체를 메소드 파라미터로 받으며  UsersConnectionRepository object.를 반환한다.
  8. getUsersConnectionRepository() 메소드의 구현은 다음 단계를 거친다.
    1.   JdbcUsersConnectionRepository 객체를 만들고 다음 객체들을 생성자필드로 전달한다.
      1.1 첫번째 필드는 dataSource
      1.2 두번째 필드는  ConnectionFactoryLocator 이며, 우리는
      ConnectionFactoryLocator 값을두번째 파라미터로 전달.
      1.3 세번째 파라미터는   TextEncryptor 객체이며,  우리 프로그램과 SaaS API 프로바이더사이에 연결된 코넥션의 인증세부사항을을 암호화한다. 우리는  noOpText() method of the Encryptors class를 통해 이것을 생성할 수 있으며, 이것은 우리의 예제가 평문으로 이러한 세부정보를 저장한다는 것을 의미한다. 개발때는 간편하지만 실제사용에서는 쓰지말아야 한다. 
    2. 생성된 객체를 반납한다.
  9.  ConnectController 빈을 설정한다. 이 메소드는 두개의 파라미터를 가진다. 첫번째 파라미터는   ConnectionFactoryLocator bean이고 두번째 파라미터는   ConnectionRepository bean이다..  ConnectController 객체를 생성할때 이 파라미터들을 전달하라.
설정소스코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
public class SocialContext implements SocialConfigurer {
 
    @Autowired
    private DataSource dataSource;
 
    @Override
    public 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")
        ));
    }
 
    @Override
    public UserIdSource getUserIdSource() {
        return new AuthenticationNameUserIdSource();
    }
 
    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        return new JdbcUsersConnectionRepository(
                dataSource,
                connectionFactoryLocator,
                Encryptors.noOpText()
        );
    }
 
    @Bean
    public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {
        return new ConnectController(connectionFactoryLocator, connectionRepository);
    }
}

웹 계층 설정

우리는 다음과 같은 단계를 통해 웹 레이어 계층을 설정할 수 있다.

  1. 다음단계로 설정클래스를 만든다.  WebMvcConfigurerAdapter 클래스를 상속하고  @Configuration를 붙인다.
  2. 모든 컨트롤러 클래스가  @ComponentScan에 의해 발견되도록 한다.
  3.  @EnableWebMvcannotation 을 통해서 어노테이션 드리븐 웹 mvc를 활성화한다.
  4. 콘테이너의 디폴트 서블렛이 정적자원을 관리하게 해준다. 
    1.   WebMvcConfigurerAdapter class의 addResourceHandlers()메소드를 오버라이딩해서 정적 자원을 관리하게 한다.
    2. 
      configureDefaultServletHandling() method of theWebMvcConfigurerAdapter class.를 오버라이딩해서 정적자원 처리가 디폴트 서블릿으로 위임되게 한다.
  5. exception resolver 빈을 설정한다.
  6. ViewResolver 빈을 설정한다.
WebAppContext소스코드는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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 {
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("/static/");
    }
 
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
 
    @Bean
    public 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;
    }
 
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
 
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/jsp/");
        viewResolver.setSuffix(".jsp");
 
        return viewResolver;
    }
}

자 이제 이 설정들을 어떻게 연동시킬 지, 부모 어플리케이션 콘텍스트를 만들어보자.

설정 연동(Tieing it all Together)

마지막 어플리케이션 콘텍스트는 세가지 책임이 있다.
1. 어플리케이션에 사용되는 일반적인 컴포넌트들을 설정한다. 
2. 서비스 클래스들을 발견한다.
3. 이것은 우리 프로그램의 루트 어플리케이션 컨텍스트다.

다음 단계를 통해 만들어보자.
  1.  @Configuration어노테이션 명시
  2. basePackage 를 @ComponentScan 로 서비스계층을 명시
  3.  @Importannotation으로 다른 설정파일 임포트
  4.  @PropertySource annotation으로 application.properties 임포트.
  5. MessageSource bean.설정
  6. PropertySourcesPlaceholderConfigurer bean설정
소스코드는 다음과 같다. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 {
 
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
 
        messageSource.setBasename("i18n/messages");
        messageSource.setUseCodeAsDefaultMessage(true);
 
        return messageSource;
    }
 
    @Bean
    public PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

우리는 어플리케이션 콘텍스트를 설정했지만 우리의 웹 어플리케이션 설정을 여전히 좀 해야 한다. 자바설정을 통해서 어떻게 하는지 알아보자.

Web Application 설정
우리의 마지막 단계는 우리의 예제어플리케이션을 설정하는 것이다. 우리는 우리의 프로그램이 servlet3.0 적용 컨테이너에서 디플로이되는 한 이것을 web.xml 없이 할 수 있다. 


다음단계로 구성된다.
  1.  WebApplicationInitializer구현하는 클래스를 만든다.
  2. WebApplicationInitializer의 onStartUp() 메소드를 오버라이딩하여 어플리케이션을 설정한다. 우리는 다음단계를 통해서 이 메소드를 구현할 수 있다.
    1. 어플리케이션의 루트 컨텍스트를 만들고  ExampleApplicationContext 클래스에 생성된 루트 컨텍스트를 등록한다. 
    2.  dispatcher servlet설정
    3.  character encoding filter설정
    4.   Spring Security filter chain설정
    5. 사이트 메쉬 설정
    6. 서블릿콘텍스트에  context loader listener 를 추가
소스는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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 {
 
    @Override
    public 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));
    }
}


우리는 성공적으로 자바 설정을 통해 설정을 마쳤다. 이 튜토리얼은 우리에게 두가지를 가르쳐 주었다.
  • 어떻게 스프링 시큐리티와 스프링소셜에게 필요한 구성물들을 구현하는지
  • 스프링 시큐리티와 스프링 소셜을 어떻게 통합하는지
다음 튜토리얼은 어떻게 등록하고 로그인기능을 제공할지 알아보도록 하겠다.



About the Author

Petri Kainulainen 은 소프트웨어개발과 지속적향상에 열정과 관심을 가지고 있으며, 스프링 프레임워크의 전문가이자 Spring Data book 의 저자이기도 합니다. About Petri Kainulainen →

Connect With Him

트위터 : https://www.twitter.com/petrikainulaine

구글+: https://plus.google.com/+PetriKainulainen?rel=author

링크드인 : http://www.linkedin.com/in/petrikainulainen

유튜브 : https://www.youtube.com/user/Loketus

RSS : http://feeds.feedblitz.com/PetriKainulainen




깃헙 주소들 :

저작자 님의 글 : As always, the example application of this blog post is available at Github.

저의 깃헙: https://github.com/arahansa/LearningSpringSocial