2. Spring Boot 로 OAuth 인증서버 만들기
Spring Boot 와 Spring Security 를 통해 인증 및 리소스 서버를 만들어 보자.
인증서버와 리소스 서버는 분리할 수도 있지만, 먼저 간단한 구현을 위해 하나로 만들어 볼 것이다.
의존성 및 프로젝트 구조는 다음과 같다.
프로젝트 구조에서 알 수 있듯이 총 세 가지의 설정파일이 필요한데, 각각
- 인증 서버에 대한 설정(AuthServerConfig)
- 리소스 서버에 대한 설정(ResourceServerConfig)
- OAuth 와 별개인 전반적인 서버에 대한 인증 설정(SecurityConfig)
이다.
AuthServerConfig
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
...
@EnableAuthorizationServer
@Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("iamclient")
.secret(passwordEncoder.encode("iamsecret"))
.authorizedGrantTypes("authorization_code", "password", "refresh_token")
.scopes("read", "write")
.accessTokenValiditySeconds(60*60)
.refreshTokenValiditySeconds(6*60*60)
.autoApprove(true);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
인증서버 설정에서는 @EnableAuthorizationServer 어노테이션을 통해 oauth 서버에 필요한 기본설정을 셋팅할 수 있다. (물론 @Configuration 어노테이션도 함께 붙여야 한다.)
클래스에 @EnableAuthorizationServer 어노테이션을 붙이는 것 만으로도 OAuth 관련 endpoints 가 생성된다.
(/oauth/token, /oauth/authorize 등. 자세한 것은 여기 참고)
이 클래스는 AuthorizationServerConfigurer 를 구현해야 하며, 주요 메소드를 포함한 구현체인 AuthorizationServerConfigurerAdapter 를 상속받으면 편리하다.
설정 내에서는 configure 메소드를 override 하면 되는데, configure 메소드의 파라미터에 따라 다음 세 가지 종류의 설정을 할 수 있다.
1. ClientDetailsServiceConfigurer
클라이언트에 대한 설정을 할 수 있다. 클라이언트 정보를 DB 로 관리할 것인지, Inmemory 로 관리할 것인지 등에 대한 설정이나, 클라이언트별로 제공할 scope 나 권한 등을 정의할 수 있다. 예제에서는 간단한 테스트를 위해 InMemory 로 설정하였으며 client id 와 secret 을 정의해놓았다. (DB 를 사용할 경우 스키마는 각자 정의하기 나름이지만, Spring 에서는 예시를 제공해주고 있다. 예시)
2. AuthorizationServerSecurityConfigurer
토큰 엔드포인트 (/auth/token) 에 대한 보안관련 설정을 할 수 있다.
3. AuthorizationServerEndpointsConfigurer
인증, 토큰 엔드포인트, 토큰 서비스를 정의할 수 있다.
위에서 tokenStore, userDetailsService 등 객체를 빈으로 가져와 설정에 등록해주고 있는데, 이 빈들은 뒤에 나올 SecurityConfig 에서 등록해주게 될 것이다.
ResourceServerConfig
OAuth 토큰에 의해 보호되고 있는 자원 서버에 대한 설정이다. Sprint OAuth 에서는 자원에대한 보호를 구현하는 Spring Security Filter 를 제공한다. @EnableResourceServer 어노테이션을 붙임으로서 해당 필터를 활성화 할 수 있다.
이 설정에서는 각 리소스 (여기에서는 user 정보) 에 대한 접근 권한을 설정해주고 있다.
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
@Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable()
.authorizeRequests()
.antMatchers("/users/**").authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
SecurityConfig
서버에 대한 전반적인 security 설정을 한다.
대부분 Spring Security 와 관련된 설정들이며, OAuth 관련 설정은 TokenStore 빈을 등록해주는 부분이다.
여기서는 inMemory 토큰을 사용하는 것으로 설정했다. UserDetailsServier 는 Spring Security 에서 제공하는 인터페이스로, 해당 인터페이스를 구현해 회원정보를 관리할 수 있다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Resource(name="userService")
private UserDetailsService userDetailsService;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
public PasswordEncoder encoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(encoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.anonymous()
.disable()
.authorizeRequests()
.antMatchers("/api-docs/**")
.permitAll();
}
}
User
리소스 서버의 리소스인 User 정보에 대한 구현이다.
다음과 같이 User 정보를 담을 엔티티를 하나 만들어준다.
@Entity
@Data
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
}
UserController
그리고 리소스를 만들어준다.
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user")
public List<User> listUser(){
return userService.findAll();
}
@PostMapping("/user")
public User create(@RequestBody User user){
return userService.save(user);
}
}
UserRepository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
UserService
위에서 언급한 UserDetailsService 를 구현하는 UserService 를 만들어준다. 또한 처음 빈 등록 시 User 객체 하나를 생성해준다. (테스트를 위해)
@Service
public class UserService implements UserDetailsService{
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
public List<User> findAll() {
return userRepository.findAll();
}
public User save(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
@PostConstruct
public void init(){
User autumn = userRepository.findByUsername("autumn");
if(autumn == null){
User user = new User();
user.setUsername("autumn");
user.setPassword("pass");
System.out.println(this.save(user));
}
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthorities());
}
private Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
}
이제 준비가 끝났다. 어플리케이션을 실행시켜 보자.
실행 시 autumn 이라는 유저가 생성되는 것을 볼 수 있다.
테스트에는 PostMan 을 활용했다.
먼저 token 을 얻기 위해 요청을 보내보자. token 을 얻기 위해서는 client id, secret 정보를 함께 요청해야 한다. 위에서 inMemory 로 설정해두었던 정보를 입력하자.
다음은 요청 Body 에 User 의 인증정보를 넣어준다. (자원 소유자를 인증하기 위해 구글, 페이스북 등에 로그인하는 과정이 될 것이다.)
curl -X POST \
http://localhost:8081/oauth/token \
-H 'Authorization: Basic aWFtY2xpZW50OmlhbXNlY3JldA==' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'username=autumn&password=pass&grant_type=password'
한번의 요청에 클라이언트, 자원 소유자의 정보를 모두 입력했지만, 이는 password 방식의 grant type 을 사용한 것이다. authorization code 방식도 지원하도록 설정해놓았으므로, 따로 요청하는 것도 가능하다.
요청을 보내면 다음과 같이 토큰 정보를 얻을 수 있다.
이번에는 얻어낸 토큰을 이용해 자원에 접근해보자.
아래처럼 요청에 따라 자원을 조회할 수 있다.
curl -X GET \
http://localhost:8081/users/user \
-H 'Authorization: Bearer df1c4a87-863d-4056-b5a8-fb272e1c08b1'
간단한 예제를 통해 OAuth 인증 서버와 자원 서버를 구현해 보았다.
'Spring' 카테고리의 다른 글
Spring Security OAuth2 인증서버 만들기 01 (0) | 2019.10.10 |
---|---|
Spring Framework : Transaction (0) | 2017.06.14 |
Spring Framework : Spring Security @Valid (0) | 2017.06.09 |
Spring Framework : Spring Security 패스워드 암호화 (0) | 2017.06.09 |
Spring Framework : Spring Security 인증, 권한부여 (0) | 2017.06.08 |