아키텍처
그림은 프로파게이션의 모델 그림이고 나는 서비스 1개만 구성되어있다.
흐름 설명
1. 사용자 인증
사용자가 인증이 필요한 시스템에 접근했을경우 Spring Boot는 사용자를 Keycloak 로그인 페이지로 보냅니다.
사용자는 로그인 화면에서 아이디와 비밀번호를 입력하게 됩니다.
2. JWT 토큰 발급
사용자가 로그인에 성공하면 Keycloak은 사용자에게 JWT 토큰을 발급합니다.
이 토큰은 사용자 정보를 담고 있으며, 나중에 인증된 요청임을 증명하는 데 사용됩니다.
3. 서비스 요청
사용자는 로그인 후 받은 JWT 토큰을 가지고, Spring Boot 애플리케이션에 요청을 보냅니다. 이 토큰은 요청의 Authorization 헤더에 포함됩니다.
Spring Boot는 Keycloak에서 발급한 JWT 토큰을 검증합니다. 이를 위해 Keycloak의 공개 키를 사용해 토큰이 유효한지 확인하고, 사용자가 적절한 권한을 가지고 있는지 검사합니다.
4. 프로파게이션
- 이 구조에서는 사용자가 한 번 로그인하여 받은 JWT 토큰을 계속 사용합니다. 즉, 사용자가 다른 페이지나 리소스에 접근할 때마다 JWT 토큰을 포함시켜 요청을 보내고, Spring Boot은 매번 이 토큰을 확인하여 사용자의 권한을 체크합니다.
키클락 설치
KeyCloak을 설치하였고 admin 아이디를 생성하였다.
키클락 세팅
클라이언트 프로토콜은 OIDC
액세스 타입은 confiential로 설정하였다.
ROLE은 Admin User를 생성하였다.
어드민은 USER와 ADMIN Role을 부여하였고 유저는 USER만 등록하였다.
포스트맨으로 테스트 해보니 유저, 어드민 모두 정상적으로 액세스 토큰을 발급할수있었다.
https://jwt.io/ 에서 페이로드 확인!
roles에 user만 등록되어 있다.(당연)
admin도 발급!!
admin 계정의 jwt 토큰 정보
roles에 어드민 user가 정상적으로 등록되어있다.
JWT 토큰 발급받아 로그인하기
keycloak.bearer-only=true
테스트를 위해 bearer only 설정을 true로 바꿔서
인증 실패시 따로 로그인 페이지로 연결 하지 않았다
옵션을 false로 하면 컨트롤러 요청했을때 인증 실패시 키클락 로그인페이지로 redirect 된다
이번 프로젝트는 테스트를 위해 true로 설정하였다.
( 글 맨 아래에 flase 바꿔서 테스트 한것도 있다.)
Config
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/test/permitAll").permitAll()
.antMatchers("/test/authenticated").authenticated()
.antMatchers("/test/user").hasAnyRole("USER")
.antMatchers("/test/admin").hasAnyRole("ADMIN")
.anyRequest()
.permitAll();
http.csrf().disable();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
permitall 은 모두 허용 authenticated 는 인증받은 사용자만
user는 user role을 부여받은 사람만
admin 는 admin role를 부여받은 사람만 가능하도록 했다.
Controller
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
// 누구나 접근 가능한 엔드포인트
@GetMapping("/permitAll")
public ResponseEntity<String> permitAll() {
return ResponseEntity.ok("모든 사용자가 접근 가능합니다.");
}
// 인증된 사용자만 접근 가능한 엔드포인트
@GetMapping("/authenticated")
public ResponseEntity<String> authenticated(@RequestHeader(value = "Authorization", required = false) String authorization) {
log.debug("Authorization Token: {}", authorization);
if (authorization == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 토큰이 필요합니다.");
}
return ResponseEntity.ok("인증된 사용자입니다.");
}
// USER 역할이 필요한 엔드포인트
@GetMapping("/user")
public ResponseEntity<String> user(@RequestHeader(value = "Authorization", required = false) String authorization) {
log.debug("Authorization Token: {}", authorization);
if (authorization == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 토큰이 필요합니다.");
}
return ResponseEntity.ok("USER 권한 확인 완료.");
}
// ADMIN 역할이 필요한 엔드포인트
@GetMapping("/admin")
public ResponseEntity<String> admin(@RequestHeader(value = "Authorization", required = false) String authorization) {
log.debug("Authorization Token: {}", authorization);
if (authorization == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 토큰이 필요합니다.");
}
return ResponseEntity.ok("ADMIN 권한 확인 완료.");
}
}
토큰 없이 요청 보내보기
permitall 같은경우 토큰없이 아무나 당연히 접근이 가능하다.
나머지 authenticated , user ,admin 은 토큰이 필요하다고 나온다.
유저 토큰을 통해 요청보내기
admin 권한이 없는 user는 admin권한이 필요한 요청엔 403 상태가 나온다!!
ADMIN 토큰을 통해 요청 보내기
admin 토큰은 모든 요청이 가능하다.
간단한 프로젝트로 OIDC 인증 인가 시스템을 구현해보았고 너무 재밌었다. 개선할수있으면 더 개선해야겠다.
키클락 로그인 페이지로 리다이렉트하기
keycloak.bearer-only=false
로 설정하면
Controller에 연결시 키클락 로그인 페이지로 연결된다.
로그인하면
jsessionid 랑 oauth_token_request_state가 저장되어있고
로그인후 세션연결된 부분을 보니까 잘 연결되어있다.
로그아웃도 관리자가 직접 로그아웃 시킬수있다.
편리한 GUI 제공하는 키클락 좋다..
sso 인증서버만들기 너무나 재밌다 실제로 프로젝트에 붙여서 사용해보고싶은 생각이들었다.
다음프로젝트엔 사용해보려고 한다.
'스프링부트' 카테고리의 다른 글
[스프링부트] OCP 원칙을 지키도록 수정해보기 (1) | 2024.11.26 |
---|---|
스프링 부트 Redis 분산 락 활용 동시성 제어 (1) | 2024.07.04 |
이미지 업로드 비동기 처리 (0) | 2024.06.19 |
[스프링] 프로젝트 N+1 해결하기 (fetch join (0) | 2023.09.01 |
[스프링부트] 개인 프로젝트 버그 고치기 (0) | 2023.08.27 |