-
[Spring] Spring Security OAuth2.0 인증 서버 구축 v0.2Spring 2021. 12. 7. 00:41728x90반응형
지난 시간에 우리는 OAuth가 무엇이고, OAuth2.0의 인증 방식에 대해서 알아보았다.
이번 시간에는 OAuth2.0 인증방식을 어떻게 구현하는지에 대해서 알아보고자 한다.
먼저 지난 시간에 알아봤던 OAuth2.0 승인 방식의 종류를 먼저 되짚어 보자
OAuth2.0 승인 방식의 종류
1. Authorization Code Grant (권한 부여 승인 방식)
: 클라이언트가 다른 사용자 대신 특정 리소스에 접근을 요청할 때 사용. 리소스 접근을 위한 사용자 명과 비밀번호, 권한 서버에 요청해서 받은 권한 코드를 함께 사용해 리소스에 대한 Access Token을 받는 방식.
2. Implicit Grant (암묵적 승인 방식)
: 권한 부여 코드 승인 방식과 유사하지만 차이점이 권한 코드 교환 단계 없이 Access Token을 즉시 반환해 이를 인증에 이용하는 방식.
3. Resource Owner Password Credentials Grant (자원 소유자 자격 증명 승인 방식)
: 클라이언트가 사용자 이름과 암호를 직접 Authorization Server에 전달하여 Access Token에 대한 사용자의 자격 증명을 교환하는 방식.
4. Client Credentials Grant (클라이언트 자격 증명 승인 방식)
: 클라이언트가 컨텍스트 외부에서 Access Token을 얻어 특정 리소스에 접근을 요청할 때 사용하는 방식
Authorization Code Grant Type 방식
1)클라이언트가 Client ID, Redirection URI,Response_type을 지정해Authorization Server에 전달.권한 서버는정상적으로 인증이 되면 권한 부여 코드를 클라이언트에 전달.
2) 성공적으로 권한 부여 코드를 받은 클라이언트는 권한 부여 코드를 사용해 Access Token을 권한 서버에 추가로 요청. 필요한 파라미터는 Client ID, Client Secret, Redirection URI, Authorization_Code
3) 마지막으로 받은 액세스 토큰을 사용해 리소스 서버에 사용자의 데이터를 전송
Spring Security Code
인증 서버 설정
//설정파일임을 등록하기 위한 어노테이션 @Configuration //OAuth 서버에 필요한 기본 설정(토큰을 발행하고, 발행된 토큰을 검증하는 역할) @EnableAuthorizationServer //인증 서버에 대한 설정 public class AuthServerConfig extends AuthorizationServerConfigurerAdapter { //클라이언트에서 사용될 사용자 정보 객체 @Autowired @Qualifier("userDetailsService") private UserDetailsService userDetailsService; //인증 토큰을 관리하는 객체 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients .inMemory() //DB가 아닌 메모리에 저장하는 방식 .withClient("authorization") //Authorization Code Grant Type Client 명 .secret("secret") // Client Secret .authorizedGrantTypes("authorization_code","refresh_token") //Authorization Code 방식을 사용하며 Refresh Token을 받겠다. .scopes("read_profile") // 권한 사용 목적 .redirectUris("http://localhost:8080/callback") //인증 허가 시 보여질 URI .accessTokenValiditySeconds(60*10) //AccessToken 만료 기한 (초) .refreshTokenValiditySeconds(60*60) //RefreshToken 만료 기한 (초) } //토큰 인증, 토큰 엔드포인트 정의 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } }
전반적인 서버 설정
@Configuration @EnableWebSecurity //전반적인 서버에 대한 security 설정 public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean //인증 토큰을 관리하는 클래스. //리소스에 대한 요청이 들어오면 AuthenticationManager는 Authenticate 메서드를 호출해 후속요청에 사용할 권한 부여 인스턴스를 가져옴 public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } //패스워드 인코딩 방식을 결정해주는 빈 @Bean public PasswordEncoder passwordEncoder(){ return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } //client 회원 정보 관리 //클라이언트에서 사용될 사용자 정보를 등록 //권한서버에서 엔드포인트에 설정 @Bean public UserDetailsService userDetailsService() { PasswordEncoder encoder = passwordEncoder(); String password = encoder.encode("pass"); //메모리에 유저를 생성 InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("user").password(password).roles("USER").build()); manager.createUser(User.withUsername("admin").password("{noop}pass").roles("USER", "ADMIN").build()); return manager; } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .disable() .authorizeRequests() //시큐리티 처리에 있어 request 정보를 서블릿에 전달하기 위한 목적으로 사용하겠다 .anyRequest() .authenticated() //모든 request는 인증이 된 상태여야 한다. .and() .formLogin() //시큐리티에서 제공하는 로그인 form을 사용하겠다 .and() .httpBasic(); //특정 resource에 대한 접근을 요청 시 브라우저가 사용자에게 username과 password를 확인해 인가를 제한하는 방법 } }
인증
http://localhost:8080/oauth/authorize?response_type=code&client_id=authorization&redirect_uri=http://localhost:8080/callback&scope=read_profile 로 접속하면 아래와 같이 로그인 페이지로 리다이렉트 됨.
UserDetailService에 입력한 로그인 정보를 입력. 소셜 로그인에서 소셜 아이디로 로그인하는 것과 동일한 행위.
유저 정보 인증이 완료되면 설정한 scope에 대한 권한을 승인할 것인지에 대한 페이지가 redirect_uri로 설정한 URI에서 보여집니다. 소셜 로그인에서 프로필 정보, 프로필 사진을 사용하는 것에 동의하는지 묻는 것과 동일한 행위입니다.
권한 승인이 완료되면 위와 같이 권한 코드가 전송이 됩니다. Authorization Code Grant Type 방식에서 말한 권한 부여 코드를 응답받은 것입니다.
리다이렉트된 페이지에서 발급 받은 권한 부여 코드로 PostMan을 통해 권한 서버에 Access Token을 요청 받을 수 있습니다.
실제 응답값은 아래와 같습니다.
Implicit Grant 방식
1) 클라이언트가 파라미터로 Client ID, Redirection URI, 응답 타입을 token으로 지정해 권한 서버로 전달. 보낸 파라미터로 정상적으로 인증이 되면 권한 부여 코드를 클라이언트에 전달.
2) 응답해 준 Access Token이 유효한지 검증 요청
3) 요청받은 Access Token 정보에 대한 검증에 대한 응답 값 정보를 돌려줍니다.
4) 유효한 Access Token 기반으로 Resource Server와 통신
Spring Security Code
인증 서버 설정
//설정파일임을 등록하기 위한 어노테이션 @Configuration //OAuth 서버에 필요한 기본 설정(토큰을 발행하고, 발행된 토큰을 검증하는 역할) @EnableAuthorizationServer //인증 서버에 대한 설정 public class AuthServerConfig extends AuthorizationServerConfigurerAdapter { //클라이언트에서 사용될 사용자 정보 객체 @Autowired @Qualifier("userDetailsService") private UserDetailsService userDetailsService; //인증 토큰을 관리하는 객체 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients .inMemory() //DB가 아닌 메모리에 저장하는 방식 .withClient("implicit") //Implicit 방식 client 명 .secret("secret") // Client Secret .authorizedGrantTypes("implicit") //implicit 방식을 사용하겠다. .scopes("read_profile") // 권한 사용 목적 .redirectUris("http://localhost:8080/callback") //인증 허가 시 보여질 URI .accessTokenValiditySeconds(60*10) //AccessToken 만료 기한 } //토큰 인증, 토큰 엔드포인트 정의 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } }
Client명과 인증방식, Refresh Token을 수정해주기만 하면 Implicit Grant 방식 설정이 완료됩니다.
인증
http://localhost:8080/oauth/authorize?response_type=token&client_id=implicit&redirect_uri=http://localhost:8080/callback&scope=read_profile
으로 요청합니다.
Authorization Code 방식과 다른 점은 response_type에 code가 아닌 token을 요청합니다.
위와 같이 로그인 정보 입력
유저 정보 인증이 완료되면 scope에 대한 권한 승인 페이지가 나옵니다.
Implicit Grant 방식에서는 Token정보를 리다이렉트된 URL 값에 바로 전달됩니다.
Implicit Grant 방식은 리프레시 토큰을 발급하지 않습니다.
Implicit Grant은 서드파티 애플리케이션에 의한 리다이렉트 URI 등록이 필요합니다. 등록되지 않은 클라이언트에 액세스 토큰이 전달되는 것을 막기 위한 장치입니다.
Resource Owner Password Credentials Grant 방식
1) 인증을 진행합니다. Username, Password를 통해서 자격 증명이 진행
2) 넘겨받은 정보 기반으로 권한 서버에 Access Token 정보를 요청
3) Access Token 정보를 응답 받음. 이때 Refresh Token 정보도 넘겨줄 수 있음
4) Access Token 기반으로 Resource Server와 통신
Spring Security Code
//설정파일임을 등록하기 위한 어노테이션 @Configuration //OAuth 서버에 필요한 기본 설정(토큰을 발행하고, 발행된 토큰을 검증하는 역할) @EnableAuthorizationServer //인증 서버에 대한 설정 public class AuthServerConfig extends AuthorizationServerConfigurerAdapter { //클라이언트에서 사용될 사용자 정보 객체 @Autowired @Qualifier("userDetailsService") private UserDetailsService userDetailsService; //인증 토큰을 관리하는 객체 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients .inMemory() //DB가 아닌 메모리에 저장하는 방식 .withClient("resource") //Resource Owner Password Credentials Grant 방식 Client 명 .secret("secret") // Client Secret .authorizedGrantTypes("password","refresh_token")//Password방식을 사용하겠다. .scopes("read_profile") // 권한 사용 목적 .accessTokenValiditySeconds(60*10) //AccessToken 만료 기한 초단위 .refreshTokenValiditySeconds(60*60) //RefreshToken 만료 기한 초단위 } //토큰 인증, 토큰 엔드포인트 정의 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } }
인증
인증은 Postman을 통해 진행합니다.
인증 요청은 위 플로우 설명 처럼 password 기반으로 token 정보를 요청합니다. 유저의 비밀번호 인증이 완료되면 아래와 같이 응답을 받습니다.
Client Credentails Grant 방식
1) Access Token 정보를 요청
2) Access Token 정보를 응답하여 Access Token 기반으로 Resource Server와 통신
Spring Security Code
//설정파일임을 등록하기 위한 어노테이션 @Configuration //OAuth 서버에 필요한 기본 설정(토큰을 발행하고, 발행된 토큰을 검증하는 역할) @EnableAuthorizationServer //인증 서버에 대한 설정 public class AuthServerConfig extends AuthorizationServerConfigurerAdapter { //클라이언트에서 사용될 사용자 정보 객체 @Autowired @Qualifier("userDetailsService") private UserDetailsService userDetailsService; //인증 토큰을 관리하는 객체 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients .inMemory() //DB가 아닌 메모리에 저장하는 방식 .withClient("client") //client라는 이름과 secret라는 비밀번호를 사용해 권한 서버를 사용할 클라이언트 설정 .secret("secret")// secret .authorizedGrantTypes("client_credentials") //client 방식을 사용하겠다. .scopes("read_profile")//권한 사용 목적 .accessTokenValiditySeconds(60*10); //RefreshToken 만료 기한 초단위 } //토큰 인증, 토큰 엔드포인트 정의 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } }
인증
인증은 Postman을 통해서 진행합니다.
다음과 같이 요청을 보내면 아래와 같은 응답값을 확인할 수 있습니다.
AccessToken 재발급
AccessToken이 만료시 RefreshToken을 통해 재발급 받을 수 있습니다.
재발급
AccessToken 만료시 RefreshToken을 파라미터로 넘겨주면 다음과 같은 응답값을 받게 됩니다.
이렇게 RefreshToken을 받을 수 있는 방식은 Authorization Code Grant, Resource Owner Password Credentials Grant입니다.
반응형참고
https://cheese10yun.github.io/spring-oauth2-provider/#null728x90반응형'Spring' 카테고리의 다른 글
[Spring] Spring Security OAuth2.0 인증 서버 구축 v0.1 (0) 2021.12.01