springBoot

[Spring Boot] Spring Security + JWT token 로그인

뇽꾸리 2022. 9. 6. 17:58
반응형
 @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()

                //h2
                .headers()
                .frameOptions()
                .sameOrigin()

                // 시큐리티는 기본적으로 세션을 사용
                // 여기서는 세션을 사용하지 않기 때문에 세션 설정을 Stateless 로 설정
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()

                //권한
                .authorizeRequests()
                .antMatchers("/resources/**","/css/**", "/images/**","/js/**", "/h2-console/**", "/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**")
                .permitAll()
                .antMatchers("/user/**").hasRole(Role.USER.name()) //user는 유저만
                .antMatchers("/admin/**").hasRole(Role.ADMIN.name()) //admin은 관리자만
                .anyRequest().authenticated()   // 나머지 API 는 전부 인증 필요
                .and()
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider,userDetailService), UsernamePasswordAuthenticationFilter.class)
              ;
    }
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends GenericFilterBean {

 private final JwtTokenProvider jwtTokenProvider;
 private final CustomUserDetailService userDetailService;

@Override 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

       // 1. header 에서 토큰 가져오기 
       String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);

       // 2. 유효한 토큰인지 확인합니다.
       if (token != null && jwtTokenProvider.validateToken(token)) {
           
          // 3. 토큰이 유효하면 토큰으로부터 유저 정보를 받아옵니다.
           UserDetails userDetails = userDetailService.loadUserByUsername(jwtTokenProvider.getUserPk(token));
           Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
          
           // 4. Authentication 을 가져와서 SecurityContext 에 저장
           SecurityContextHolder.getContext().setAuthentication(authentication);
       }

       chain.doFilter(request, response);
   }
@Component
@RequiredArgsConstructor
public class JwtTokenProvider {

    @Value("${jwt.secret}")
    private static final String secretKey; //application.properties 혹은 yml에 설정 
    private static final long tokenValidTime = 30 * 60 * 1000L;
    
    
    @PostConstruct
    protected void init() {
        secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
    }

    // JWT 토큰 생성
    public String createToken(String memberId, List<GrantedAuthority> roles) {
        Claims claims = Jwts.claims().setSubject(memberId); // JWT payload 에 저장되는 정보단위
        claims.put("roles", roles); // 정보는 key / value 쌍으로 저장된다.
        Date now = new Date();
        return Jwts.builder().setClaims(claims) // 정보 저장
                .setIssuedAt(now) // 토큰 발행 시간 정보
                .setExpiration(new Date(now.getTime() + tokenValidTime)) // set Expire Time
                .signWith(SignatureAlgorithm.HS256, secretKey) // 사용할 암호화 알고리즘과 // signature 에 들어갈 secret값 세팅
                .compact();
    }

    // 토큰에서 회원 정보 추출
    public String getUserPk(String token) {
        if(Pattern.matches("^Bearer .*",token)){
            token = token.replaceAll("^Bearer( )*","");
        }
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
    }

    // Request의 Header에서 token 값을 가져옵니다. "AUTH-TOKEN" : "TOKEN값'
    public String resolveToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if(token != null){
            if(Pattern.matches("^Bearer .*",request.getHeader("Authorization"))){
                token = token.replaceAll("^Bearer( )*","");
            }
        }
        return 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;
        }
    }
}
@Component
@RequiredArgsConstructor
public class CustomUserDetailService implements UserDetailsService {

    private final MemberRepository memberRepository;

    @Override
    public UserDetails loadUserByUsername(String memberId) throws UsernameNotFoundException {
        Member member =  memberRepository.findById(Long.valueOf(memberId)).orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));
        UserDetails userDetail = User.withUsername(memberId).password(member.getPassword() != null?  member.getPassword() : member.getName() ).authorities(member.getRoleKey()).build();
        return userDetail;
    }
}
반응형