Spring Boot 게시판 만들기(RemakeBoard) - 05 ( Security setting )

FFTL:)·2021년 7월 20일
1

RemakeBoard

목록 보기
6/8

Spring Security, JWT 적용하기

이번엔 보안요소인 Spring Security를 사용해보도록 하겠습니다. 일단은 build.gradle에 라이브러리를 추가해야 하지만 저는 프로젝트 생성할 때에 미리 추가를 해놓았습니다.

그리고 추가로 JWT(JSON Web Token)을 사용하여 인증할 것 이기 때문에 JWT도 추가하였습니다.

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt:0.9.1'

일단은 security를 사용하기 위해서는 몇몇의 설정이 필요한데 이를 한 곳에 모아놓기 위해서 폴더를 만들어 관리하면 사용하기 편리합니다.

저는 첫째로 SecurityConfig를 만들었습니다.

@Configuration
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtTokenProvider jwtTokenProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http
                .httpBasic().disable()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                .and()
                .authorizeRequests()
                .antMatchers("/user/login", "/user").permitAll()
                .anyRequest().hasRole("USER")
                .anyRequest().hasRole("ADMIN")

                .and()
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
    }
}

두 번째로는 JWT 토큰에 대한 생성과 사용을 설정해 주는 JwtTokenProvider를 만들었습니다.

@RequiredArgsConstructor
@Component
public class JwtTokenProvider {

    @Value("${spring.jwt.secret")
    private String secretKey;

    private long tokenValidMilliSecond = 1000L * 60 * 120; //2시간 유효기간
    private final UserDetailsService userDetailsService;

    /**
     * 잘 모른다! 알아보자!
     * */
    @PostConstruct
    protected  void init() { secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); }

    //유저 정보와 등급을 통하여 토큰을 생성해냅니다.
    public String createToken(String username, String role){
        Claims claims = Jwts.claims().setSubject(username);
        claims.put("role", role);
        Date now = new Date();

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(new Date(now.getTime() + tokenValidMilliSecond))
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

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

    //토큰을 이용하여 username 을 얻어낸다.
    public String getUserName(String token){
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
    }

    //Header를 통해 받아온 토큰에서 필요 없는 부분을 잘라낸다.
    public String resolveToken(HttpServletRequest req){
        String myToken = req.getHeader("Authorization");

        if(myToken == null){
            return null;
        }

        String realToken = myToken.substring("Bearer".length());

        return realToken;
    }

    //토큰의 유효기간이 지났는지 아닌지 확인한다.
    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;
        }
    }

}

마지막으로는 Security과정에서 Jwt인증 과정을 시행하게 하기 위한 JwtAuthenticationFilter 를 만들어 주었습니다.

/**
 * JWT를 이용한 실질적인 인증 절차를 진행하는 곳 입니다.
 * */
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends GenericFilterBean {
    private final JwtTokenProvider jwtTokenProvider;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);

        if (token != null && jwtTokenProvider.validateToken(token)){
            Authentication auth = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }

        chain.doFilter(request, response);
    }
}

이 세가지 파일을 이용해 JWT 토큰을 생성하고, 인증하는 방식을 사용하였습니다.

각 코드에 대한 설명은 차차 정리해서 업데이트 하도록 하겠습니다.

해당 내용의 프로젝트는 Github에서 확인할 수 있습니다.

profile
생각하는 개발자가 되자!

0개의 댓글