Day 개발 기록

[SpringBoot + Security + JWT + JPA ] 회원가입 , 로그인 구현하기-3 본문

Spring Security/security

[SpringBoot + Security + JWT + JPA ] 회원가입 , 로그인 구현하기-3

데이25 2023. 2. 1. 19:43

이번 포스트 에서는  jwt 토큰을 발행해보고 인증하는 로직을 구현해겠다.

 

먼저 Application.java 에 비밀번호 암호화해줄 빈을 등록한다. 

 

Application.java

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    
}

 

config 패키지 밑에 jwt 패키지를 생성한다.

그리고 하위에 JwtProperties , JwtTokenProvider , JwtAuthFilter 클래스를 생성한다. 

JwtProperties.java

public interface JwtProperties {
    String SECRET = "your secret key";
    int EXPIRATION_TIME = 864000000; // 10일 (1/1000초)
    String HEADER_STRING = "Authorization";
}

 

JwtTokenProvider.java

@RequiredArgsConstructor
@Component
public class JwtTokenProvider {
    private final UserDetailsService userDetailsService;
    private String secretKey = Base64.getEncoder().encodeToString(JwtProperties.SECRET.getBytes());

    public String generateToken(String userPk, String roles) {
        Claims claims = Jwts.claims().setSubject(userPk); 
        claims.put("roles", roles); 
        Date now = new Date();
        String jwtToken =  Jwts.builder()
                .setClaims(claims) 
                .setIssuedAt(now) 
                .setExpiration(new Date(now.getTime() + JwtProperties.EXPIRATION_TIME)) 
                .signWith(SignatureAlgorithm.HS256, secretKey)  // 사용할 암호화 알고리즘과 signature 에 들어갈 secret값 세팅
                .compact();
        System.out.println("jwt 토큰 : " + jwtToken);
        return jwtToken;

    }

    public boolean verifyToken(String token) {
        try {
            Jws<Claims> claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(token);
            return claims.getBody()
                    .getExpiration()
                    .after(new Date());
        } catch (Exception e) {
            return false;
        }
    }

    public String getUid(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
    }

    public Authentication getAuthentication(String token) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUid(token));
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }
}

 

generateToken 함수는 토큰을 발행하는 함수이다. 

Claims 은 JWT의 Payload에 저장되는 정보 단위이고, 정보는 key value 쌍으로 저장된다. 

 

 

JwtAuthFilter.java

@RequiredArgsConstructor
public class JwtAuthFilter extends GenericFilterBean {
    private final JwtTokenProvider jwtTokenProvider;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String token = ((HttpServletRequest)request).getHeader(JwtProperties.HEADER_STRING);
        if (token != null && jwtTokenProvider.verifyToken(token)) {
            String email = jwtTokenProvider.getUid(token);
            Authentication authentication = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        chain.doFilter(request, response);
    }

    public Authentication getAuthentication(Member member) {
        return new UsernamePasswordAuthenticationToken(member, "",
                Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
    }
}

doFilter에서 JwtProperties에서 정의해주었던 Authorization 헤더에 토큰을 가져오도록한다.

 

 

Filter를 다시 작성했으니 SecurityConfig를 수정하도록 한다.

SecurityConfig.java

@RequiredArgsConstructor
@Configuration
public class SecurityConfig {

	//추가
    private final JwtTokenProvider jwtTokenProvider;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf()
                    .ignoringAntMatchers("/h2-console/**")
                	.disable()
                .headers().frameOptions().sameOrigin()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
                .and()
                .formLogin().disable()
                .httpBasic().disable()

                .authorizeRequests()
                    .antMatchers("/join/**","/login/**","/index/**","/h2-console/**").permitAll()
                    .anyRequest().authenticated() 
                ;
        
        //추가
        http.addFilterBefore(new JwtAuthFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}