@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().disable() // cors 허용 금지
.csrf().disable() // csrf 허용 금지
.formLogin().disable() // 폼기반 로그인 인증 허용
.httpBasic().disable() // http 기반 인증 허용
// 세션을 사용하지 않게 설정.
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests() // 요청에 의한 사용권환 체크
.antMatchers("/user/login").permitAll() // permitAll() : 인증 없이 허용.
.antMatchers("/user/join").permitAll()
.antMatchers("/user").hasRole("USER")
.anyRequest().authenticated();
}
}
antMatchers
: 해당 URL로 요청 시 설정을 해준다.permitAll
: antMatchers 설정한 접근을 권환에 상관없이 허용한다.anyRequest()
: 모든 요청에 대해 인증을 하게 설정한다.hasRole("USER")
: 유저의 권한을 가지고 있는 사람만 접근이 가능하다./**
* 토큰 생성 및 검증 클래스
*/
@RequiredArgsConstructor
@Component
public class JwtProvider {
private String secretKey = "c2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQtc2lsdmVybmluZS10ZWNoLXNwcmluZy1ib290LWp3dC10dXRvcmlhbC1zZWNyZXQK";
// 토큰 유효시간 30분
private Long tokenValueTime = 30 * 60 * 1000L;
private final UserDetailsService userDetailsService;
// 객체 초기화, sercetKey를 Base64로 인코딩.
@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
// JWT 토큰 생성
public String createToken(String userPK, List<String> roles) {
Claims claims = Jwts.claims().setSubject(userPK); // JWT payload에 저장되는 정보단위
claims.put("roles", roles);
Date now = new Date(); // 날짜
return Jwts.builder()
.setClaims(claims) // 정보 저장.
.setIssuedAt(now) // 토큰 발행 시간 저장.
.setExpiration(new Date(now.getTime() + tokenValueTime)) // 현재 시간 더하기 30분
.signWith(SignatureAlgorithm.HS256, secretKey) // 사용할 암호화 알고리즘(HS256)
.compact();
}
// JWT 토큰에서 인증 정보 조회
public Authentication getAuthentication(String token) {
UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUserPk(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
// 토큰에서 회원 정보 추출
public String getUserPk(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
// request의 Header에서 token값을 가져온다. "X-AUTH-TOKEN" : "TOKEN값"
public String resolveToken(HttpServletRequest request) {
return request.getHeader("X-AUTH-TOKEN");
}
// 토큰의 유효성 검사 + 만료일 확인
public boolean validateToken(String jwtToken) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken);
return !claims.getBody().getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
}
/**
* JwtTokenProvider가 검증을 끝낸 Jwt로부터 유저 정보를 조회해서
* UserPasswordAuthenticationFilter로 전달.
*/
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// Header에서 Jwt를 받아옴.
String token = jwtProvider.resolveToken(request);
// 유효한 토큰인지 확인.
if (token != null && jwtProvider.validateToken(token)) {
// 토큰으로부터 유저 정보를 가져 옴.
Authentication authentication = jwtProvider.getAuthentication(token);
// SecurityContext에 authentication 객체 저장
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
private final JwtProvider jwtProvider;
@Transactional
public String login(Map<String, String> users) {
User user = userRepository.findByEmail(users.get("email"))
.orElseThrow(() -> new IllegalArgumentException("가입되지 않은 이메일입니다."));
String password = users.get("password");
if (passwordEncoder.encode(user.getPassword()).equals(password)) {
throw new IllegalArgumentException("잘못된 비밀번호입니다.");
}
List<String> roles = new ArrayList<>();
roles.add(user.getRole().name());
return jwtProvider.createToken(user.getNickname(), roles);
}
이전에 작성한 UserSerivce 클래스에 로그인 코드를 작성한다.
@PostMapping("/login")
public String login(@RequestBody Map<String, String> user) {
return userService.login(user);
}