Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
Tags
- 자동포맷팅
- AES256
- gitreflog
- 주식용어
- 버전관리
- docker
- gitreset
- python
- git
- 개발생산성
- git입문
- 배당소득 분리과세
- MYSQL
- Ai
- 자바패턴
- java
- gitlog
- 민생회복지원금
- git초보자팁
- GPT
- git연재
- JetBrains
- Spring
- 개발툴팁
- git초보
- 디버깅
- IntelliJ
- 민생회복소비쿠폰
- scrapy
- gradle
Archives
나만의공간
Spring Security로 로그인 기능 구현하기 (JWT 포함) 본문
Spring Security로 로그인 기능 구현하기 (JWT 포함)
Spring Boot로 웹 애플리케이션을 개발할 때, 보안은 필수입니다. 그 중심에 있는 것이 Spring Security이며, 여기에 JWT(JSON Web Token)을 결합하면 세션리스한 인증 시스템을 손쉽게 구축할 수 있습니다.
이 글에서는 Spring Security의 인증/인가 개념을 이해하고, 실제로 JWT 기반 로그인 기능을 구현하는 실습 코드까지 차근차근 알아보겠습니다.
Spring Security의 기본 개념: 인증(Authentication)과 인가(Authorization)
1. 인증(Authentication)이란?
사용자가 누구인지 확인하는 절차입니다. 예: 아이디/비밀번호로 로그인
2. 인가(Authorization)란?
인증된 사용자가 어떤 자원(Endpoint, 기능 등)에 접근할 수 있는지를 판단하는 과정입니다.
JWT란 무엇인가?
JWT (JSON Web Token)은 인증 정보를 담은 토큰입니다. 서버에서 로그인 성공 시 토큰을 발급하고, 이후 클라이언트가 요청할 때마다 헤더에 토큰을 담아 인증하는 방식입니다.
- 장점: 세션 저장소가 필요 없음 (stateless)
- 단점: 토큰 탈취 시 위험, 토큰 갱신 필요
Spring Security + JWT 로그인 흐름
- 사용자가 ID/PW로 로그인 요청
- 서버에서 인증 후 JWT 토큰 발급
- 클라이언트는 이후 요청 시 Authorization 헤더에 토큰 포함
- 서버는 JWT를 검증하고, 인증된 사용자로 처리
Spring Boot 프로젝트 실습: JWT 로그인 구현
이제 실제 코드로 로그인 기능을 구현해보겠습니다.
1. build.gradle 설정
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}
2. JWT 유틸 클래스
@Component
public class JwtTokenProvider {
private final String secretKey = "mysecretkey";
private final long validityInMilliseconds = 3600000; // 1 hour
public String createToken(String username, List<String> roles) {
Claims claims = Jwts.claims().setSubject(username);
claims.put("roles", roles);
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(SignatureAlgorithm.HS256, secretKey.getBytes())
.compact();
}
public Authentication getAuthentication(String token) {
String username = getUsername(token);
return new UsernamePasswordAuthenticationToken(username, "", List.of());
}
public String getUsername(String token) {
return Jwts.parser()
.setSigningKey(secretKey.getBytes())
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secretKey.getBytes()).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
3. 로그인 컨트롤러
@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider;
public AuthController(AuthenticationManager authenticationManager, JwtTokenProvider jwtTokenProvider) {
this.authenticationManager = authenticationManager;
this.jwtTokenProvider = jwtTokenProvider;
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody AuthRequest authRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
String token = jwtTokenProvider.createToken(authRequest.getUsername(), List.of("ROLE_USER"));
return ResponseEntity.ok(Map.of("token", token));
}
}
AuthRequest DTO
public class AuthRequest {
private String username;
private String password;
// getters and setters
}
4. JWT 필터 구현
public class JwtTokenFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = resolveToken(request);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
String bearer = request.getHeader("Authorization");
return (bearer != null && bearer.startsWith("Bearer ")) ? bearer.substring(7) : null;
}
}
5. Security 설정
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtTokenProvider jwtTokenProvider;
public SecurityConfig(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtTokenFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
마무리: Spring Security + JWT로 간결하고 안전한 인증 시스템 구축
Spring Security는 복잡해 보이지만, 핵심 개념을 이해하고 JWT와 결합하면 세션리스한 인증 구조를 비교적 간단하게 구현할 수 있습니다. 이번 글에서 제공한 구조는 실제 실무에서도 활용 가능한 기본 틀이며, 여기에 Refresh Token, 사용자 권한 관리 등을 추가하면 보다 완성도 높은 인증 시스템이 됩니다.
추가로 구현해볼 수 있는 내용:
- Refresh Token 발급 및 재발급 기능
- 유저 DB와 연동한 사용자 인증 처리
- 권한(ROLE_ADMIN, ROLE_USER 등) 기반 인가 처리
보안은 개발의 핵심입니다. Spring Security + JWT로 안전한 애플리케이션을 만들어보세요!
'IT > JAVA' 카테고리의 다른 글
자바 초보자가 자주 하는 실수 10가지와 해결법 (1) | 2025.06.21 |
---|---|
자바로 크롤러 만들기 – Jsoup 사용법 완전 정리 (1) | 2025.06.21 |
🚀 Spring Boot로 REST API 만들기 완전 정복 (1) | 2025.06.17 |
🦋 플라이웨이트(Flyweight) 패턴 완전 정복 (1) | 2025.06.01 |
🎬 퍼사드 패턴(Facade Pattern) 완전 정복 - 영화 감상 시스템 예제로 이해하기 (0) | 2025.06.01 |
Comments