SpringBoot SpringSecurity 설정 v2-3. UsernamePasswordAuthenticationFilter 설정

jeonbang123·2023년 4월 16일
0

springboot

목록 보기
12/14
  • SpringBoot SpringSecurity 설정 v2-1. securityConfig 설정-
  • SpringBoot SpringSecurity 설정 v2-2. UserDetails, UserDetailsService 설정
  • SpringBoot SpringSecurity 설정 v2-3. UsernamePasswordAuthenticationFilter 설정
  • SpringBoot SpringSecurity 설정 v2-4. BasicAuthorizationFilter 설정

1. UsernamePasswordAuthenticationFilter 설정

package com.codesign.base.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.codesign.base.auth.PrincipalDetails;
import com.codesign.base.model.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

@RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        log.info("========= attemptAuthentication : 로그인 시도중 =========");

        try {
        
            ObjectMapper objectMapper = new ObjectMapper();
            User user = objectMapper.readValue(request.getInputStream(), User.class);

            UsernamePasswordAuthenticationToken userToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
            Authentication authenticate = authenticationManager.authenticate(userToken);            

            return authenticate;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        log.info("successfulAuthentication 인증 성공됨 ");
        PrincipalDetails principal = (PrincipalDetails) authResult.getPrincipal();

        // RSA방식은 아니고 Hash암호 방식
        // HAMC > 시크릿값을 가지고 있어야함
        String jwtToken = JWT.create()
                .withSubject("cos토큰")
                .withExpiresAt(new Date(System.currentTimeMillis() + (1000 * 60 * 30) ))// 만료시간 (30분) : 현재시간 + 사용시간
                .withClaim("id", principal.getUser().getId())
                .withClaim("username", principal.getUsername())
                .sign(Algorithm.HMAC512(JwtProperties.SECRET));

        response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + jwtToken);
    }
}
  • authenticationManagerWebSecurityConfigurerAdapter에 정의되어 있어서 인자로 넘겨 받음

1-1. attemptAuthentication(HttpServletRequest request, HttpServletResponse response)

  • attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
    /login으로 POST요청시 UsernamePasswordAuthenticationFilter를 타게 되는데, attemptAuthentication()는 인증시도하는 메서드다.
속성
설명
objectMapper.readValue()파라미터로 넘어온 username, password를 User 객체에 맵핑
new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword())username, password로 security 토큰 생성
authenticationManager.authenticate(userToken)userDetailsService.loadUserByUsername() 호출해서 인증정보를 받아 return

1-2. successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)

  • attemptAuthentication()에서 인증 성공시 successfulAuthentication()를 타게 된다. 여기에서 JWT토큰을 생성해서 response Header에 Authorization=JWT토큰을 실어보낸다.
속성
설명
create()JWT 토큰 생성
withSubject("cos토큰")subject 설정
withExpiresAt()만료시간 설정
withClaim()claim 추가
sign()시크릿 값을 넣어 서명
JwtProperties.SECRET서버에서만 관리되도록해야함

1-3. jwt 관련 상수 정의

JwtProperties.java

package com.codesign.base.jwt;


// TODO .properties 파일로 변경하기
public interface JwtProperties {
    String SECRET = "codesign123";
    int EXPIRATION_TIME = 1000 * 60 * 30;
    String TOKEN_PREFIX = "Bearer ";
    String HEADER_STRING = "Authorization";
}

2. JwtAuthenticationFilter 적용

SecurityConfig.java

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserService userService;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable(); // 위변조 방지 미사용
        http.cors().configurationSource(corsFilter()) // cors설정
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // session 미사용
                .and().formLogin().disable() // formlogin 미사용
                .httpBasic().disable() // Baerer 사용으로 Basic방식 미사용
                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                // .addFilter(new JwtAuthorizationFilter(authenticationManager(), userService))
                .authorizeRequests()
                .antMatchers("/api/v1/user/**").permitAll()
                .antMatchers("/api/v1/manager/**").access("hasRole('ROLE_MANAGER')")
                .anyRequest().permitAll();
    }
    
    ...
    
}

.addFilter(new JwtAuthenticationFilter(authenticationManager())) 추가

  • authenticationManager()WebSecurityConfigurerAdapter에 정의 되어있음

참고
https://www.youtube.com/watch?v=GEv_hw0VOxE&list=PL93mKxaRDidERCyMaobSLkvSPzYtIk0Ah

profile
Design Awesome Style Code

0개의 댓글