[Security] Jwt Authorization ๊ตฌํ by Overriding Spring Security ์ธ์ฆ ์ํคํ ์ณ
by rlaehddnd0422์ง๋ ํฌ์คํ ์ ์ด์ด Spring Security์ ํํฐ๋ฅผ ์ปค์คํ ํ์ฌ Authorization Filter๋ฅผ ๊ตฌํํด๋ด ์๋ค.
๋ก๊ทธ์ธ์ Jwt ๋ฐฉ์์ ์ ์ฉํ ๋, Authentication Filter์์๋ ์์ฒญํค๋๋ฅผ ์ธ์ฝ๋ฉํ์ฌ ํ ํฐ์ผ๋ก ๋ณํ์ํค๊ณ .. authenticate()๋ฅผ ํตํด ํ ํฐ๊ณผ DB์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋น๊ตํ์ฌ ๊ฒ์ฆํ๊ณ .. ํ ํฐ์ ๋ง๋ค์ด ์๋ตํค๋์ ์ค์ ํ๊ณ .. ๋ฑ ๊ฝค ๋ณต์กํ ๊ณผ์ ์ ๊ฑฐ์ณ ์ธ์ฆ์ด ์ด๋ฃจ์ด์ก์ต๋๋ค.
ํ์ง๋ง, Authorization์ ๊ณผ์ ์ ๋น๊ต์ ๊ฐ๋จํฉ๋๋ค!
ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์ ํ๋ ๋ฆฌ์์ค์ ๋ํ ์์ฒญ์ ๋ณด๋ผ ๋ Authentication ๊ณผ์ ์์ ๋ฐ๊ธ๋ฐ์ JWT๋ฅผ ์์ฒญํค๋์ ๋ณด๋ด๊ณ ,
์๋ฒ์์๋ ์ด ํ ํฐ์ด ์ ํจํ์ง ๊ฒ์ฌ ํ ์ ํจํ๋ฉด ์์ฒญ์ ๋ํ ์๋ต์ ๋ณด๋ด์ฃผ๋ฉด ๋ฉ๋๋ค.
์ด ๊ณผ์ ์์ ํ์ํ ํํฐ๋ ๋ฐ๋ก 'BasicAuthenticationFilter' ์ ๋๋ค.
BasicAuthenticationFilter
์ด ํํฐ๋ Authorization, Authentication์ด ํ์ํ ํน์ ์ฃผ์๋ฅผ ์์ฒญํ ๋ ์ฌ์ฉ๋๋ ํํฐ์ ๋๋ค.
์์ํ๊ฒ ๋ก๊ทธ์ธ(์ธ์ฆ)์๋ง ์ฌ์ฉ๋์๋ UsernamePasswordAuthenticationFilter๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์ด ํํฐ๋ฅผ ์์๋ฐ์ JWT ๋ฐฉ์์ ๋ง๊ฒ ๋ฆฌํํ ๋งํ๊ณ ๋ฑ๋กํด๋ด ์๋ค.
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
BasicAuthentication ํํฐ๋ AuthenticationManager๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์์ฑ์์ ๋ฐ๋์ ๋ฃ์ด ์ฃผ์ด์ผ ํฉ๋๋ค.
public JwtAuthorizationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
super(authenticationManager, authenticationEntryPoint);
}
+ ์ถ๊ฐ์ ์ผ๋ก ์ฌ๊ธฐ์์ AuthenticationEntryPoint๋ ์ธ์ฆ ์ฒ๋ฆฌ ๊ณผ์ ์์ ์์ธ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ ์์ธ๋ฅผ ์ฒ๋ฆฌํด์ฃผ๋ ํธ๋ค๋ฌ์ ๋๋ค.
์ง๊ธ ๋น์ฅ ์์ธ ์ฒ๋ฆฌ๋ ์ฐ์ ์ ์ด์ง๋ ์๊ธฐ ๋๋ฌธ์ ์ฒซ ๋ฒ์งธ ์์ฑ์๋ฅผ ์ฌ์ฉํ๋๋ก ํ๊ฒ ์ต๋๋ค.
BasicAuthenticationFilter - doFilterInternal()
์ผ๋ฐ์ ์ธ ํํฐ์ doFilter์ ๊ธฐ๋ฅ์ ํ๋ ๋ฉ์๋๋ก, ์ค์ง์ ์ผ๋ก ์ด ๋ฉ์๋์ ๊ถํ, ์ธ์ฆ์ ๋ํ ๊ฒ์ฆ๋ก์ง์ด ๊ตฌํ๋์ด์ผ ํฉ๋๋ค.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String jwtHeader = request.getHeader("Authorization");
// ํค๋๊ฐ ์๋์ง ํ์ธ
if (jwtHeader == null || !jwtHeader.startsWith("Bearer")) {
chain.doFilter(request, response);
return;
}
String jwtToken = request.getHeader("Authorization").replace("Bearer ","");
String username = JWT.require(Algorithm.HMAC512("cos")).build().verify(jwtToken).getClaim("username").asString();
// ์๋ช
์ด ์ ์์ ์ผ๋ก ๋จ
if (username != null) {
Optional<User> userEntity = userRepository.findByUsername(username);
PrincipalDetails principalDetails = new PrincipalDetails(userEntity.get());
log.info("HI");
// Jwt ํ ํฐ ์๋ช
์ ํตํด ์๋ช
์ด ์ ์์ด๋ฉด Authentication ๊ฐ์ฒด ์์ฑ
Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities());
log.info("{}", authentication.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
super.doFilterInternal(request, response, chain);
}
๋ก์ง์ ์ด๋ ์ต๋๋ค.
1. ์ฐ์ Request Header์ Authorization์ด ์๋์ง ํ์ธํฉ๋๋ค. ( ๋ก๊ทธ์ธ ๊ณผ์ ์์ ๋ฐ๊ธ๋ฐ์ JWT )
2. ์๋ค๋ฉด ๋ค์ ํํฐ๋ก ๋๊ธฐ๊ณ ๋ฆฌํด.
3. ์๋ค๋ฉด ๋ฐ๊ธ๋ฐ์ ํ ํฐ์ ๋ฒ ๋ฆฌ์ด๋ฅผ ๋ฒ๊ธฐ๊ณ , ์์ Jwt ๋ง ์ถ์ถํฉ๋๋ค.
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC512(JwtProperties.SECRET)).build();
String username = jwtVerifier.verify(jwtToken).getClaim("username").asString();
- 4. JWT์ require ๋ฉ์๋๋ก ํ ํฐ์ ๊ฒ์ฆํ ์ ์์ต๋๋ค.
- ์ํฌ๋ฆฟ ํค๋ฅผ ๊ฐ์ง๊ณ ํ ํฐ ํด๋ ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.
- ํ ํฐ ํด๋ ๊ฐ์ฒด๋ฅผ ํตํด ์์ฒญ ํค๋๋ก ๋ฐ์ ํ ํฐ์ ํด๋ ํ์ฌ username์ ํ๋๊ฐ์ string์ผ๋ก ๋ณํ
- username์ด null์ด ์๋๋ผ๋ฉด ์๋ช ์ด ์ ์์ ์ผ๋ก ๋์๋ค๊ณ ๋ด๋ ๋ฌด๋ฐฉ.
// ์๋ช
์ด ์ ์์ ์ผ๋ก ๋จ
if (username != null) {
Optional<User> userEntity = userRepository.findByUsername(username);
PrincipalDetails principalDetails = new PrincipalDetails(userEntity.get());
// Jwt ํ ํฐ ์๋ช
์ ํตํด ์๋ช
์ด ์ ์์ด๋ฉด Authentication ๊ฐ์ฒด ์์ฑ
Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
- ์๋ช
(์ธ์ฆ)์ ํต๊ณผํ์ผ๋ ์ด์ ์ธ๊ฐ๋ง ์ฒ๋ฆฌํด์ฃผ๋ฉด ๋ฉ๋๋ค.
- jwt๋ฅผ ํด๋ ํ์ฌ ์ป์ username์ ์ฐพ๊ณ , ์ฐพ์ user๋ฅผ PrincipalDetails์ ๋ด์ Authentication ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ค์๋ค.
- ๋ง์ง๋ง์ผ๋ก, ๋ง๋ Authentication ๊ฐ์ฒด๋ฅผ SecurityContext์ ๋ด์์ฃผ๊ณ ๋ค์ ํํฐ๋ก ์งํ์ํต๋๋ค.
- Authentication ๊ฐ์ฒด๋ฅผ Security Context์ ๋ด์์, Authorization์ ์ฒ๋ฆฌํ์์ต๋๋ค.
- ์๋ฒ์์๋ Security Context์ ๋ด๊ธด Authentication ๊ฐ์ฒด ๋ด๋ถ์ UserDetails ๊ฐ์ฒด์ Role ์ ๋ณด๋ฅผ ํ์ธํ์ฌ ์์ฒญ์ ํ์ฉํ ์ง, ๊ฑฐ๋ถํ ์ง ํ๋จํฉ๋๋ค.
ํํฐ ์ฒ๋ฆฌ๊ฐ ๋ชจ๋ ๋๋ฌ์ต๋๋ค. SecurityConfig์ ํํฐ์ ๋ฑ๋กํด์ฃผ๊ณ Postman์ผ๋ก ํ ์คํธํด๋ด ์๋ค.
ํํฐ Override
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig{
private final CorsFilter corsFilter;
private final UserRepository userRepository;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // ์ธ์
์ฌ์ฉ X, Stateless
.and()
.formLogin().disable() // ํผ ๋ก๊ทธ์ธ ์ฌ์ฉ X
.httpBasic().disable() // ๊ธฐ๋ณธ์ธ์ฆ๋ฐฉ์ ID, PW ์ฌ์ฉ X
.apply(new MyCustomDsl())
.and()
.authorizeRequests()
.antMatchers("/api/v1/user/**")
.access("hasRole('ROLE_USER') or hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")
.antMatchers("/api/v1/manager/**")
.access("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")
.antMatchers("/api/v1/admin/**")
.access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll();
return http.build();
}
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
@Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http
.addFilter(corsFilter)
.addFilter(new JwtAuthenticationFilter(authenticationManager))// ์ธ๊ฐ
.addFilter(new JwtAuthorizationFilter(authenticationManager,userRepository)); // ์ธ์ฆ
}
}
Postman ๊ฒ์ฆ
Login์ ํตํด JWT ๋ฐ๊ธ
JWT๋ฅผ ์์ฒญ ํค๋์ ๋ฃ์ด์ ์ ํ๋ ๋ฆฌ์์ค ์์ฒญ
๊ถํ ๋ณ๊ฒฝ ํ admin ํ์ด์ง ์์ฒญ Test
<์ ๋ฆฌ>
- ๋ก๊ทธ์ธ์์ ๋ฐ๊ธ๋ฐ์ JWT๋ฅผ ์ฌ์ฉํด์, ์ธ์ฆ, ์ธ๊ฐ๊ฐ ํ์ํ ์ ํ๋ ๋ฆฌ์์ค ์ ๊ทผ ์์ฒญ์ ๋ํด ์ฒ๋ฆฌํด์ฃผ๋ ํํฐ๋ฅผ JWT ์ธ์ฆ ๋ฐฉ์์ผ๋ก ๋ฆฌํํ ๋งํ์ฌ ์ ์ฉํด๋ณด์์ต๋๋ค.
- BasicAuthenticationFilter๋ ์ธ์ฆ, ์ธ๊ฐ๊ฐ ํ์ํ ์ ํ๋ ๋ฆฌ์์ค ์ ๊ทผ ์์ฒญ ์ ํธ์ถ๋๋ ํํฐ์ ๋๋ค.
- Spring Security์ ์ ์ฒด์ ์ธ ์ธ์ฆ ์ํคํ ์ฒ์ ํ๋ฆ์ ์ดํดํ๋ฉด, Jwt Base์ Authentication, Authorization์ ์ฒ๋ฆฌํ๋ ๋ก์ง๋ค์ ์ฝ๊ฒ ์ดํดํ ์ ์์ต๋๋ค.
<์ฐธ๊ณ ์๋ฃ>
https://www.youtube.com/watch?v=mW-8MQ-4arU
'๐ Backend > Spring Security' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Security] JWT ๊ตฌํ (2) - Security ์ค์ , ๋ฐ์ดํฐ ์ฝ์ (0) | 2023.06.06 |
---|---|
[Security] JWT ๊ตฌํ (1) - ํ๋ก์ ํธ ์์ฑ (0) | 2023.06.06 |
[Security] Jwt Authentication ๊ตฌํ by Overriding Spring Security ์ธ์ฆ ์ํคํ ์ณ (0) | 2023.06.05 |
[Security] JWT(Json Web Token) ์ธ์ฆ ๋ฐฉ์ (0) | 2023.06.02 |
[Security] ์ฟ ํค vs ์ธ์ vs ํ ํฐ (0) | 2023.06.02 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422