์คํ๋ง๋ถํฐ 3.0 ๋ถํฐ ์ํ๋ฆฌํฐ ์ค์ ๋ฐฉ๋ฒ์ด ๋ณ๊ฒฝ๋์์ต๋๋ค.
(Springย security 6.0 ๋ฒ์ ๋ถํฐ)
ํ์ฌ ํ๋ก์ ํธ :v 5.7.8
@Bean
public UserDetailsService userDetailsService()
@Bean
public PasswordEncoder passwordEncoder()
@Bean
public SecurityFilterChain filterChain(HttpSecurity http)
@Bean
public WebSecurityCustomizer webSecurityCustomizer()
implementation 'org.springframework.boot:spring-boot-starter-security'
์์กด์ฑ ์ถ๊ฐ ์ ๋ชจ๋ ๊ฒฝ๋ก์์ ๋ก๊ทธ์ธํ๋ฉด์ด ๋ณด์ด๊ฒ ๋๋ค.
โ
Application Class
์ ์ถ๊ฐํ๋ฉด Security ๊ธฐ๋ฅ์ ๋ ์ ์๋ค.@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
๋นํ์/ํ์์ ์ ๊ทผ ๊ฐ๋ฅํ ํ์ด์ง์ ๋ํด Spring Security ์ค์ ์ ์ถ๊ฐํด๋ณด์!
filterChain
์ผ๋ก ์ง์ ํ๋ฉด Spring์ด ํด๋น ๋น์ ์๋์ผ๋ก ์ธ์ํ๊ณ ์ฒ๋ฆฌ@Configuration
, @EnableWebSecurity
์ด๋
ธํ
์ด์
์ค์ HttpSecurity
๋ก ์ด๋ฃจ์ด์ง๋๋ค.code
JwtAuthFilter
// ํด๋ผ์ด์ธํธ๊ฐ ์ ์กํ ํ ํฐ์ ๊ฒ์ฌํ๋ ํํฐ
@Component
@Slf4j
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final TokenProvider tokenProvider;
// ํํฐ๊ฐ ํด์ผํ ์์
์ ๊ธฐ์
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String token = parseBearerToken(request);
log.info("Jwt Token Filter is running... - token: {}", token);
// ํ ํฐ ์์กฐ๊ฒ์ฌ ๋ฐ ์ธ์ฆ ์๋ฃ ์ฒ๋ฆฌ
if (token != null) {
// ํ ํฐ ์๋ช
์์กฐ ๊ฒ์ฌ์ ํ ํฐ์ ํ์ฑํด์ ํด๋ ์์ ์ป์ด๋ด๋ ์์
TokenUserInfo userInfo
= tokenProvider.validateAndGetTokenUserInfo(token);
// ์ธ๊ฐ ์ ๋ณด ๋ฆฌ์คํธ
List<SimpleGrantedAuthority> authorityList
= new ArrayList<>();
// authorityList.add(new SimpleGrantedAuthority("ROLE_" + userInfo.getRole().toString()));
// ์ธ์ฆ ์๋ฃ ์ฒ๋ฆฌ
// - ์คํ๋ง ์ํ๋ฆฌํฐ์๊ฒ ์ธ์ฆ์ ๋ณด๋ฅผ
// ์ ๋ฌํด์ ์ ์ญ์ ์ผ๋ก ์ฑ์์
// ์ธ์ฆ์ ๋ณด๋ฅผ ํ์ฉํ ์ ์๊ฒ ์ค์
AbstractAuthenticationToken auth
= new UsernamePasswordAuthenticationToken(
userInfo, // ์ปจํธ๋กค๋ฌ์์ ํ์ฉํ ์ ์ ์ ๋ณด
null, // ์ธ์ฆ๋ ์ฌ์ฉ์์ ๋น๋ฐ๋ฒํธ - ๋ณดํต ๋๊ฐ
authorityList // ์ธ๊ฐ ์ ๋ณด(๊ถํ ์ ๋ณด)
);
// ์ธ์ฆ์๋ฃ ์ฒ๋ฆฌ์ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋ณด ์ธํ
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// ์คํ๋ง ์ํ๋ฆฌํฐ ์ปจํ
์ด๋์ ์ธ์ฆ์ ๋ณด๊ฐ์ฒด ๋ฑ๋ก
SecurityContextHolder.getContext().setAuthentication(auth);
}
} catch (Exception e) {
e.printStackTrace();
log.error("ํ ํฐ์ด ์์กฐ๋์์ต๋๋ค.");
}
// ํํฐ ์ฒด์ธ์ ๋ด๊ฐ ๋ง๋ ํํฐ ์คํ๋ช
๋ น
filterChain.doFilter(request, response);
}
private String parseBearerToken(HttpServletRequest request) {
// ์์ฒญ ํค๋์์ ํ ํฐ ๊ฐ์ ธ์ค๊ธฐ
// http request header
// -- Content-type : application/json
// -- Authorization : Bearer dshlkfsdhfiue#$23hdshf
String bearerToken = request.getHeader("Authorization");
// ์์ฒญ ํค๋์์ ๊ฐ์ ธ์จ ํ ํฐ์ ์์ํ ํฐ ๊ฐ์ด ์๋๋ผ
// ์์ Bearer ๊ฐ ๋ถ์ด์์ผ๋ ์ด๊ฒ์ ์ ๊ฑฐํ๋ ์์
if (StringUtils.hasText(bearerToken)
&& bearerToken.startsWith("Bearer")) {
return bearerToken.substring(7);
}
return null;
}
}
WebSecurityConfig
@EnableWebSecurity
@RequiredArgsConstructor
// ์๋ ๊ถํ๊ฒ์ฌ๋ฅผ ์ํํ๊ธฐ ์ํ ์ค์
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
private final JwtAuthFilter jwtAuthFilter;
// ์ํ๋ฆฌํฐ ์ค์
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.httpBasic().disable()
// ์ธ์
์ธ์ฆ์ ์ฌ์ฉํ์ง ์๊ฒ ๋ค
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// ์ด๋ค ์์ฒญ์์ ์ธ์ฆ์ ์ํ ๊ฒ์ธ์ง ์ค์ , ์ธ์ ํ ๊ฒ์ธ์ง ์ค์
.authorizeRequests()
.antMatchers(HttpMethod.PUT, "/api/auth/promote").authenticated()
.antMatchers("/api/auth/load-profile").authenticated()
.antMatchers("/", "/api/auth/**").permitAll()
// .antMatchers(HttpMethod.POST, "/api/todos").hasRole("ADMIN")
.anyRequest().authenticated()
;
// ํ ํฐ์ธ์ฆ ํํฐ ์ฐ๊ฒฐ
http.addFilterAfter(
jwtAuthFilter
, CorsFilter.class // import์ฃผ์: ์คํ๋ง๊บผ
);
return http.build();
}
}