Spring Security 설정
Spring Security를 활용한 인증 및 인가
JWT(JSON(JavaScript Object Notation) Web Token)를 이용한 로그인 및 회원 가입 처리
WebMvcConfig, WebSecurityConfig 를 이용한 SecurityFilterChain 설정
1. JWT(JSON Web Token) 생성 및 인증 처리
유저 정보를 받아서 JWT 를 생성해준다.
생성된 토큰값으로 사용자 인증 진행
1-1 인증 토큰 생성
@Service
public class TokenProvider {
private static final String SECRET_KEY = "무작위 난수";
byte[] keyBytes = SECRET_KEY.getBytes();
Key key = Keys.hmacShaKeyFor(keyBytes);
public String create(UserEntity userEntity) {
// 기한 시작으로부터 1일로 설정
Date expiryDate = Date.from(
Instant.now().plus(1, ChronoUnit.DAYS));
return Jwts.builder()
.signWith(key, SignatureAlgorithm.HS512)
.setSubject(userEntity.getId())
.setIssuer("demo app")
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.compact();
}
public String validateAndGetUserId(String token) {
// parseClaimsJws 메서드가 Base 64로 디코딩 및 파싱.
// 즉, 헤더와 페이로드를 setSigningKey로 넘어온 시크릿을 이용해 서명 후, token의 서명과 비교.
// 위조되지 않았다면 페이로드(Claims) 리턴, 위조라면 예외를 날림
// userId가 필요하므로 getBody를 부른다.
Claims claims = Jwts.parserBuilder()
.setSigningKey(key).build()
.parseClaimsJws(token).getBody();
return claims.getSubject();
}
}
1-2 인증 필터 설정
OncePerRequestFilte 클래스를 상속받아서 설정함
OncePerRequestFilte는 한 요청당 반드시 한번만 실행되는 필터
@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private TokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
// 요청에서 토큰 가져오기
String token = parseBearerToken(request);
log.info("Filter is running....");
// 토큰 검사, JWT이므로 인가 서버에 요청하지 않고도 검증가능
if (token != null && !token.equalsIgnoreCase("null")) {
// userId 가져오기, 위조된경우 예외처리됨(tokenProvider.java 파일 참고)
String userId = tokenProvider.validateAndGetUserId(token);
log.info("Authenticated user ID : " + userId);
// 인증완료; SecurityContextHolder에 등록되야 인증된 사용자로 처리함
AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
// userId-> 인증된 사용자의 정보, 문자열이 아니어도 아무거나 등록가능, 보통 UserDetails 오브젝트로 처리하나 책에서는 생략한다고함,
// AuthenticationPrincipal
userId,
null,
AuthorityUtils.NO_AUTHORITIES);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication);
SecurityContextHolder.setContext(securityContext);
}
} catch (Exception ex) {
logger.error("Coule not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String parseBearerToken(HttpServletRequest request) {
// Http 요청의 헤더를 파싱하여 Bearer 토큰을 리턴시킨다.
String bearerToken = request.getHeader("Authorization");
// hasText(bearerToken) 널같은 값체크, startsWith("Bearer ") 시작을 Bearer로 시작하는지
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
// substring(7) bearerToken 토큰의 앞 7자리 리턴
return bearerToken.substring(7);
}
return null;
}
}
2. Spring Security 설정
책에 작성된 코드는 아래와 같다
@EnableWebSecurity @Configuration @Slf4j // WebSecurityConfigurerAdapter 더이상 사용 안됨 public class WebSecurityConfig { @Autowired private JwtAuthenticationFilter jdJwtAuthenticationFilter; @Bean protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http.cors().and().csrf().disable() .httpBasic().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/", "/auth/**").permitAll() .anyRequest() .authenticated(); } }
Spring boot 3.0 이상부터는 Spring Security 버전 업으로 인해 설정방법이 변경되어서 아래와 같이 설정하였다.
참고한 사이트
@EnableWebSecurity
@Configuration
@Slf4j
// WebSecurityConfigurerAdapter 더이상 사용 안됨
public class WebSecurityConfig {
@Autowired
private JwtAuthenticationFilter jdJwtAuthenticationFilter;
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
// https://docs.spring.io/spring-security/reference/servlet/configuration/java.html
// https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html
// WebMvcConfig에서 이미 설정했으므로 기본 cors 설정.
http.cors((cors) -> cors.disable());
// csrf는 현재 사용하지 않으므로 disable
http.csrf((csrf) -> csrf.disable());
// token을 사용하므로 basic 인증 disable
http.httpBasic((basic) -> basic.disable());
// session 기반이 아님을 선언
http.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
// /와 /auth/** 경로는 인증 안해도 됨.
http.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/", "/auth/**", "/error", "/login", "/todo").permitAll()
// /와 /auth/**이외의 모든 경로는 인증 해야됨.
.anyRequest().authenticated());
http.addFilterAfter(
jdJwtAuthenticationFilter,
CorsFilter.class);
return http.build();
}
}
'개인 포트폴리오 > TodoList' 카테고리의 다른 글
TodoList 프로젝트 - AWS 배포 (0) | 2023.10.19 |
---|---|
TodoList 프로젝트 - Spring Security, JWT 설정 (0) | 2023.10.19 |
TodoList 프로젝트 - User 로그인/회원가입 API (0) | 2023.10.19 |
TodoList 프로젝트 - Todo메인 API (0) | 2023.10.18 |
TodoList 포트폴리오 (0) | 2023.10.18 |