์๋ฒ๊ฐ๋ฐ์บ ํ์ ๋ ๋ฒ์งธ ๊ฐ์ธ ๊ณผ์ ๋ก ์ธ์ฆ ์๋ฒ ๊ตฌ์ถ์ด ์ฃผ์ด์ก๋ค. ์ธ์ฆ ์๋ฒ๋ฅผ ๊ตฌ์ถํ๋ฉฐ ์ฌ์ฉํ๋ ๊ธฐ์ ๊ณผ ์ด์๋ค์ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค. ์คํ๋ง์์๋ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ๋ฅผ ํตํด ๋ฆฌ์์ค์ ์ฌ์ฉ์ ์ฝ๊ฒ ์ปจํธ๋กค ํ ์ ์๋๋ก Spring Security๋ฅผ ์ ๊ณตํ๋ค. ์ํ๋ฆฌํฐ๋ฅผ ์ ์ฉํ๋ฉฐ ๋ฐฑ๊ธฐ์ ๋์ ์ ํ๋ธ ๊ฐ์ข์ happydaddy๋์ ํฌ์คํ ์ด ํฐ ๋์์ด ๋์๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ๋ ์คํ๋ง์ Dispatcher Servlet ์๋จ์ ํํฐ๋ฅผ ๋ฑ๋ก์์ผ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๊ฐ๋ก์ฑ๋ค. ์ด ํ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋ํด ๊ถํ์ด ์์ ๊ฒฝ์ฐ ๋ก๊ทธ์ธ ํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ ์ํจ๋ค. ๋๋ API ์๋ฒ๋ฅผ ๊ตฌ์ถํ๊ณ , ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก ํต์ ํ ๊ณํ์ด์๊ธฐ ๋๋ฌธ์ ์ด์ ๋ง๋ ์ํ๋ฆฌํฐ ์ค์ ๊ณผ ๋๋ถ์ด JWT๋ฅผ ์ํ ํํฐ๊ฐ ํ์ํ๋ค.
Spring Security
์ํ๋ฆฌํฐ ์ค์ ์ ์๋์ ๊ฐ๋ค. ์คํ๋ง ์ํ๋ฆฌํฐ๋ ๋ค์ํ ํํฐ๋ฅผ ํตํด ์ธ์ฆ์ ์งํํ๋๋ฐ, ๊ทธ ์ค UsernamePasswordAuthenticationFilter
๊ฐ ์ธ์ฆ๋์ง ์์ ์ฌ์ฉ์๋ฅผ ๋ก๊ทธ์ธ ํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ ์ํค๋ ์ญํ ์ ํ๋ ํํฐ์ด๋ค. ๋ฐ๋ผ์ ํด๋น ํํฐ์ ์์ JWT๋ฅผ ํ์ธํ๊ธฐ ์ํ ํํฐ๋ฅผ ๋ฑ๋กํด์ผํ๋ค.
@RequiredArgsConstructor
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtUtil jwtUtil;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(
"/users/signin",
"/users/signup/**",
"/users/password/**",
"/exception/**"
).permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(new CustomAccessDeniedHandler())
.and()
.exceptionHandling()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.addFilterBefore(
new JwtAuthenticationFilter(jwtUtil),
UsernamePasswordAuthenticationFilter.class
);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
๋ํ ์ฐ๋ฆฌ๊ฐ ๋ฑ๋กํ JWT ํํฐ์์ ์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ, ํํฐ์ ์์์ Spring์ DispatcherServlet๊น์ง ์์ธ๊ฐ ๋๋ฌํ์ง ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ฒ๋ฆฌํ๊ธฐ์ํ Handler๊ฐ ํ์ํ๋ค.
ํค๋์ ํ ํฐ์ด ๋ด๊ฒจ์์ง ์๊ฑฐ๋, ํ ํฐ์ด ๋ง๋ฃ ๋๋ ์กฐ์๋ ๊ฒฝ์ฐ ํ ํฐ์ ๊ฒ์ฆํ๋ ๋ถ๋ถ์์ ํ๋ก์ธ์ค๊ฐ ๋๋๊ฒ ๋๋ฏ๋ก, ์ด๋ฅผ ์ก์๋ด๊ธฐ ์ํด์๋ SpringSecurity์์ ์ ๊ณตํ๋ AuthenticationEntryPoint
๋ฅผ ์์๋ฐ์ ์ฌ์ ์ ํด์ผํ๋ค. ์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ /exception/entrypoint
๋ก ํฌ์๋ฉํ๋๋ก ์ฒ๋ฆฌํ๋ค.
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
response.sendRedirect("/exception/entrypoint");
}
}
๋ฐ๋ฉด ํค๋์ ์ฌ๋ฐ๋ฅธ ํ ํฐ์ด ๋ด๊ฒจ์์ผ๋, ์์ฒญ์ ๋ํ ๊ถํ์ด ์๋ ๊ฒฝ์ฐ SpringSecurity์์ ์ ๊ณตํ๋ AccessDeniedHandler
๋ฅผ ์์๋ฐ์ ํ, /exception/accessdenied
๋ก ํฌ์๋ฉํ๋๋ก ์ฒ๋ฆฌํ๋ค.
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect("/exception/accessdenied");
}
}
JWT Authentication Filter
JWT ์ธ์ฆ ํํฐ๋ OncePerRequestFilter
๋ฅผ ์์๋ฐ์ ์์ฑํ๋ค. ํด๋น ํํฐ์์ ํ ํฐ์ ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ์งํ๋๋ค.
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private JwtUtil jwtUtil;
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = jwtUtil.getToken(request);
if(token != null && jwtUtil.isValidToken(token)) {
Authentication authentication = jwtUtil.getAuthentication(token);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
ํ ํฐ์ด ์ ํจํ ํ ํฐ์ผ๋ก ํ๋ช
๋๋ฉด, ํ ํฐ์์ ์ฌ์ฉ์์ email๊ณผ role์ ๊บผ๋ด์์ Authentication
์ ๋ง๋ ํ, SecurityContext์ ํด๋น Authentication
์ ์ ๋ฌํ๋ค. ์ด Authentication
๊ฐ์ฒด๋ฅผ ํตํด ์ฌ์ฉ์๊ฐ ํ ํฐ์ ํตํด ์ ๊ทผํ ์ ์๋ ๋ฆฌ์์ค๊ฐ ์ ํด์ง๋ค.
@Component
public class JwtUtil {
public Authentication getAuthentication(String token) {
Claims claims = getClaims(token);
String email = claims.getSubject();
int role = (int) claims.get("role");
return new UsernamePasswordAuthenticationToken(
email,
null,
Collections.singletonList(new SimpleGrantedAuthority(this.roles.get(role)))
);
}
...
}
'์ค๋ง์ผ๊ฒ์ดํธ ์๋ฒ๊ฐ๋ฐ์บ ํ 4๊ธฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์๋ฒ๊ฐ๋ฐ์บ ํ] ์ธ์ฆ ์๋ฒ - ๋ก๊ทธ์ธ ์ฑ๋ฅ ํ ์คํธ : nGrinder (0) | 2020.02.15 |
---|---|
[์๋ฒ๊ฐ๋ฐ์บ ํ] ์ธ์ฆ ์๋ฒ - AWS EC2 & RDS ๊ตฌ์ถ ๋ฐ ๋ฐฐํฌ (0) | 2020.02.10 |
[์๋ฒ๊ฐ๋ฐ์บ ํ] ์ธ์ฆ ์๋ฒ - ๋ก๊ทธ์ธ : JWT + Redis (1) | 2020.02.10 |
[์๋ฒ๊ฐ๋ฐ์บ ํ] ์ธ์ฆ ์๋ฒ - ์ด๋ฉ์ผ ์ธ์ฆ ํ์๊ฐ์ (0) | 2020.02.08 |
๐ ์๋ฒ๊ฐ๋ฐ์บ ํ 4๊ธฐ (0) | 2020.01.12 |